|
#!/bin/bash |
|
# Tune I/O scheduler for sda |
|
# Usage: tune_sda_iosched.sh [multiplier] |
|
# |
|
# Workload: pure large sequential I/O (photo reads, snapshotter video frame writes) |
|
# DB has been moved to NVMe SSD - no more random I/O on this disk. |
|
# |
|
# Multiplier 0 = kernel defaults |
|
# Multiplier 1 = base tuned values (recommended for normal use) |
|
# Multiplier 2 = 2× base (snapshotter bulk writing sessions) |
|
# etc. (non-integer multipliers accepted, e.g. 1.5) |
|
# |
|
# Parameters scaled: |
|
# read_expire (ms) - base 750 (relaxed, sequential reads aren't latency-sensitive) |
|
# write_expire (ms) - base 5000 (same as default, writes are already sequential) |
|
# writes_starved - base 2 (no competing workloads, keep default) |
|
# fifo_batch - base 32 (less reordering needed, I/O is already sequential) |
|
# nr_requests - base 128 (no deep queue needed for sequential workload) |
|
# read_ahead_kb - base 8192 (8MB readahead, key for large sequential reads) |
|
# wbt_lat_usec - inversely scaled: mult 0=75000, 1=75000, >=2=0 (disabled) |
|
# # front_merges - boolean, not scaled; confirmed 1 (enabled) - correct for HDD |
|
|
|
DEFAULTS="500 5000 2 16 60 128 75000 1" |
|
BASES="750 5000 2 32 128 8192 75000 1" |
|
LABELS="read_expire write_expire writes_starved fifo_batch nr_requests read_ahead_kb wbt_lat_usec front_merges" |
|
PATHS=( |
|
/sys/block/sda/queue/iosched/read_expire |
|
/sys/block/sda/queue/iosched/write_expire |
|
/sys/block/sda/queue/iosched/writes_starved |
|
/sys/block/sda/queue/iosched/fifo_batch |
|
/sys/block/sda/queue/nr_requests |
|
/sys/block/sda/queue/read_ahead_kb |
|
/sys/block/sda/queue/wbt_lat_usec |
|
/sys/block/sda/queue/iosched/front_merges |
|
) |
|
|
|
MULT=${1:-2} |
|
|
|
if ! [[ "$MULT" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then |
|
echo "Usage: $0 [multiplier] (non-negative number, default: 2)" |
|
echo " 0 = kernel defaults, 1 = base tuned, 2 = 2x base, etc." |
|
exit 1 |
|
fi |
|
|
|
read -r -a DEF_ARR <<< "$DEFAULTS" |
|
read -r -a BASE_ARR <<< "$BASES" |
|
read -r -a LBL_ARR <<< "$LABELS" |
|
|
|
# Compute the values to apply |
|
APPLY=() |
|
for i in "${!BASE_ARR[@]}"; do |
|
base=${BASE_ARR[$i]} |
|
def=${DEF_ARR[$i]} |
|
lbl=${LBL_ARR[$i]} |
|
|
|
if [[ "$MULT" == "0" ]]; then |
|
APPLY+=("$def") |
|
elif [[ "$lbl" == "wbt_lat_usec" ]]; then |
|
# Inverse: higher mult = lower latency cap; 0 = disabled |
|
# mult 1 = 75000 (default), mult 2+ = 0 (fully disabled) |
|
if (( $(echo "$MULT >= 2" | bc -l) )); then |
|
val=0 |
|
else |
|
val=$(echo "scale=0; $base / $MULT / 1" | bc) |
|
fi |
|
APPLY+=("$val") |
|
elif [[ "$lbl" == "front_merges" ]]; then |
|
# Boolean - always enabled (1) for HDD; multiplier 0 also keeps it 1 |
|
APPLY+=(1) |
|
else |
|
val=$(echo "scale=0; $base * $MULT / 1" | bc) |
|
APPLY+=("$val") |
|
fi |
|
done |
|
|
|
# Read current values |
|
CUR=() |
|
for path in "${PATHS[@]}"; do |
|
CUR+=("$(cat "$path" 2>/dev/null || echo '?')") |
|
done |
|
|
|
# Print header |
|
printf "%-20s %10s %10s %10s %10s\n" "parameter" "default" "current" "will-apply" "after" |
|
printf "%-20s %10s %10s %10s %10s\n" "--------------------" "----------" "----------" "----------" "----------" |
|
|
|
# Apply and read back |
|
AFTER=() |
|
for i in "${!PATHS[@]}"; do |
|
path=${PATHS[$i]} |
|
val=${APPLY[$i]} |
|
echo "$val" > "$path" 2>/dev/null |
|
AFTER+=("$(cat "$path" 2>/dev/null || echo '?')") |
|
done |
|
|
|
# Print all rows |
|
for i in "${!LBL_ARR[@]}"; do |
|
printf "%-20s %10s %10s %10s %10s\n" \ |
|
"${LBL_ARR[$i]}" "${DEF_ARR[$i]}" "${CUR[$i]}" "${APPLY[$i]}" "${AFTER[$i]}" |
|
done |