[REGRESSION] ACPI processor/cpuidle change in 6.18.52 breaks bare-metal Xen dom0 boot

From: Support TRINITY

Date: Sun Sep 20 2026 - 11:48:47 EST


Hello,

I am reporting a bare-metal Xen dom0 boot regression seen with Linux 6.18.52.

On affected systems, Linux 6.18.51 boots successfully as Xen dom0 on bare metal, while Linux 6.18.52 consistently black-screens before dom0 userspace/networking comes up.

#regzbot introduced: v6.18.51..v6.18.52
#regzbot title: ACPI processor/cpuidle lifecycle change breaks bare-metal Xen dom0 boot
#regzbot link: https://gitlab.alpinelinux.org/alpine/aports/-/work_items/18447

Tested results:

  • Linux 6.18.51-r0, Xen dom0, bare metal: boots
  • Linux 6.18.52-r0, Xen dom0, bare metal: black screen before userspace/network
  • Linux 6.18.52-r0, Xen domU: boots
  • Linux 6.18.52-r0 with Xenbus notifier change reverted: still fails
  • Linux 6.18.52-r0 with only ACPI processor/cpuidle changes reverted: boots
  • Linux 6.18.52-r0 with refined ACPI idle lifecycle patch: boots

Affected hardware tested:

  • Intel Core i7-4785T thin mini-ITX, 16 GB DDR3
  • Intel N305 thin mini-ITX, 16 GB DDR5
  • Supermicro / Intel Xeon E5-1650, 32 GB DDR3

The successful boot used the regular Xen command line:

multiboot2 /boot/xen.gz cpufreq=xen:performance
module2 /boot/vmlinuz-lts modules=loop,squashfs,sd-mod,usb-storage,xfs quiet
module2 /boot/initramfs-lts

No IOMMU workaround, serial console parameter, debug parameter, storage workaround, or Xen command-line change was required.

The regression was narrowed to ACPI processor/cpuidle changes between 6.18.51 and 6.18.52, specifically the idle-driver registration lifecycle.

The working 6.18.51-style behavior registers the ACPI idle driver from acpi_processor_power_init() and unregisters it from acpi_processor_power_exit().

The failing 6.18.52 behavior registers the ACPI idle driver globally from acpi_processor_driver_init() before driver_register().

A first ACPI-only revert confirmed the regression source. A refined candidate patch was then prepared to preserve the working ACPI idle lifecycle while keeping unrelated 6.18.52 safety fixes, including:

  • _LPI bounds checks
  • cpufreq notifier cleanup on acpi_processor_driver_init() failure

The refined candidate patch modifies only:

  • drivers/acpi/processor_driver.c
  • drivers/acpi/processor_idle.c
  • include/acpi/processor.h

It does not modify Xen, Xenbus, IOMMU, APIC, PCI/ASPM, intel_idle, syscore, USB, storage, XFS, networking, printk, or the cpuidle core API.

The earlier cpuidle_disabled() workaround is not included.

The refined patch has been rebuilt and boot-tested successfully as Xen dom0 on bare metal on TRINITY-EDGE.

This issue is currently visible to Alpine users because the Alpine v3.24 stable repository contains:

alpine-release 3.24.2-r0
linux-lts 6.18.52-r0

Systems tracking Alpine 3.24 stable / latest-stable may therefore receive Linux 6.18.52 as the default LTS kernel.

Attachments:

  • revert-acpi-idle-registration-lifecycle.patch
  • APKBUILD

The original Alpine report is here:

https://gitlab.alpinelinux.org/alpine/aports/-/work_items/18447

Please let me know if this should be submitted as a formal patch with Signed-off-by, or if there is a better upstream fix/dependency that should be backported instead.

Regards,

Tony BONNIN

# Maintainer: Natanael Copa <ncopa@xxxxxxxxxxxxxxx>

_flavor=${FLAVOR:-lts}
pkgname=linux-$_flavor
pkgver=6.18.52
_kernver=${pkgver%.*}
pkgrel=0
pkgdesc="Linux lts kernel"
url="https://www.kernel.org";
depends="initramfs-generator"
_depends_dev="perl gmp-dev mpc1-dev mpfr-dev elfutils-dev bash flex bison zstd"
makedepends="$_depends_dev sed installkernel bc linux-headers linux-firmware-any openssl-dev>3 mawk
diffutils elfutils findutils zstd pahole python3 gcc>=13.1.1_git20230624 bpftool"
options="!strip !check"
source="https://cdn.kernel.org/pub/linux/kernel/v${pkgver%%.*}.x/linux-$_kernver.tar.xz
0001-powerpc-boot-wrapper-Add-z-notext-flag-for-ppc64le.patch
0002-x86-Compress-vmlinux-with-zstd-19-instead-of-22.patch
0003-kexec-add-kexec_load_disabled-boot-option.patch
0004-objtool-respect-AWK-setting.patch
0005-powerpc-config-defang-gcc-check-for-stack-protector-.patch
0001-x86-CPU-AMD-avoid-printing-reset-reasons-on-Xen-domU.patch
revert-acpi-idle-registration-lifecycle.patch
sophgo-fixes.patch

lts.aarch64.config
lts.armv7.config
lts.loongarch64.config
lts.ppc64le.config
lts.riscv64.config
lts.s390x.config
lts.x86.config
lts.x86_64.config

virt.aarch64.config
virt.armv7.config
virt.ppc64le.config
virt.x86.config
virt.x86_64.config
"

# Allow adding custom flavor config via FLAVOR var
# The config file should be named ${FLAVOR}.${CARCH}.config
if [ -n "$FLAVOR" ]; then
source="$source
${FLAVOR}.${CARCH}.config"
fi

subpackages="$pkgname-dev:_dev:$CBUILD_ARCH $pkgname-doc"
for _i in $source; do
case $_i in
*.$CARCH.config)
_f=${_i%."$CARCH".config}
if [ -n "$FLAVOR" ] && [ "$_f" != "$FLAVOR" ]; then
# skip flavors that don't match
continue
fi
_flavors="$_flavors $_f"
if [ "linux-$_f" != "$pkgname" ]; then
subpackages="$subpackages linux-$_f::$CBUILD_ARCH linux-$_f-dev:_dev:$CBUILD_ARCH"
fi
;;
esac
done
builddir="$srcdir"/linux-$_kernver

if [ "${pkgver%.0}" = "$pkgver" ]; then
# Prepend to apply first
source="patch-$pkgver.patch.xz::https://cdn.kernel.org/pub/linux/kernel/v${pkgver%%.*}.x/patch-$pkgver.xz $source"
fi
arch="all !armhf"
license="GPL-2.0-only"

# secfixes are not tracked
# https://gitlab.alpinelinux.org/alpine/aports/-/work_items/18194

prepare() {
default_prepare

# remove localversion from patch if any
rm -f localversion*
}

_kernelarch() {
local arch="$1"
case "$arch" in
aarch64*) arch="arm64" ;;
arm*) arch="arm" ;;
ppc*) arch="powerpc" ;;
s390*) arch="s390" ;;
riscv*) arch="riscv" ;;
loongarch64) arch="loongarch" ;;
esac
echo "$arch"
}

_prepareconfig() {
local _flavor="$1"
local _arch="$2"
local _config=$_flavor.$_arch.config
local _builddir="$srcdir"/build-$_flavor.$_arch
mkdir -p "$_builddir"
echo "-$pkgrel-$_flavor" > "$_builddir"/localversion-alpine

cp "$srcdir"/$_config "$_builddir"/.config
msg "Configuring $_flavor kernel ($_arch)"
make -C "$builddir" \
O="$_builddir" \
ARCH="$(_kernelarch $_arch)" \
olddefconfig

if grep "CONFIG_MODULE_SIG=y" "$_builddir"/.config >/dev/null; then
if [ -f "$KERNEL_SIGNING_KEY" ]; then
sed -i -e "s:^CONFIG_MODULE_SIG_KEY=.*:CONFIG_MODULE_SIG_KEY=\"$KERNEL_SIGNING_KEY\":" \
"$_builddir"/.config
msg "Using $KERNEL_SIGNING_KEY to sign $_flavor kernel ($_arch) modules"
else
warning "KERNEL_SIGNING_KEY was not set. A signing key will be generated, but 3rd"
warning "party modules can not be signed"
fi
fi
}

listconfigs() {
for i in $source; do
case "$i" in
*.config) echo $i;;
esac
done
}

prepareconfigs() {
for _config in $(listconfigs); do
local _flavor=${_config%%.*}
local _arch=${_config%.config}
_arch=${_arch#*.}
local _builddir="$srcdir"/build-$_flavor.$_arch
_prepareconfig "$_flavor" "$_arch"
done
}

# this is supposed to be run before version is bumped so we can compare
# what new kernel config knobs are introduced
prepareupdate() {
clean && fetch && unpack && prepare && deps
prepareconfigs
rm -r "$builddir"
}

updateconfigs() {
if ! [ -d "$builddir" ]; then
deps && fetch && unpack && prepare
fi
for _config in ${CONFIGS:-$(listconfigs)}; do
msg "updating $_config"
local _flavor=${_config%%.*}
local _arch=${_config%.config}
_arch=${_arch#*.}
local _builddir="$srcdir"/build-$_flavor.$_arch
mkdir -p "$_builddir"
echo "-$pkgrel-$_flavor" > "$_builddir"/localversion-alpine
local actions="listnewconfig oldconfig"
if ! [ -f "$_builddir"/.config ]; then
cp "$srcdir"/$_config "$_builddir"/.config
actions="olddefconfig"
fi
env | grep ^CONFIG_ >> "$_builddir"/.config || true
make -j1 -C "$builddir" \
O="$_builddir" \
ARCH="$(_kernelarch $_arch)" \
$actions savedefconfig

cp "$_builddir"/defconfig "$startdir"/$_config
done
}

set_kbuild_timestamp() {
# KBUILD_BUILD_TIMESTAMP needs to be parsable by busybox date
export KBUILD_BUILD_TIMESTAMP="$(date '+%Y-%m-%d %H:%M:%S' -u${SOURCE_DATE_EPOCH:+d @$SOURCE_DATE_EPOCH})"
}

build() {
unset LDFLAGS
# for some reason these sometimes leak into the kernel build,
# -Werror=format-security breaks some stuff
unset CFLAGS CPPFLAGS CXXFLAGS
set_kbuild_timestamp
for i in $_flavors; do
_prepareconfig "$i" "$CARCH"
done
for i in $_flavors; do
msg "Building $i kernel"
cd "$srcdir"/build-$i.$CARCH

# set org in cert for modules signing
# https://www.kernel.org/doc/html/v6.1/admin-guide/module-signing.html#generating-signing-keys
mkdir -p certs
sed -e 's/#O = Unspecified company/O = alpinelinux.org/' \
"$builddir"/certs/default_x509.genkey \
> certs/x509.genkey

make ARCH="$(_kernelarch $CARCH)" \
CC="${CC:-gcc}" \
AWK="${AWK:-mawk}" \
KBUILD_BUILD_VERSION="$((pkgrel + 1 ))-Alpine"

if grep -q '^CONFIG_DEBUG_INFO_BTF=y' .config; then
# Generate vmlinux.h
bpftool btf dump file vmlinux format c > vmlinux.h

if [ "$CARCH" = "ppc64le" ]; then
# ppc64le installs the uncompressed vmlinux, which is extremely
# large with BTF debuginfo unless stripped
eu-strip --remove-comment vmlinux
fi
fi
done
}

_package() {
local _buildflavor="$1" _outdir="$2"
set_kbuild_timestamp

cd "$srcdir"/build-$_buildflavor.$CARCH
local _abi_release="$(make -s kernelrelease)"
# modules_install seems to regenerate a defect Modules.symvers on s390x. Work
# around it by backing it up and restore it after modules_install
cp Module.symvers Module.symvers.backup

mkdir -p "$_outdir"/boot "$_outdir"/lib/modules

local _install
case "$CARCH" in
arm*|aarch64|riscv*) _install="zinstall dtbs_install";;
*) _install=install;;
esac

make modules_install $_install \
ARCH="$(_kernelarch $CARCH)" \
INSTALL_MOD_PATH="$_outdir" \
INSTALL_MOD_STRIP=1 \
INSTALL_PATH="$_outdir"/boot \
INSTALL_DTBS_PATH="$_outdir/boot/dtbs-$_buildflavor"

cp Module.symvers.backup Module.symvers

rm -f "$_outdir"/lib/modules/"$_abi_release"/build \
"$_outdir"/lib/modules/"$_abi_release"/source
rm -rf "$_outdir"/lib/firmware

install -D -m644 include/config/kernel.release \
"$_outdir"/usr/share/kernel/$_buildflavor/kernel.release

ln -sf /boot/vmlinuz-"$_buildflavor" \
"$_outdir"/lib/modules/"$_abi_release"/vmlinuz
}

# main flavor installs in $pkgdir
package() {
depends="$depends linux-firmware-any"

_package "$_flavor" "$pkgdir"

# copy files for linux-lts-doc sub package
mkdir -p "$pkgdir"/usr/share/doc
cp -r "$builddir"/Documentation \
"$pkgdir"/usr/share/doc/linux-doc-"$pkgver"/
# remove files that aren't part of the documentation itself
for nondoc in \
.gitignore conf.py docutils.conf \
Kconfig Makefile
do
rm "$pkgdir"/usr/share/doc/linux-doc-"$pkgver"/"$nondoc"
done
# create /usr/share/doc/linux-doc symlink
cd "$pkgdir"/usr/share/doc; ln -s linux-doc-"$pkgver" linux-doc
}

# subflavors install in $subpkgdir
virt() {
_package virt "$subpkgdir"
}

_dev() {
local _flavor=$(echo $subpkgname | sed -E 's/(^linux-|-dev$)//g')
local _builddir="$srcdir"/build-$_flavor.$CARCH
local _abi_release="$(make -C "$_builddir" -s kernelrelease)"
local _karch="$(_kernelarch $CARCH | sed 's/x86_64/x86/')"
# copy the only the parts that we really need for build 3rd party
# kernel modules and install those as /usr/src/linux-headers,
# simlar to what ubuntu does
#
# this way you dont need to install the 300-400 kernel sources to
# build a tiny kernel module
#
pkgdesc="Headers and script for third party modules for $_flavor kernel"
depends="$_depends_dev"
local dir="$subpkgdir"/usr/src/linux-headers-"$_abi_release"
set_kbuild_timestamp

# first we import config, run prepare to set up for building
# external modules, and create the scripts
mkdir -p "$dir"
cp -a "$_builddir"/.config "$_builddir"/localversion-alpine \
"$dir"/

install -D -t "$dir"/certs "$_builddir"/certs/signing_key.x509 || :

# Install vmlinux.h
if grep -q '^CONFIG_DEBUG_INFO_BTF=y' "$_builddir"/.config; then
install -Dm644 "$_builddir"/vmlinux.h -t "$dir/"
fi

make -C "$builddir" \
O="$dir" \
ARCH="$(_kernelarch $CARCH)" \
AWK="${AWK:-mawk}" \
prepare modules_prepare scripts

# remove the stuff that points to real sources. we want 3rd party
# modules to believe this is the sources
rm "$dir"/Makefile "$dir"/source

# copy the needed stuff from real sources
#
# this is taken from ubuntu kernel build script
# http://kernel.ubuntu.com/git/ubuntu/ubuntu-zesty.git/tree/debian/rules.d/3-binary-indep.mk
cd "$builddir"
find . -path './include/*' -prune \
-o -path './scripts/*' -prune -o -type f \
\( -name 'Makefile*' -o -name 'Kconfig*' -o -name 'Kbuild*' -o \
-name '*.sh' -o -name '*.pl' -o -name '*.lds' -o -name 'Platform' \) \
-print | cpio -pdm "$dir"

cp -a scripts include "$dir"

find "arch/$_karch" "tools/include" "tools/arch/$_karch" -type f -path '*/include/*' \
-print | cpio -pdm "$dir"

install -Dm644 "$srcdir"/build-$_flavor.$CARCH/Module.symvers \
"$dir"/Module.symvers

# remove unneeded things
msg "Removing documentation..."
rm -r "$dir"/Documentation
sed -i -e '/Documentation/d' "$dir"/Kconfig
find "$dir" -type f \( -name '*.o' -o -name '*.cmd' \) -exec rm -v -- {} +

mkdir -p "$subpkgdir"/lib/modules/"$_abi_release"
ln -sf /usr/src/linux-headers-"$_abi_release" \
"$subpkgdir"/lib/modules/"$_abi_release"/build
}

sha512sums="
f1c32303a8eb7f20c7f5c8d592a23140ff880075d0e4151bf96d5b0365ff64b35324753894d8ba822ad797e673b2a878f18d6a2f557dd6d3aca1fcc8d4e5764d patch-6.18.52.patch.xz
88599ffdec96d150c1feb9b261ba93bb0301a9d0e1ad6bef7aeab1f5372cbfc57d8b43c7e902bd8f76921d1dbd8189663c142ea869e51d0e2b483b150ee00fe0 linux-6.18.tar.xz
b296717ef0cd63978142b4d47b3bc49faa04daf77e115e702ba611cc254a6e782cdab7d5639e724cd5f029cfb6a3dc33c3036f346d681d1fdfe0df723c7e0321 0001-powerpc-boot-wrapper-Add-z-notext-flag-for-ppc64le.patch
055e10e7b3e00bb3621389315d5206a8feb5022a1f0dc51c3aa107facfe83d191da05a502a0c62488be7299677a63c1da84f87c42cbf3125788c56db67e810ee 0002-x86-Compress-vmlinux-with-zstd-19-instead-of-22.patch
e1d2d5358f5b8189236108973395f00eafce8db6d5baaf55025b1360f08ab7cffc930d3818719f220b98e7e8541cc8a7f056611b6e290f224a102c1addbe34ac 0003-kexec-add-kexec_load_disabled-boot-option.patch
28c83be4c97152001381cd5e3a8acb8552a42d9dac9aaec8f096e3762b7944035aa5a4363fb4602f9bb427c4f72cfd2f996a0a725f7f9e07ab250317985aff9e 0004-objtool-respect-AWK-setting.patch
5ce79416f8f111c36393718656e3eeb4cb0adccd117dcf661876d1564010e5c58b6351cc4116d187d0a9a838bcca0fecc236d81ac71afb940b89ccb14f21e179 0005-powerpc-config-defang-gcc-check-for-stack-protector-.patch
4e72af8e77a555d2a850837b64a525a51d5d118048584dc3ea0d0e8620bb56bf56b69a94aa67dac879b841c9ba033e33d94610da0028fde7b6602ad2fe63f02f 0001-x86-CPU-AMD-avoid-printing-reset-reasons-on-Xen-domU.patch
f5c9c99e637eb47c582e2db57c2609be803c6d6037717a949a0dd181ba50cd4ea19678da81ded9de737e1669ca81f371a4809258a71eb7fef73e46212b1cdfab revert-acpi-idle-registration-lifecycle.patch
855522b816bbb2b0738251cf24714c2566c7aacad39aaf6adcebd0a256d11f64ff2762ecdace0ab1bac38db87fd579863f852f799356a368f057c166e4deb03c sophgo-fixes.patch
fc69158d31d9d3c951d057aee01397f46c5a72462334f14b79aa538294c1900338bc6af154c9bda051fb29eff11541d8c1893d735a12b7bafb86cdb0f98a5485 lts.aarch64.config
e8036dc7b74ffa09f84c0d13246a637aa7326df3da3729a158177be9b47036ff06d5ded2886b567b55fac44dcb937ca7271751ca5dc5a7bfed5a91df1ecaf4ce lts.armv7.config
6b2af88167234828c9793aeefc8ccc0ed5ae7d6814bbaff30a91c0b336b95e2008697608c9630e8507a493e02d3a7fc798e490467eac969ede14db9b92e21e17 lts.loongarch64.config
77a84a8a3b1ae289fe88aef9183f2eafc49379f55e7745700534801a56c0f7f0d900af19a231d25e48c7acab46f23a88df15044d9efe4ff16881c43984cdc40c lts.ppc64le.config
2bf106cd7ddc04dcb69c69ae73e9a1f95b964190301c272856ea7ee68820d77aceaa90ededa741da0ec9355fc942ad9647ef3e3c49b232455d93a4f727b8bcd9 lts.riscv64.config
34622b02baf05ac9f9dfa3cbcd817368b9ea0bc2056a55e4cab770e0e0100f8cdc9ddb0b68bbdfe381f6f3d8b1a81bbd55f56db43a824b691f8788907ed6fa23 lts.s390x.config
33903e3a877734ab5f3fd2b03589817dc996f5114e66a34abe30ae137b61de6960478fd4d33174da319939bf4b77d7aba809ccf5566acf46946fca476f03e35f lts.x86.config
32db10060987654c515342eabad99ff68471aca739159a6dcbf79b2157e0dbe15138be67bd25acf326eae13e1790f07c77f179e3f21f9ba9ffd340a91006d5cd lts.x86_64.config
ffbe61069184a46baf90cbffeba5512df7e0d0b11c2404f629ad698bdfb6199269618013abdc2895a9bc960bb5e0425ee4f8a928fca08aae5b5ff97e49023e3f virt.aarch64.config
77b6dfe665ddd3a896a4a9e7807d6d2aff7d3270eb98a7342cd08a5304446a62d4755ad80e5a0df404e1809738f5b9e82201743289c3348993074d723b5990c1 virt.armv7.config
7145644f2950ff16a158060cb4266431b3f696bfba23bc84d570e8135889024f3e92cdeac61bdf4ff490cf0e6f0065ca7ebd0508efff6a91e1eab8e6f655eef4 virt.ppc64le.config
6acc6d3e54a70eb8cfc35da824589ad45167c1380e0c4a01bc479f965a24de9c07ec17c7e5ba045a9ce0a11a7858ff1659eac9888ebb6174c6bdd341e514b834 virt.x86.config
0da34fd867f797004369e75157e764604093d1ae957fc686fe2343ecb30a271bc5c0ad876526fe5954cf5bf6e3affb71dba3a802f9089271cd51e1c2fa411201 virt.x86_64.config
"
Revert the ACPI idle driver registration lifecycle introduced by upstream
commit 13ebeef6a1b9 ("ACPI: processor: idle: Optimize ACPI idle driver
registration").

That change moved ACPI idle driver registration out of
acpi_processor_power_init() and ahead of driver_register(). This breaks the
established lifecycle on Xen dom0, where cpuidle is disabled during early
architecture setup. Restore the 6.18.51 per-processor registration behavior.

Keep unrelated 6.18.52 fixes, notably _LPI bounds validation and cpufreq
notifier cleanup on initialization failure.

diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 06589bf48..174dfb0ac 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -259,11 +259,9 @@ static int __init acpi_processor_driver_init(void)
acpi_processor_ignore_ppc_init();
}

- acpi_processor_register_idle_driver();
-
result = driver_register(&acpi_processor_driver);
if (result < 0)
- goto unregister_idle_drv;
+ goto unregister_cpufreq;

result = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"acpi/cpu-drv:online",
@@ -289,9 +287,7 @@ static int __init acpi_processor_driver_init(void)
err:
driver_unregister(&acpi_processor_driver);

-unregister_idle_drv:
- acpi_processor_unregister_idle_driver();
-
+unregister_cpufreq:
if (acpi_processor_cpufreq_init) {
cpufreq_unregister_notifier(&acpi_processor_notifier_block,
CPUFREQ_POLICY_NOTIFIER);
@@ -315,7 +311,6 @@ static void __exit acpi_processor_driver_exit(void)
cpuhp_remove_state_nocalls(hp_online);
cpuhp_remove_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD);
driver_unregister(&acpi_processor_driver);
- acpi_processor_unregister_idle_driver();
}

module_init(acpi_processor_driver_init);
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 5c6f736ba..7b4e948fa 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -831,13 +831,19 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr)
return 0;
}

-static inline void acpi_processor_update_max_cstate(void)
+static inline void acpi_processor_cstate_first_run_checks(void)
{
+ static int first_run;
+
+ if (first_run)
+ return;
dmi_check_system(processor_power_dmi_table);
max_cstate = acpi_processor_cstate_check(max_cstate);
if (max_cstate < ACPI_C_STATES_MAX)
pr_notice("processor limited to max C-state %d\n", max_cstate);

+ first_run++;
+
if (nocst)
return;

@@ -846,7 +852,7 @@ static inline void acpi_processor_update_max_cstate(void)
#else

static inline int disabled_by_idle_boot_param(void) { return 0; }
-static inline void acpi_processor_update_max_cstate(void) { }
+static inline void acpi_processor_cstate_first_run_checks(void) { }
static int acpi_processor_get_cstate_info(struct acpi_processor *pr)
{
return -ENODEV;
@@ -1365,59 +1371,7 @@ int acpi_processor_power_state_has_changed(struct acpi_processor *pr)
return 0;
}

-void acpi_processor_register_idle_driver(void)
-{
- struct acpi_processor *pr;
- int ret = -ENODEV;
- int cpu;
-
- /*
- * If a cpuidle driver is already registered, there is no need to
- * evaluate _CST or attempt to register the ACPI idle driver.
- */
- if (cpuidle_get_driver()) {
- pr_debug("cpuidle driver %pS already registered.\n", cpuidle_get_driver());
- return;
- }
-
- acpi_processor_update_max_cstate();
-
- /*
- * ACPI idle driver is used by all possible CPUs.
- * Use the processor power info of one in them to set up idle states.
- * Note that the existing idle handler will be used on platforms that
- * only support C1.
- */
- for_each_possible_cpu(cpu) {
- pr = per_cpu(processors, cpu);
- if (!pr)
- continue;
-
- ret = acpi_processor_get_power_info(pr);
- if (!ret) {
- pr->flags.power_setup_done = 1;
- acpi_processor_setup_cpuidle_states(pr);
- break;
- }
- }
-
- if (ret) {
- pr_debug("No ACPI power information from any CPUs.\n");
- return;
- }
-
- ret = cpuidle_register_driver(&acpi_idle_driver);
- if (ret) {
- pr_debug("register %s failed.\n", acpi_idle_driver.name);
- return;
- }
- pr_debug("%s registered with cpuidle.\n", acpi_idle_driver.name);
-}
-
-void acpi_processor_unregister_idle_driver(void)
-{
- cpuidle_unregister_driver(&acpi_idle_driver);
-}
+static int acpi_processor_registered;

int acpi_processor_power_init(struct acpi_processor *pr)
{
@@ -1427,10 +1381,27 @@ int acpi_processor_power_init(struct acpi_processor *pr)
if (disabled_by_idle_boot_param())
return 0;

+ acpi_processor_cstate_first_run_checks();
+
if (!acpi_processor_get_power_info(pr))
pr->flags.power_setup_done = 1;

+ /*
+ * Install the idle handler if processor power management is supported.
+ * Note that the existing idle handler is used on platforms that only
+ * support C1.
+ */
if (pr->flags.power) {
+ /* Register acpi_idle_driver if not already registered */
+ if (!acpi_processor_registered) {
+ acpi_processor_setup_cpuidle_states(pr);
+ retval = cpuidle_register_driver(&acpi_idle_driver);
+ if (retval)
+ return retval;
+ pr_debug("%s registered with cpuidle\n",
+ acpi_idle_driver.name);
+ }
+
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
@@ -1443,11 +1414,14 @@ int acpi_processor_power_init(struct acpi_processor *pr)
*/
retval = cpuidle_register_device(dev);
if (retval) {
+ if (acpi_processor_registered == 0)
+ cpuidle_unregister_driver(&acpi_idle_driver);

per_cpu(acpi_cpuidle_device, pr->id) = NULL;
kfree(dev);
return retval;
}
+ acpi_processor_registered++;
}
return 0;
}
@@ -1461,6 +1435,10 @@ int acpi_processor_power_exit(struct acpi_processor *pr)

if (pr->flags.power) {
cpuidle_unregister_device(dev);
+ acpi_processor_registered--;
+ if (acpi_processor_registered == 0)
+ cpuidle_unregister_driver(&acpi_idle_driver);
+
kfree(dev);
}

diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index ff864c1ce..d0eccbd92 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -423,8 +423,6 @@ int acpi_processor_power_init(struct acpi_processor *pr);
int acpi_processor_power_exit(struct acpi_processor *pr);
int acpi_processor_power_state_has_changed(struct acpi_processor *pr);
int acpi_processor_hotplug(struct acpi_processor *pr);
-void acpi_processor_register_idle_driver(void);
-void acpi_processor_unregister_idle_driver(void);
#else
static inline int acpi_processor_power_init(struct acpi_processor *pr)
{