printk: nobkl: Add print state functions
Provide three functions which are related to the safe handover mechanism and allow console drivers to denote takeover unsafe sections: - console_can_proceed() Invoked by a console driver to check whether a handover request is pending or whether the console was taken over in a hostile fashion. - console_enter/exit_unsafe() Invoked by a console driver to denote that the driver output function is about to enter or to leave an critical region where a hostile take over is unsafe. These functions are also cancellation points. The unsafe state is stored in the console state and allows a takeover attempt to make informed decisions whether to take over and/or output on such a console at all. The unsafe state is also available to the driver in the write context for the atomic_write() output function so the driver can make informed decisions about the required actions or take a special emergency path. 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: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
This commit is contained in:
committed by
Sebastian Andrzej Siewior
parent
54388ba872
commit
d803bcd90a
@@ -466,9 +466,15 @@ static inline bool console_is_registered(const struct console *con)
|
||||
hlist_for_each_entry(con, &console_list, node)
|
||||
|
||||
#ifdef CONFIG_PRINTK
|
||||
extern bool console_can_proceed(struct cons_write_context *wctxt);
|
||||
extern bool console_enter_unsafe(struct cons_write_context *wctxt);
|
||||
extern bool console_exit_unsafe(struct cons_write_context *wctxt);
|
||||
extern bool console_try_acquire(struct cons_write_context *wctxt);
|
||||
extern bool console_release(struct cons_write_context *wctxt);
|
||||
#else
|
||||
static inline bool console_can_proceed(struct cons_write_context *wctxt) { return false; }
|
||||
static inline bool console_enter_unsafe(struct cons_write_context *wctxt) { return false; }
|
||||
static inline bool console_exit_unsafe(struct cons_write_context *wctxt) { return false; }
|
||||
static inline bool console_try_acquire(struct cons_write_context *wctxt) { return false; }
|
||||
static inline bool console_release(struct cons_write_context *wctxt) { return false; }
|
||||
#endif
|
||||
|
||||
@@ -947,6 +947,148 @@ static void cons_free_percpu_data(struct console *con)
|
||||
con->pcpu_data = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* console_can_proceed - Check whether printing can proceed
|
||||
* @wctxt: The write context that was handed to the write function
|
||||
*
|
||||
* Returns: True if the state is correct. False if a handover
|
||||
* has been requested or if the console was taken
|
||||
* over.
|
||||
*
|
||||
* Must be invoked after the record was dumped into the assigned record
|
||||
* buffer and at appropriate safe places in the driver. For unsafe driver
|
||||
* sections see console_enter_unsafe().
|
||||
*
|
||||
* When this function returns false then the calling context is not allowed
|
||||
* to go forward and has to back out immediately and carefully. The buffer
|
||||
* content is no longer trusted either and the console lock is no longer
|
||||
* held.
|
||||
*/
|
||||
bool console_can_proceed(struct cons_write_context *wctxt)
|
||||
{
|
||||
struct cons_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
|
||||
struct console *con = ctxt->console;
|
||||
struct cons_state state;
|
||||
|
||||
cons_state_read(con, CON_STATE_CUR, &state);
|
||||
/* Store it for analysis or reuse */
|
||||
copy_full_state(ctxt->old_state, state);
|
||||
|
||||
/* Make sure this context is still the owner. */
|
||||
if (!cons_state_full_match(state, ctxt->state))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* Continue if the requested priority is not sufficient.
|
||||
*/
|
||||
if (state.req_prio <= state.cur_prio)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* A console printer within an unsafe region is allowed to continue.
|
||||
* It can perform the handover when exiting the safe region. Otherwise
|
||||
* a hostile takeover will be necessary.
|
||||
*/
|
||||
if (state.unsafe)
|
||||
return true;
|
||||
|
||||
/* Release and hand over */
|
||||
cons_release(ctxt);
|
||||
/*
|
||||
* This does not check whether the handover succeeded. The
|
||||
* outermost callsite has to make the final decision whether printing
|
||||
* should continue or not (via reacquire, possibly hostile). The
|
||||
* console is unlocked already so go back all the way instead of
|
||||
* trying to implement heuristics in tons of places.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(console_can_proceed);
|
||||
|
||||
/**
|
||||
* __console_update_unsafe - Update the unsafe bit in @con->atomic_state
|
||||
* @wctxt: The write context that was handed to the write function
|
||||
*
|
||||
* Returns: True if the state is correct. False if a handover
|
||||
* has been requested or if the console was taken
|
||||
* over.
|
||||
*
|
||||
* Must be invoked before an unsafe driver section is entered.
|
||||
*
|
||||
* When this function returns false then the calling context is not allowed
|
||||
* to go forward and has to back out immediately and carefully. The buffer
|
||||
* content is no longer trusted either and the console lock is no longer
|
||||
* held.
|
||||
*
|
||||
* Internal helper to avoid duplicated code
|
||||
*/
|
||||
static bool __console_update_unsafe(struct cons_write_context *wctxt, bool unsafe)
|
||||
{
|
||||
struct cons_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
|
||||
struct console *con = ctxt->console;
|
||||
struct cons_state new;
|
||||
|
||||
do {
|
||||
if (!console_can_proceed(wctxt))
|
||||
return false;
|
||||
/*
|
||||
* console_can_proceed() saved the real state in
|
||||
* ctxt->old_state
|
||||
*/
|
||||
copy_full_state(new, ctxt->old_state);
|
||||
new.unsafe = unsafe;
|
||||
|
||||
} while (!cons_state_try_cmpxchg(con, CON_STATE_CUR, &ctxt->old_state, &new));
|
||||
|
||||
copy_full_state(ctxt->state, new);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* console_enter_unsafe - Enter an unsafe region in the driver
|
||||
* @wctxt: The write context that was handed to the write function
|
||||
*
|
||||
* Returns: True if the state is correct. False if a handover
|
||||
* has been requested or if the console was taken
|
||||
* over.
|
||||
*
|
||||
* Must be invoked before an unsafe driver section is entered.
|
||||
*
|
||||
* When this function returns false then the calling context is not allowed
|
||||
* to go forward and has to back out immediately and carefully. The buffer
|
||||
* content is no longer trusted either and the console lock is no longer
|
||||
* held.
|
||||
*/
|
||||
bool console_enter_unsafe(struct cons_write_context *wctxt)
|
||||
{
|
||||
return __console_update_unsafe(wctxt, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(console_enter_unsafe);
|
||||
|
||||
/**
|
||||
* console_exit_unsafe - Exit an unsafe region in the driver
|
||||
* @wctxt: The write context that was handed to the write function
|
||||
*
|
||||
* Returns: True if the state is correct. False if a handover
|
||||
* has been requested or if the console was taken
|
||||
* over.
|
||||
*
|
||||
* Must be invoked before an unsafe driver section is exited.
|
||||
*
|
||||
* When this function returns false then the calling context is not allowed
|
||||
* to go forward and has to back out immediately and carefully. The buffer
|
||||
* content is no longer trusted either and the console lock is no longer
|
||||
* held.
|
||||
*/
|
||||
bool console_exit_unsafe(struct cons_write_context *wctxt)
|
||||
{
|
||||
return __console_update_unsafe(wctxt, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(console_exit_unsafe);
|
||||
|
||||
/**
|
||||
* cons_nobkl_init - Initialize the NOBKL console specific data
|
||||
* @con: Console to initialize
|
||||
|
||||
Reference in New Issue
Block a user