Skip to content

Instantly share code, notes, and snippets.

@jakob-hede
Last active March 25, 2024 15:05
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jakob-hede/66e9f3439d0891f090fe99daef45cf0d to your computer and use it in GitHub Desktop.
Save jakob-hede/66e9f3439d0891f090fe99daef45cf0d to your computer and use it in GitHub Desktop.
apple_sleep 2023-11-15

apple_sleep 2023-11-15

Explanation

Fedora Linux on Apple MacBook might not be able to wake from sleep.
(Or it might eventually wake up, but it can take several minutes.)

This package apple_sleep is a workaround using isolation of CPUs, that allows the laptop to wake up from sleep within a normal timeframe.

I have no idea why it works, but I found the trick online - and polished the implementation as far as my Linux-fu allows. And it helps my Fedora-ized MacBooks wake up from sleep.

There was some uncertainty about which cpu-num to isolate, so I made a script that isolates all but the first cpu; cpu0, and this seems to work ... speedily.

The package consists of a systemd service unit-file and a Bash script.

Usage

Put the files where you prefer, and edit the service file to point to the script.
Enable the service, and sleep.target will trigger the service to source the script when the laptop goes to sleep, and again when it wakes up.

In my case I simply "link-enable" like so:

[root@sofa /]# systemctl enable /opt/optulation/mixed/apple_sleep/apple_sleep.service
Created symlink /etc/systemd/system/apple_sleep.service → /opt/optulation/mixed/apple_sleep/apple_sleep.service.
Created symlink /etc/systemd/system/sleep.target.wants/apple_sleep.service → /opt/optulation/mixed/apple_sleep/apple_sleep.service.

The Bash script does not need to be executable.

The script logs to /tmp/apple_sleep.log.
The log shows the status of the CPUs after the script has modified the cpu-online-files.

21:24:17 START. cpu_statuses: 1:0 2:0 3:0 4:0 5:0 6:0 7:0 
21:24:23 STOP. cpu_statuses: 1:1 2:1 3:1 4:1 5:1 6:1 7:1 

"START" is when sleep.target is triggered -i.e. when the laptop goes to sleep.

References

tested on:

  • Fedora 39 on MacBook Pro (Retina, 15-inch, Late 2013), MacBookPro11,2
  • Fedora 38 on MacBook Pro (Retina, 13-inch, Late 2013), MacBookPro11,1
[Unit]
Description=Isolate cpu cores before sleep and restore after wake
Documentation=https://gist.github.com/jakob-hede/66e9f3439d0891f090fe99daef45cf0d
PartOf=sleep.target
[Service]
Type=simple
RemainAfterExit=yes
# Edit to customize script location:
Environment="APPLE_SLEEP_SCRIPT=/opt/optulation/mixed/apple_sleep/apple_sleep.sh"
ExecStart=/bin/bash -c '. ${APPLE_SLEEP_SCRIPT} START'
ExecStop=/bin/bash -c '. ${APPLE_SLEEP_SCRIPT} STOP'
[Install]
WantedBy=sleep.target
#
#!/bin/bash
# Documentation=https://gist.github.com/jakob-hede/66e9f3439d0891f090fe99daef45cf0d
# You must be root to run this script.
execute() {
debug() {
# Change to 'false' to disable debug output
true && printf "\e[35m%s\e[0m\n" "$*"
}
fail() {
printf "\e[31m%s\e[0m\n" "$*"
exit 1
}
is_apple() {
declare system_vendor txt
declare -i response
system_vendor=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null)
if [[ "$system_vendor" == *Apple* ]]; then
response=0
txt="Running on Apple hardware"
else
response=1
txt="NOT running on Apple hardware"
fi
debug "${txt}"
return "${response}"
}
is_root() {
[[ $UID -eq 0 ]]
}
check_circumstances() {
is_root || fail "You must be root to run this script."
is_apple || fail "You must be running on Apple hardware to run this script."
}
argparse() {
indx=-99
if [[ $1 == 'START' ]]; then
indx=0
num=0
elif [[ $1 == 'STOP' ]]; then
indx=0
num=1
else
fail "Usage: $0 [START|STOP]"
fi
}
logify() {
declare log_file timestamp
timestamp=$(date +%H:%M:%S)
log_file="/tmp/${name}.log"
echo "${timestamp} ${*}. cpu_statuses: ${cpu_statuses}" >>"${log_file}"
debug "${name} ${timestamp} ${log_file} $* ${cpu_statuses}"
}
isolate() {
declare directory file
while ((indx > -1)); do
((indx++)) # We do not isolate CPU0, so we start at 1.
directory="/sys/devices/system/cpu/cpu${indx}"
[[ ! -d $directory ]] && indx=-99 && break
file="${directory}/online"
is_apple >/dev/null && echo "${num}" >"${file}"
cpu_statuses+="${indx}:$(cat ${file}) "
done
}
flow() (# <- subshell !!!
# Subshell ensures not to break some system stuff by doing 'exit'.
declare -i num indx
declare name cpu_statuses
name="$(basename "${BASH_SOURCE[0]%.*}")"
debug "${name} FLOW $*"
check_circumstances # Optionally disable while testing
argparse "$@"
isolate
logify "$@"
)
flow "$@"
}
execute "$@"
printf '\e[34mapple_sleep DONE %s\e[0m\n' "$?"
#
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment