mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-12-22 12:17:45 +01:00
The pm_vt_switch_required() function fails silently when memory allocation fails, offering no indication to callers that the operation was unsuccessful. This behavior prevents drivers from handling allocation errors correctly or implementing retry mechanisms. By ensuring that failures are reported back to the caller, drivers can make informed decisions, improve robustness, and avoid unexpected behavior during critical power management operations. Change the function signature to return an integer error code and modify the implementation to return -ENOMEM when kmalloc() fails. Update both the function declaration and the inline stub in include/linux/pm.h to maintain consistency across CONFIG_VT_CONSOLE_SLEEP configurations. The function now returns: - 0 on success (including when updating existing entries) - -ENOMEM when memory allocation fails This change improves error reporting without breaking existing callers, as the current callers in drivers/video/fbdev/core/fbmem.c already ignore the return value, making this a backward-compatible improvement. Reviewed-by: Lyude Paul <lyude@redhat.com> Signed-off-by: Malaya Kumar Rout <mrout@redhat.com> Reviewed-by: Dhruva Gole <d-gole@ti.com> Reviewed-by: Lyude Paul <lyude@redhat.com> Link: https://patch.msgid.link/20251013193028.89570-1-mrout@redhat.com Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
162 lines
3.6 KiB
C
162 lines
3.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Functions for saving/restoring console.
|
|
*
|
|
* Originally from swsusp.
|
|
*/
|
|
|
|
#include <linux/console.h>
|
|
#include <linux/vt_kern.h>
|
|
#include <linux/kbd_kern.h>
|
|
#include <linux/vt.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include "power.h"
|
|
|
|
#define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
|
|
|
|
static int orig_fgconsole, orig_kmsg;
|
|
static bool vt_switch_done;
|
|
|
|
static DEFINE_MUTEX(vt_switch_mutex);
|
|
|
|
struct pm_vt_switch {
|
|
struct list_head head;
|
|
struct device *dev;
|
|
bool required;
|
|
};
|
|
|
|
static LIST_HEAD(pm_vt_switch_list);
|
|
|
|
|
|
/**
|
|
* pm_vt_switch_required - indicate VT switch at suspend requirements
|
|
* @dev: device
|
|
* @required: if true, caller needs VT switch at suspend/resume time
|
|
*
|
|
* The different console drivers may or may not require VT switches across
|
|
* suspend/resume, depending on how they handle restoring video state and
|
|
* what may be running.
|
|
*
|
|
* Drivers can indicate support for switchless suspend/resume, which can
|
|
* save time and flicker, by using this routine and passing 'false' as
|
|
* the argument. If any loaded driver needs VT switching, or the
|
|
* no_console_suspend argument has been passed on the command line, VT
|
|
* switches will occur.
|
|
*/
|
|
int pm_vt_switch_required(struct device *dev, bool required)
|
|
{
|
|
struct pm_vt_switch *entry, *tmp;
|
|
int ret = 0;
|
|
|
|
mutex_lock(&vt_switch_mutex);
|
|
list_for_each_entry(tmp, &pm_vt_switch_list, head) {
|
|
if (tmp->dev == dev) {
|
|
/* already registered, update requirement */
|
|
tmp->required = required;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
|
if (!entry) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
entry->required = required;
|
|
entry->dev = dev;
|
|
|
|
list_add(&entry->head, &pm_vt_switch_list);
|
|
out:
|
|
mutex_unlock(&vt_switch_mutex);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(pm_vt_switch_required);
|
|
|
|
/**
|
|
* pm_vt_switch_unregister - stop tracking a device's VT switching needs
|
|
* @dev: device
|
|
*
|
|
* Remove @dev from the vt switch list.
|
|
*/
|
|
void pm_vt_switch_unregister(struct device *dev)
|
|
{
|
|
struct pm_vt_switch *tmp;
|
|
|
|
mutex_lock(&vt_switch_mutex);
|
|
list_for_each_entry(tmp, &pm_vt_switch_list, head) {
|
|
if (tmp->dev == dev) {
|
|
list_del(&tmp->head);
|
|
kfree(tmp);
|
|
break;
|
|
}
|
|
}
|
|
mutex_unlock(&vt_switch_mutex);
|
|
}
|
|
EXPORT_SYMBOL(pm_vt_switch_unregister);
|
|
|
|
/*
|
|
* There are three cases when a VT switch on suspend/resume are required:
|
|
* 1) no driver has indicated a requirement one way or another, so preserve
|
|
* the old behavior
|
|
* 2) console suspend is disabled, we want to see debug messages across
|
|
* suspend/resume
|
|
* 3) any registered driver indicates it needs a VT switch
|
|
*
|
|
* If none of these conditions is present, meaning we have at least one driver
|
|
* that doesn't need the switch, and none that do, we can avoid it to make
|
|
* resume look a little prettier (and suspend too, but that's usually hidden,
|
|
* e.g. when closing the lid on a laptop).
|
|
*/
|
|
static bool pm_vt_switch(void)
|
|
{
|
|
struct pm_vt_switch *entry;
|
|
bool ret = true;
|
|
|
|
mutex_lock(&vt_switch_mutex);
|
|
if (list_empty(&pm_vt_switch_list))
|
|
goto out;
|
|
|
|
if (!console_suspend_enabled)
|
|
goto out;
|
|
|
|
list_for_each_entry(entry, &pm_vt_switch_list, head) {
|
|
if (entry->required)
|
|
goto out;
|
|
}
|
|
|
|
ret = false;
|
|
out:
|
|
mutex_unlock(&vt_switch_mutex);
|
|
return ret;
|
|
}
|
|
|
|
void pm_prepare_console(void)
|
|
{
|
|
if (!pm_vt_switch())
|
|
return;
|
|
|
|
orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1);
|
|
if (orig_fgconsole < 0)
|
|
return;
|
|
|
|
vt_switch_done = true;
|
|
|
|
orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
|
|
return;
|
|
}
|
|
|
|
void pm_restore_console(void)
|
|
{
|
|
if (!pm_vt_switch() && !vt_switch_done)
|
|
return;
|
|
|
|
if (orig_fgconsole >= 0) {
|
|
vt_move_to_console(orig_fgconsole, 0);
|
|
vt_kmsg_redirect(orig_kmsg);
|
|
}
|
|
|
|
vt_switch_done = false;
|
|
}
|