Merge branches 'pm-core', 'pm-sleep' and 'powercap'

Merge PM core changes, updates related to system sleep and power capping
updates for 5.19-rc1:

 - Export dev_pm_ops instead of suspend() and resume() in the IIO
   chemical scd30 driver (Jonathan Cameron).

 - Add namespace variants of EXPORT[_GPL]_SIMPLE_DEV_PM_OPS and
   PM-runtime counterparts (Jonathan Cameron).

 - Move symbol exports in the IIO chemical scd30 driver into the
   IIO_SCD30 namespace (Jonathan Cameron).

 - Avoid device PM-runtime usage count underflows (Rafael Wysocki).

 - Allow dynamic debug to control printing of PM messages  (David
   Cohen).

 - Fix some kernel-doc comments in hibernation code (Yang Li, Haowen
   Bai).

 - Preserve ACPI-table override during hibernation (Amadeusz Sławiński).

 - Improve support for suspend-to-RAM for PSCI OSI mode (Ulf Hansson).

 - Make Intel RAPL power capping driver support the RaptorLake and
   AlderLake N processors (Zhang Rui, Sumeet Pawnikar).

 - Remove redundant store to value after multiply in the RAPL power
   capping driver (Colin Ian King).

* pm-core:
  PM: runtime: Avoid device usage count underflows
  iio: chemical: scd30: Move symbol exports into IIO_SCD30 namespace
  PM: core: Add NS varients of EXPORT[_GPL]_SIMPLE_DEV_PM_OPS and runtime pm equiv
  iio: chemical: scd30: Export dev_pm_ops instead of suspend() and resume()

* pm-sleep:
  cpuidle: PSCI: Improve support for suspend-to-RAM for PSCI OSI mode
  PM: runtime: Allow to call __pm_runtime_set_status() from atomic context
  PM: hibernate: Don't mark comment as kernel-doc
  x86/ACPI: Preserve ACPI-table override during hibernation
  PM: hibernate: Fix some kernel-doc comments
  PM: sleep: enable dynamic debug support within pm_pr_dbg()
  PM: sleep: Narrow down -DDEBUG on kernel/power/ files

* powercap:
  powercap: intel_rapl: remove redundant store to value after multiply
  powercap: intel_rapl: add support for ALDERLAKE_N
  powercap: RAPL: Add Power Limit4 support for RaptorLake
  powercap: intel_rapl: add support for RaptorLake
This commit is contained in:
Rafael J. Wysocki 2022-05-23 19:06:33 +02:00
commit 95f2ce548a
16 changed files with 172 additions and 73 deletions

View File

@ -1772,7 +1772,7 @@ int __acpi_release_global_lock(unsigned int *lock)
void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
{
e820__range_add(addr, size, E820_TYPE_ACPI);
e820__range_add(addr, size, E820_TYPE_NVS);
e820__update_table_print();
}

View File

@ -263,7 +263,7 @@ static int rpm_check_suspend_allowed(struct device *dev)
retval = -EINVAL;
else if (dev->power.disable_depth > 0)
retval = -EACCES;
else if (atomic_read(&dev->power.usage_count) > 0)
else if (atomic_read(&dev->power.usage_count))
retval = -EAGAIN;
else if (!dev->power.ignore_children &&
atomic_read(&dev->power.child_count))
@ -1039,13 +1039,33 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay)
}
EXPORT_SYMBOL_GPL(pm_schedule_suspend);
static int rpm_drop_usage_count(struct device *dev)
{
int ret;
ret = atomic_sub_return(1, &dev->power.usage_count);
if (ret >= 0)
return ret;
/*
* Because rpm_resume() does not check the usage counter, it will resume
* the device even if the usage counter is 0 or negative, so it is
* sufficient to increment the usage counter here to reverse the change
* made above.
*/
atomic_inc(&dev->power.usage_count);
dev_warn(dev, "Runtime PM usage count underflow!\n");
return -EINVAL;
}
/**
* __pm_runtime_idle - Entry point for runtime idle operations.
* @dev: Device to send idle notification for.
* @rpmflags: Flag bits.
*
* If the RPM_GET_PUT flag is set, decrement the device's usage count and
* return immediately if it is larger than zero. Then carry out an idle
* return immediately if it is larger than zero (if it becomes negative, log a
* warning, increment it, and return an error). Then carry out an idle
* notification, either synchronous or asynchronous.
*
* This routine may be called in atomic context if the RPM_ASYNC flag is set,
@ -1057,7 +1077,10 @@ int __pm_runtime_idle(struct device *dev, int rpmflags)
int retval;
if (rpmflags & RPM_GET_PUT) {
if (!atomic_dec_and_test(&dev->power.usage_count)) {
retval = rpm_drop_usage_count(dev);
if (retval < 0) {
return retval;
} else if (retval > 0) {
trace_rpm_usage_rcuidle(dev, rpmflags);
return 0;
}
@ -1079,7 +1102,8 @@ EXPORT_SYMBOL_GPL(__pm_runtime_idle);
* @rpmflags: Flag bits.
*
* If the RPM_GET_PUT flag is set, decrement the device's usage count and
* return immediately if it is larger than zero. Then carry out a suspend,
* return immediately if it is larger than zero (if it becomes negative, log a
* warning, increment it, and return an error). Then carry out a suspend,
* either synchronous or asynchronous.
*
* This routine may be called in atomic context if the RPM_ASYNC flag is set,
@ -1091,7 +1115,10 @@ int __pm_runtime_suspend(struct device *dev, int rpmflags)
int retval;
if (rpmflags & RPM_GET_PUT) {
if (!atomic_dec_and_test(&dev->power.usage_count)) {
retval = rpm_drop_usage_count(dev);
if (retval < 0) {
return retval;
} else if (retval > 0) {
trace_rpm_usage_rcuidle(dev, rpmflags);
return 0;
}
@ -1210,12 +1237,13 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
{
struct device *parent = dev->parent;
bool notify_parent = false;
unsigned long flags;
int error = 0;
if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
return -EINVAL;
spin_lock_irq(&dev->power.lock);
spin_lock_irqsave(&dev->power.lock, flags);
/*
* Prevent PM-runtime from being enabled for the device or return an
@ -1226,7 +1254,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
else
error = -EAGAIN;
spin_unlock_irq(&dev->power.lock);
spin_unlock_irqrestore(&dev->power.lock, flags);
if (error)
return error;
@ -1247,7 +1275,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
device_links_read_unlock(idx);
}
spin_lock_irq(&dev->power.lock);
spin_lock_irqsave(&dev->power.lock, flags);
if (dev->power.runtime_status == status || !parent)
goto out_set;
@ -1288,7 +1316,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
dev->power.runtime_error = 0;
out:
spin_unlock_irq(&dev->power.lock);
spin_unlock_irqrestore(&dev->power.lock, flags);
if (notify_parent)
pm_request_idle(parent);
@ -1527,14 +1555,17 @@ EXPORT_SYMBOL_GPL(pm_runtime_forbid);
*/
void pm_runtime_allow(struct device *dev)
{
int ret;
spin_lock_irq(&dev->power.lock);
if (dev->power.runtime_auto)
goto out;
dev->power.runtime_auto = true;
if (atomic_dec_and_test(&dev->power.usage_count))
ret = rpm_drop_usage_count(dev);
if (ret == 0)
rpm_idle(dev, RPM_AUTO | RPM_ASYNC);
else
else if (ret > 0)
trace_rpm_usage_rcuidle(dev, RPM_AUTO | RPM_ASYNC);
out:

View File

@ -23,6 +23,7 @@
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/syscore_ops.h>
#include <asm/cpuidle.h>
@ -131,6 +132,49 @@ static int psci_idle_cpuhp_down(unsigned int cpu)
return 0;
}
static void psci_idle_syscore_switch(bool suspend)
{
bool cleared = false;
struct device *dev;
int cpu;
for_each_possible_cpu(cpu) {
dev = per_cpu_ptr(&psci_cpuidle_data, cpu)->dev;
if (dev && suspend) {
dev_pm_genpd_suspend(dev);
} else if (dev) {
dev_pm_genpd_resume(dev);
/* Account for userspace having offlined a CPU. */
if (pm_runtime_status_suspended(dev))
pm_runtime_set_active(dev);
/* Clear domain state to re-start fresh. */
if (!cleared) {
psci_set_domain_state(0);
cleared = true;
}
}
}
}
static int psci_idle_syscore_suspend(void)
{
psci_idle_syscore_switch(true);
return 0;
}
static void psci_idle_syscore_resume(void)
{
psci_idle_syscore_switch(false);
}
static struct syscore_ops psci_idle_syscore_ops = {
.suspend = psci_idle_syscore_suspend,
.resume = psci_idle_syscore_resume,
};
static void psci_idle_init_cpuhp(void)
{
int err;
@ -138,6 +182,8 @@ static void psci_idle_init_cpuhp(void)
if (!psci_cpuidle_use_cpuhp)
return;
register_syscore_ops(&psci_idle_syscore_ops);
err = cpuhp_setup_state_nocalls(CPUHP_AP_CPU_PM_STARTING,
"cpuidle/psci:online",
psci_idle_cpuhp_up,

View File

@ -68,10 +68,7 @@ struct scd30_state {
scd30_command_t command;
};
int scd30_suspend(struct device *dev);
int scd30_resume(struct device *dev);
static __maybe_unused SIMPLE_DEV_PM_OPS(scd30_pm_ops, scd30_suspend, scd30_resume);
extern const struct dev_pm_ops scd30_pm_ops;
int scd30_probe(struct device *dev, int irq, const char *name, void *priv, scd30_command_t command);

View File

@ -517,7 +517,7 @@ static const struct iio_chan_spec scd30_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3),
};
int __maybe_unused scd30_suspend(struct device *dev)
static int scd30_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct scd30_state *state = iio_priv(indio_dev);
@ -529,9 +529,8 @@ int __maybe_unused scd30_suspend(struct device *dev)
return regulator_disable(state->vdd);
}
EXPORT_SYMBOL(scd30_suspend);
int __maybe_unused scd30_resume(struct device *dev)
static int scd30_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct scd30_state *state = iio_priv(indio_dev);
@ -543,7 +542,8 @@ int __maybe_unused scd30_resume(struct device *dev)
return scd30_command_write(state, CMD_START_MEAS, state->pressure_comp);
}
EXPORT_SYMBOL(scd30_resume);
EXPORT_NS_SIMPLE_DEV_PM_OPS(scd30_pm_ops, scd30_suspend, scd30_resume, IIO_SCD30);
static void scd30_stop_meas(void *data)
{
@ -759,7 +759,7 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv,
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL(scd30_probe);
EXPORT_SYMBOL_NS(scd30_probe, IIO_SCD30);
MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor core driver");

View File

@ -128,7 +128,7 @@ static struct i2c_driver scd30_i2c_driver = {
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = scd30_i2c_of_match,
.pm = &scd30_pm_ops,
.pm = pm_sleep_ptr(&scd30_pm_ops),
},
.probe_new = scd30_i2c_probe,
};
@ -137,3 +137,4 @@ module_i2c_driver(scd30_i2c_driver);
MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor i2c driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(IIO_SCD30);

View File

@ -252,7 +252,7 @@ static struct serdev_device_driver scd30_serdev_driver = {
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = scd30_serdev_of_match,
.pm = &scd30_pm_ops,
.pm = pm_sleep_ptr(&scd30_pm_ops),
},
.probe = scd30_serdev_probe,
};
@ -261,3 +261,4 @@ module_serdev_device_driver(scd30_serdev_driver);
MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor serial driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(IIO_SCD30);

View File

@ -1010,7 +1010,7 @@ static u64 rapl_compute_time_window_atom(struct rapl_package *rp, u64 value,
* where time_unit is default to 1 sec. Never 0.
*/
if (!to_raw)
return (value) ? value *= rp->time_unit : rp->time_unit;
return (value) ? value * rp->time_unit : rp->time_unit;
value = div64_u64(value, rp->time_unit);
@ -1107,6 +1107,8 @@ static const struct x86_cpu_id rapl_ids[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_N, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, &rapl_defaults_spr_server),
X86_MATCH_INTEL_FAM6_MODEL(LAKEFIELD, &rapl_defaults_core),

View File

@ -140,6 +140,7 @@ static const struct x86_cpu_id pl4_support_ids[] = {
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_TIGERLAKE_L, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ALDERLAKE, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ALDERLAKE_L, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_RAPTORLAKE, X86_FEATURE_ANY },
{}
};

View File

@ -368,13 +368,13 @@ const struct dev_pm_ops name = { \
#ifdef CONFIG_PM
#define _EXPORT_DEV_PM_OPS(name, suspend_fn, resume_fn, runtime_suspend_fn, \
runtime_resume_fn, idle_fn, sec) \
runtime_resume_fn, idle_fn, sec, ns) \
_DEFINE_DEV_PM_OPS(name, suspend_fn, resume_fn, runtime_suspend_fn, \
runtime_resume_fn, idle_fn); \
_EXPORT_SYMBOL(name, sec)
__EXPORT_SYMBOL(name, sec, ns)
#else
#define _EXPORT_DEV_PM_OPS(name, suspend_fn, resume_fn, runtime_suspend_fn, \
runtime_resume_fn, idle_fn, sec) \
runtime_resume_fn, idle_fn, sec, ns) \
static __maybe_unused _DEFINE_DEV_PM_OPS(__static_##name, suspend_fn, \
resume_fn, runtime_suspend_fn, \
runtime_resume_fn, idle_fn)
@ -391,9 +391,13 @@ static __maybe_unused _DEFINE_DEV_PM_OPS(__static_##name, suspend_fn, \
_DEFINE_DEV_PM_OPS(name, suspend_fn, resume_fn, NULL, NULL, NULL)
#define EXPORT_SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \
_EXPORT_DEV_PM_OPS(name, suspend_fn, resume_fn, NULL, NULL, NULL, "")
_EXPORT_DEV_PM_OPS(name, suspend_fn, resume_fn, NULL, NULL, NULL, "", "")
#define EXPORT_GPL_SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \
_EXPORT_DEV_PM_OPS(name, suspend_fn, resume_fn, NULL, NULL, NULL, "_gpl")
_EXPORT_DEV_PM_OPS(name, suspend_fn, resume_fn, NULL, NULL, NULL, "_gpl", "")
#define EXPORT_NS_SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn, ns) \
_EXPORT_DEV_PM_OPS(name, suspend_fn, resume_fn, NULL, NULL, NULL, "", #ns)
#define EXPORT_NS_GPL_SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn, ns) \
_EXPORT_DEV_PM_OPS(name, suspend_fn, resume_fn, NULL, NULL, NULL, "_gpl", #ns)
/* Deprecated. Use DEFINE_SIMPLE_DEV_PM_OPS() instead. */
#define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \

View File

@ -41,10 +41,16 @@
#define EXPORT_RUNTIME_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn) \
_EXPORT_DEV_PM_OPS(name, pm_runtime_force_suspend, pm_runtime_force_resume, \
suspend_fn, resume_fn, idle_fn, "")
suspend_fn, resume_fn, idle_fn, "", "")
#define EXPORT_GPL_RUNTIME_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn) \
_EXPORT_DEV_PM_OPS(name, pm_runtime_force_suspend, pm_runtime_force_resume, \
suspend_fn, resume_fn, idle_fn, "_gpl")
suspend_fn, resume_fn, idle_fn, "_gpl", "")
#define EXPORT_NS_RUNTIME_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn, ns) \
_EXPORT_DEV_PM_OPS(name, pm_runtime_force_suspend, pm_runtime_force_resume, \
suspend_fn, resume_fn, idle_fn, "", #ns)
#define EXPORT_NS_GPL_RUNTIME_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn, ns) \
_EXPORT_DEV_PM_OPS(name, pm_runtime_force_suspend, pm_runtime_force_resume, \
suspend_fn, resume_fn, idle_fn, "_gpl", #ns)
#ifdef CONFIG_PM
extern struct workqueue_struct *pm_wq;

View File

@ -542,22 +542,56 @@ static inline void unlock_system_sleep(void) {}
#ifdef CONFIG_PM_SLEEP_DEBUG
extern bool pm_print_times_enabled;
extern bool pm_debug_messages_on;
extern __printf(2, 3) void __pm_pr_dbg(bool defer, const char *fmt, ...);
static inline int pm_dyn_debug_messages_on(void)
{
#ifdef CONFIG_DYNAMIC_DEBUG
return 1;
#else
return 0;
#endif
}
#ifndef pr_fmt
#define pr_fmt(fmt) "PM: " fmt
#endif
#define __pm_pr_dbg(fmt, ...) \
do { \
if (pm_debug_messages_on) \
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \
else if (pm_dyn_debug_messages_on()) \
pr_debug(fmt, ##__VA_ARGS__); \
} while (0)
#define __pm_deferred_pr_dbg(fmt, ...) \
do { \
if (pm_debug_messages_on) \
printk_deferred(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \
} while (0)
#else
#define pm_print_times_enabled (false)
#define pm_debug_messages_on (false)
#include <linux/printk.h>
#define __pm_pr_dbg(defer, fmt, ...) \
no_printk(KERN_DEBUG fmt, ##__VA_ARGS__)
#define __pm_pr_dbg(fmt, ...) \
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#define __pm_deferred_pr_dbg(fmt, ...) \
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif
/**
* pm_pr_dbg - print pm sleep debug messages
*
* If pm_debug_messages_on is enabled, print message.
* If pm_debug_messages_on is disabled and CONFIG_DYNAMIC_DEBUG is enabled,
* print message only from instances explicitly enabled on dynamic debug's
* control.
* If pm_debug_messages_on is disabled and CONFIG_DYNAMIC_DEBUG is disabled,
* don't print message.
*/
#define pm_pr_dbg(fmt, ...) \
__pm_pr_dbg(false, fmt, ##__VA_ARGS__)
__pm_pr_dbg(fmt, ##__VA_ARGS__)
#define pm_deferred_pr_dbg(fmt, ...) \
__pm_pr_dbg(true, fmt, ##__VA_ARGS__)
__pm_deferred_pr_dbg(fmt, ##__VA_ARGS__)
#ifdef CONFIG_PM_AUTOSLEEP

View File

@ -1,6 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
ccflags-$(CONFIG_PM_DEBUG) := -DDEBUG
ifeq ($(CONFIG_DYNAMIC_DEBUG), y)
CFLAGS_swap.o := -DDEBUG
CFLAGS_snapshot.o := -DDEBUG
CFLAGS_energy_model.o := -DDEBUG
endif
KASAN_SANITIZE_snapshot.o := n

View File

@ -545,35 +545,6 @@ static int __init pm_debug_messages_setup(char *str)
}
__setup("pm_debug_messages", pm_debug_messages_setup);
/**
* __pm_pr_dbg - Print a suspend debug message to the kernel log.
* @defer: Whether or not to use printk_deferred() to print the message.
* @fmt: Message format.
*
* The message will be emitted if enabled through the pm_debug_messages
* sysfs attribute.
*/
void __pm_pr_dbg(bool defer, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
if (!pm_debug_messages_on)
return;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
if (defer)
printk_deferred(KERN_DEBUG "PM: %pV", &vaf);
else
printk(KERN_DEBUG "PM: %pV", &vaf);
va_end(args);
}
#else /* !CONFIG_PM_SLEEP_DEBUG */
static inline void pm_print_times_init(void) {}
#endif /* CONFIG_PM_SLEEP_DEBUG */

View File

@ -6,9 +6,6 @@
* Originally from swsusp.
*/
#undef DEBUG
#include <linux/interrupt.h>
#include <linux/oom.h>
#include <linux/suspend.h>

View File

@ -326,7 +326,7 @@ static void *chain_alloc(struct chain_allocator *ca, unsigned int size)
return ret;
}
/**
/*
* Data types related to memory bitmaps.
*
* Memory bitmap is a structure consisting of many linked lists of
@ -427,6 +427,10 @@ struct memory_bitmap {
/**
* alloc_rtree_node - Allocate a new node and add it to the radix tree.
* @gfp_mask: GFP mask for the allocation.
* @safe_needed: Get pages not used before hibernation (restore only)
* @ca: Pointer to a linked list of pages ("a chain") to allocate from
* @list: Radix Tree node to add.
*
* This function is used to allocate inner nodes as well as the
* leave nodes of the radix tree. It also adds the node to the
@ -902,7 +906,7 @@ static bool rtree_next_node(struct memory_bitmap *bm)
}
/**
* memory_bm_rtree_next_pfn - Find the next set bit in a memory bitmap.
* memory_bm_next_pfn - Find the next set bit in a memory bitmap.
* @bm: Memory bitmap.
*
* Starting from the last returned position this function searches for the next
@ -1937,7 +1941,7 @@ static inline int get_highmem_buffer(int safe_needed)
}
/**
* alloc_highmem_image_pages - Allocate some highmem pages for the image.
* alloc_highmem_pages - Allocate some highmem pages for the image.
*
* Try to allocate as many pages as needed, but if the number of free highmem
* pages is less than that, allocate them all.
@ -2224,7 +2228,7 @@ static int check_header(struct swsusp_info *info)
}
/**
* load header - Check the image header and copy the data from it.
* load_header - Check the image header and copy the data from it.
*/
static int load_header(struct swsusp_info *info)
{