cd8d5cb
#!/bin/bash
cd8d5cb
#
cd8d5cb
# Copyright 2009 Red Hat, Inc. and/or its affiliates.
cd8d5cb
# Released under the GPL
cd8d5cb
#
cd8d5cb
# Author:      Dan Kenigsberg <danken@redhat.com>
cd8d5cb
#
cd8d5cb
# ksmtuned - a simple script that controls whether (and with what vigor) ksm
cd8d5cb
# should search for duplicated pages.
cd8d5cb
#
cd8d5cb
# starts ksm when memory commited to qemu processes exceeds a threshold, and
cd8d5cb
# make ksm work harder and harder untill memory load falls below that
cd8d5cb
# threshold.
cd8d5cb
#
39f14e1
# send SIGUSR1 to this process right after a new qemu process is started, or
39f14e1
# following its death, to retune ksm accordingly
39f14e1
#
cd8d5cb
# needs testing and ironing. contact danken@redhat.com if something breaks.
cd8d5cb
cd8d5cb
if [ -f /etc/ksmtuned.conf ]; then
cd8d5cb
    . /etc/ksmtuned.conf
cd8d5cb
fi
cd8d5cb
563054a
debug() {
563054a
    if [ -n "$DEBUG" ]; then
563054a
        s="`/bin/date`: $*"
563054a
        [ -n "$LOGFILE" ] && echo "$s" >> "$LOGFILE" || echo "$s"
563054a
    fi
563054a
}
563054a
563054a
cd8d5cb
KSM_MONITOR_INTERVAL=${KSM_MONITOR_INTERVAL:-60}
cd8d5cb
KSM_NPAGES_BOOST=${KSM_NPAGES_BOOST:-300}
cd8d5cb
KSM_NPAGES_DECAY=${KSM_NPAGES_DECAY:--50}
cd8d5cb
cd8d5cb
KSM_NPAGES_MIN=${KSM_NPAGES_MIN:-64}
cd8d5cb
KSM_NPAGES_MAX=${KSM_NPAGES_MAX:-1250}
cd8d5cb
# millisecond sleep between ksm scans for 16Gb server. Smaller servers sleep
cd8d5cb
# more, bigger sleep less.
cd8d5cb
KSM_SLEEP_MSEC=${KSM_SLEEP_MSEC:-10}
cd8d5cb
cd8d5cb
KSM_THRES_COEF=${KSM_THRES_COEF:-20}
cd8d5cb
KSM_THRES_CONST=${KSM_THRES_CONST:-2048}
cd8d5cb
cd8d5cb
total=`awk '/^MemTotal:/ {print $2}' /proc/meminfo`
563054a
debug total $total
cd8d5cb
cd8d5cb
npages=0
cd8d5cb
sleep=$[KSM_SLEEP_MSEC * 16 * 1024 * 1024 / total]
cd8d5cb
[ $sleep -le 10 ] && sleep=10
563054a
debug sleep $sleep
cd8d5cb
thres=$[total * KSM_THRES_COEF / 100]
cd8d5cb
if [ $KSM_THRES_CONST -gt $thres ]; then
cd8d5cb
    thres=$KSM_THRES_CONST
cd8d5cb
fi
563054a
debug thres $thres
cd8d5cb
cd8d5cb
KSMCTL () {
cd8d5cb
    case x$1 in
cd8d5cb
        xstop)
cd8d5cb
            echo 0 > /sys/kernel/mm/ksm/run
cd8d5cb
            ;;
cd8d5cb
        xstart)
cd8d5cb
            echo $2 > /sys/kernel/mm/ksm/pages_to_scan
cd8d5cb
            echo $3 > /sys/kernel/mm/ksm/sleep_millisecs
cd8d5cb
            echo 1 > /sys/kernel/mm/ksm/run
cd8d5cb
            ;;
cd8d5cb
    esac
cd8d5cb
}
cd8d5cb
cd8d5cb
committed_memory () {
cd8d5cb
    # calculate how much memory is committed to running qemu processes
cd8d5cb
    local progname
2b6f88d
    progname=${1:-qemu-kvm}
2b6f88d
    ps -C "$progname" -o rsz | awk '{ sum += $1 }; END { print sum }'
cd8d5cb
}
cd8d5cb
cd8d5cb
free_memory () {
afaf04c
    awk '/^(MemFree|Buffers|Cached):/ {free += $2}; END {print free}' \
cd8d5cb
                /proc/meminfo
cd8d5cb
}
cd8d5cb
cd8d5cb
increase_npages() {
cd8d5cb
    local delta
cd8d5cb
    delta=${1:-0}
cd8d5cb
    npages=$[npages + delta]
cd8d5cb
    if [ $npages -lt $KSM_NPAGES_MIN ]; then
cd8d5cb
        npages=$KSM_NPAGES_MIN
cd8d5cb
    elif [ $npages -gt $KSM_NPAGES_MAX ]; then
cd8d5cb
        npages=$KSM_NPAGES_MAX
cd8d5cb
    fi
cd8d5cb
    echo $npages
cd8d5cb
}
cd8d5cb
cd8d5cb
cd8d5cb
adjust () {
cd8d5cb
    local free committed
cd8d5cb
    free=`free_memory`
cd8d5cb
    committed=`committed_memory`
563054a
    debug committed $committed free $free
cd8d5cb
    if [ $[committed + thres] -lt $total -a $free -gt $thres ]; then
cd8d5cb
        KSMCTL stop
563054a
        debug "$[committed + thres] < $total and free > $thres, stop ksm"
cd8d5cb
        return 1
cd8d5cb
    fi
563054a
    debug "$[committed + thres] > $total, start ksm"
cd8d5cb
    if [ $free -lt $thres ]; then
cd8d5cb
        npages=`increase_npages $KSM_NPAGES_BOOST`
563054a
        debug "$free < $thres, boost"
cd8d5cb
    else
cd8d5cb
        npages=`increase_npages $KSM_NPAGES_DECAY`
563054a
        debug "$free > $thres, decay"
cd8d5cb
    fi
cd8d5cb
    KSMCTL start $npages $sleep
563054a
    debug "KSMCTL start $npages $sleep"
cd8d5cb
    return 0
cd8d5cb
}
cd8d5cb
39f14e1
function nothing () {
39f14e1
    :
39f14e1
}
39f14e1
cd8d5cb
loop () {
39f14e1
    trap nothing SIGUSR1
cd8d5cb
    while true
cd8d5cb
    do
39f14e1
        sleep $KSM_MONITOR_INTERVAL &
39f14e1
        wait $!
cd8d5cb
        adjust
cd8d5cb
    done
cd8d5cb
}
cd8d5cb
cd8d5cb
PIDFILE=${PIDFILE-/var/run/ksmtune.pid}
cd8d5cb
if touch "$PIDFILE"; then
cd8d5cb
  loop &
cd8d5cb
  echo $! > "$PIDFILE"
cd8d5cb
fi