Skip to content

Instantly share code, notes, and snippets.

@rorar
Forked from samueljon/toggleHT.sh
Last active October 16, 2024 20:38
Show Gist options
  • Save rorar/cebde935cf4dfa6389df96c4df79ce38 to your computer and use it in GitHub Desktop.
Save rorar/cebde935cf4dfa6389df96c4df79ce38 to your computer and use it in GitHub Desktop.
Disable / Enable HyperThreading cores on runtime | CPU config backup and restore | simple CPU Mitigation Check that prevents HT | For Linux 🐧
#!/bin/bash
# ------------------------------------------------------------------------
# Usage Instructions:
# To download and use this script, follow these steps:
# wget http://example.com/HToggler.su -O HToggler.su
# chmod +x HToggler.su
# sudo ./HToggler.su
# ------------------------------------------------------------------------
# ASCII Art Header (Corrected)
printf "\n\n"
printf " ) \n"
printf " ( /( * ) ( \n"
printf " )\\())\` ) /( ( ( ( ( )\\ ( ( \n"
printf "((_)\ ( )(_))( )\\))( )\\))( ((_) ))\\ )( \n"
printf " _((_)(_(_()) )\\ ((_))\\((_))\\ _ /((_)(()\\ \n"
printf "| || ||_ _|((_) (()(_)(()(_)| |(_)) ((_) \n"
printf "| __ | | | / _ \\/ _\` |/ _\` | | |/ -_) | '_| \n"
printf "|_||_| |_| \\___/\\__, |\\__, | |_|\___| |_| \n"
printf " |___/ |___/ \n"
printf "\n"
# Explanation of the script functionality
printf "HToggler is a script to manage Hyperthreading settings on Linux systems.\n"
printf "It allows you to enable or disable Hyperthreading, create backups of CPU configurations, and restore them when needed.\n"
printf "\n"
HYPERTHREADING=1
LOGFILE="/var/log/hyperthreading_script.log"
BACKUP_DIR="/tmp"
# Trap to handle SIGINT (Ctrl + C) to exit cleanly
function handleSigint() {
printf "\nScript interrupted by the user. Exiting...\n" >&2
exit 1
}
trap handleSigint SIGINT
# Check if the script is run as root
function ensureRoot() {
if [[ $EUID -ne 0 ]]; then
printf "This script must be run as root. Restarting with sudo...\n"
if command -v sudo &> /dev/null; then
exec sudo "$0" "$@"
else
printf "Error: sudo is not available. Script cannot continue without root privileges.\n" >&2
exit 1
fi
fi
}
# Backup CPU configuration before making changes
function backupCpuConfig() {
local TIMESTAMP
TIMESTAMP=$(date '+%Y%m%d_%H%M%S')
local BACKUP_FILE="$BACKUP_DIR/cpu_config_backup_$TIMESTAMP"
printf "Backing up current CPU configuration...\n"
cat /sys/devices/system/cpu/online > "$BACKUP_FILE"
if [[ $? -ne 0 ]]; then
logError "Error: Failed to create backup of CPU configuration."
return 1
fi
printf "Backup saved to %s\n" "$BACKUP_FILE"
}
# Restore CPU configuration from a list of backups
function restoreCpuConfig() {
local backups=($BACKUP_DIR/cpu_config_backup_*)
local num_backups=${#backups[@]}
if [[ $num_backups -eq 0 ]]; then
printf "No backups found in %s\n" "$BACKUP_DIR" >&2
return 1
fi
printf "Available backups:\n"
for i in "${!backups[@]}"; do
printf "%d) %s\n" "$((i+1))" "${backups[$i]}"
done
while true; do
read -p "Enter the number of the backup you want to restore: " selection
if [[ "$selection" =~ ^[0-9]+$ && $selection -ge 1 && $selection -le $num_backups ]]; then
local selected_backup="${backups[$((selection-1))]}"
printf "Restoring CPU configuration from %s...\n" "$selected_backup"
while read -r cpu; do
echo "1" > "/sys/devices/system/cpu/cpu${cpu}/online"
done < "$selected_backup"
printf "CPU configuration restored.\n"
break
else
printf "Invalid selection. Please try again.\n"
fi
done
}
# Log errors and changes
function logError() {
local message="$1"
printf "%s\n" "$message" >&2
printf "%s - %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$message" >> "$LOGFILE"
}
# Log Hyperthreading changes
function logHyperThreadingChange() {
local action="$1"
printf "%s: Hyperthreading was %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$action" >> "$LOGFILE"
}
# Check CPU load and warn the user if it's too high
function checkCpuLoad() {
local load
load=$(awk '{print $1}' /proc/loadavg)
local cores
cores=$(nproc)
if (( $(echo "$load > $cores * 0.75" | bc -l) )); then
printf "Warning: CPU load is high (%.2f). Disabling Hyperthreading under high load may increase workload and cause system instability.\n" "$load"
while true; do
read -p "Do you want to resolve the high CPU load before continuing? [y/n/continue anyway] " response
case $response in
[Yy]* )
printf "Please resolve the high CPU load before proceeding.\n"
return 1
;;
[Nn]* )
printf "You chose to continue without addressing the CPU load issue.\n"
break
;;
[Cc]* )
printf "Continuing anyway despite high CPU load.\n"
break
;;
* )
printf "Invalid input. Please answer 'y', 'n', or 'continue anyway'.\n"
;;
esac
done
else
printf "CPU load is normal: %.2f\n" "$load"
fi
}
# Verify if hyperthreading has been changed correctly
function verifyHyperThreading() {
local expected_status="$1" # 1 for enabled, 0 for disabled
local current_status
current_status=$(lscpu | grep -oP 'Thread\(s\) per core:\s*\K[0-9]+')
if [[ "$expected_status" -eq 1 && "$current_status" -gt 1 ]]; then
printf "Verification successful: Hyperthreading is ENABLED.\n"
elif [[ "$expected_status" -eq 0 && "$current_status" -eq 1 ]]; then
printf "Verification successful: Hyperthreading is DISABLED.\n"
else
logError "Error: Verification failed. Expected Hyperthreading to be %s, but it is not." \
"$([[ "$expected_status" -eq 1 ]] && echo "ENABLED" || echo "DISABLED")"
return 1
fi
}
# Function to toggle hyperthreading on or off
function toggleHyperThreading() {
backupCpuConfig # Backup the CPU configuration before making changes
for CPU in /sys/devices/system/cpu/cpu[0-9]*; do
CPUID=$(basename "$CPU" | cut -b4-)
THREAD1=$(cut -f1 -d, < "$CPU/topology/thread_siblings_list")
if [[ "$CPUID" == "$THREAD1" ]]; then
printf "CPU: %s -> enable\n" "$CPUID"
if ! echo "1" > "$CPU/online"; then
logError "Error: Failed to enable hyperthreading on CPU $CPUID."
continue
fi
else
printf "CPU: %s -> %s\n" "$CPUID" "$([[ "$HYPERTHREADING" -eq 0 ]] && echo "disabled" || echo "enabled")"
if ! echo "$HYPERTHREADING" > "$CPU/online"; then
logError "Error: Failed to change hyperthreading on CPU $CPUID."
continue
fi
fi
done
}
# Disable hyperthreading
function disabled() {
printf "Disabling HyperThreading\n"
HYPERTHREADING=0
checkCpuLoad || continueAnywayCheck
toggleHyperThreading
verifyHyperThreading 0 # Verify if Hyperthreading is disabled
logHyperThreadingChange "disabled"
}
# Enable hyperthreading
function enabled() {
printf "Enabling HyperThreading\n"
HYPERTHREADING=1
toggleHyperThreading
verifyHyperThreading 1 # Verify if Hyperthreading is enabled
logHyperThreadingChange "enabled"
}
# Additional check if user still wants to continue after the issue persists
function continueAnywayCheck() {
while true; do
read -p "The issue still persists. Do you want to continue anyway? [y/n] " choice
case $choice in
[Yy]* )
printf "Continuing despite the issue.\n"
break
;;
[Nn]* )
printf "Aborting operation. Please resolve the issue first.\n"
exit 1
;;
* )
printf "Invalid input. Please answer 'y' for yes or 'n' for no.\n"
;;
esac
done
}
# Display detailed hyperthreading status for all CPUs
function detailedHyperThreadingStatus() {
for CPU in /sys/devices/system/cpu/cpu[0-9]*; do
CPUID=$(basename "$CPU" | cut -b4-)
THREADS=$(cat "$CPU/topology/thread_siblings_list")
# Check if the 'online' file exists before trying to read it
if [[ ! -f "$CPU/online" ]]; then
printf "Skipping CPU: %s (no 'online' file)\n" "$CPUID"
continue
fi
ONLINE=$(cat "$CPU/online")
printf "CPU: %s\t Siblings: %s\t Online: %s\n" "$CPUID" "$THREADS" "$ONLINE"
done
}
# Check for security mitigations that disable hyperthreading
function checkSecurityMitigations() {
local mitigations
mitigations=$(grep -o 'mitigations=off' /proc/cmdline)
if [[ "$mitigations" == "mitigations=off" ]]; then
printf "Warning: All security mitigations are turned off. Hyperthreading might be enabled but unprotected.\n"
else
printf "Security mitigations are active. Checking hyperthreading status...\n"
fi
local noht_param
noht_param=$(grep -o 'noht' /proc/cmdline)
if [[ "$noht_param" == "noht" ]]; then
printf "Security mitigation detected: Hyperthreading is disabled via kernel boot parameter 'noht'.\n"
return 1
else
printf "No kernel boot parameters disabling hyperthreading found.\n"
fi
}
# Check if a backup exists and prompt the user to restore it
function promptRestoreBackup() {
local backups=($BACKUP_DIR/cpu_config_backup_*)
local num_backups=${#backups[@]}
if [[ $num_backups -gt 0 ]]; then
printf "A previous CPU configuration backup was found.\n"
printf "Do you want to restore the previous CPU configuration? [y/n]\n"
local SECONDS_LEFT=5
while [[ $SECONDS_LEFT -gt 0 ]]; do
printf "\rProceeding without restoring the backup in %d seconds... " "$SECONDS_LEFT"
read -t 1 -n 1 response # Read user input with a 1 second timeout
if [[ "$response" =~ [Yy] ]]; then
restoreCpuConfig
return
elif [[ "$response" =~ [Nn] ]]; then
printf "\nProceeding without restoring the backup.\n"
return
fi
SECONDS_LEFT=$((SECONDS_LEFT - 1))
done
printf "\nNo input detected. Proceeding without restoring the backup.\n"
else
printf "No previous backup found. Proceeding with current configuration.\n"
fi
}
# Main function
function main() {
ensureRoot # Check if script is run as root
promptRestoreBackup # Check and prompt the user to restore the previous backup
detailedHyperThreadingStatus # Display hyperthreading details for all CPUs
checkSecurityMitigations # Check if security mitigations are disabling hyperthreading
while true; do
read -p "Type 'e' to enable, 'd' to disable hyperthreading, or 'q' to quit [e/d/q]: " ed
case $ed in
[Ee]* ) enabled; break ;;
[Dd]* ) disabled; break ;;
[Qq]* ) exit ;;
* ) printf "Please answer 'e' to enable, 'd' to disable hyperthreading, or 'q' to quit.\n" ;;
esac
done
}
# Run the main function
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment