From 9fdf235e6d4ae8b6170a046be35c62ead7e1bb2c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jan=20R=C4=99korajski?= Date: Sun, 3 Nov 2013 21:01:25 +0100 Subject: [PATCH] - added Kernel Samepage Merging support from qemu-kvm --- ksm.service | 13 +++++ ksm.sysconfig | 4 ++ ksmctl.c | 77 +++++++++++++++++++++++++++ ksmtuned | 133 +++++++++++++++++++++++++++++++++++++++++++++++ ksmtuned.conf | 21 ++++++++ ksmtuned.service | 12 +++++ qemu.spec | 30 +++++++++++ 7 files changed, 290 insertions(+) create mode 100644 ksm.service create mode 100644 ksm.sysconfig create mode 100644 ksmctl.c create mode 100644 ksmtuned create mode 100644 ksmtuned.conf create mode 100644 ksmtuned.service diff --git a/ksm.service b/ksm.service new file mode 100644 index 0000000..dbffa6e --- /dev/null +++ b/ksm.service @@ -0,0 +1,13 @@ +[Unit] +Description=Kernel Samepage Merging +ConditionPathExists=/sys/kernel/mm/ksm + +[Service] +Type=oneshot +RemainAfterExit=yes +EnvironmentFile=-/etc/sysconfig/ksm +ExecStart=/usr/sbin/ksmctl start +ExecStop=/usr/sbin/ksmctl stop + +[Install] +WantedBy=multi-user.target diff --git a/ksm.sysconfig b/ksm.sysconfig new file mode 100644 index 0000000..d99656d --- /dev/null +++ b/ksm.sysconfig @@ -0,0 +1,4 @@ +# The maximum number of unswappable kernel pages +# which may be allocated by ksm (0 for unlimited) +# If unset, defaults to half of total memory +# KSM_MAX_KERNEL_PAGES= diff --git a/ksmctl.c b/ksmctl.c new file mode 100644 index 0000000..af39591 --- /dev/null +++ b/ksmctl.c @@ -0,0 +1,77 @@ +/* Start/stop KSM, for systemd. + * Copyright (C) 2009, 2011 Red Hat, Inc. + * Written by Paolo Bonzini . + * Based on the original sysvinit script by Dan Kenigsberg + * This file is distributed under the GNU General Public License, version 2 + * or later. */ + +#include +#include +#include +#include +#include +#include + +#define KSM_MAX_KERNEL_PAGES_FILE "/sys/kernel/mm/ksm/max_kernel_pages" +#define KSM_RUN_FILE "/sys/kernel/mm/ksm/run" + +char *program_name; + +int usage(void) +{ + fprintf(stderr, "Usage: %s {start|stop}\n", program_name); + return 1; +} + +int write_value(uint64_t value, char *filename) +{ + FILE *fp; + if (!(fp = fopen(filename, "w")) || + fprintf(fp, "%llu\n", (unsigned long long) value) == EOF || + fflush(fp) == EOF || + fclose(fp) == EOF) + return 1; + + return 0; +} + +uint64_t ksm_max_kernel_pages() +{ + char *var = getenv("KSM_MAX_KERNEL_PAGES"); + char *endptr; + uint64_t value; + if (var && *var) { + value = strtoll(var, &endptr, 0); + if (value < LLONG_MAX && !*endptr) + return value; + } + /* Unless KSM_MAX_KERNEL_PAGES is set, let KSM munch up to half of + * total memory. */ + return sysconf(_SC_PHYS_PAGES) / 2; +} + +int start(void) +{ + if (access(KSM_MAX_KERNEL_PAGES_FILE, R_OK) >= 0) + write_value(ksm_max_kernel_pages(), KSM_MAX_KERNEL_PAGES_FILE); + return write_value(1, KSM_RUN_FILE); +} + +int stop(void) +{ + return write_value(0, KSM_RUN_FILE); +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + if (argc < 2) { + return usage(); + } else if (!strcmp(argv[1], "start")) { + return start(); + } else if (!strcmp(argv[1], "stop")) { + return stop(); + } else { + return usage(); + } +} diff --git a/ksmtuned b/ksmtuned new file mode 100644 index 0000000..19b5718 --- /dev/null +++ b/ksmtuned @@ -0,0 +1,133 @@ +#!/bin/sh +# +# Copyright 2009 Red Hat, Inc. and/or its affiliates. +# Released under the GPL +# +# Author: Dan Kenigsberg +# un-bash-ed: Jan Rękorajski +# +# ksmtuned - a simple script that controls whether (and with what vigor) ksm +# should search for duplicated pages. +# +# starts ksm when memory commited to qemu processes exceeds a threshold, and +# make ksm work harder and harder untill memory load falls below that +# threshold. +# +# send SIGUSR1 to this process right after a new qemu process is started, or +# following its death, to retune ksm accordingly + +[ -f /etc/ksmtuned.conf ] && . /etc/ksmtuned.conf + +debug() { + if [ -n "$DEBUG" ]; then + s="`/bin/date`: $*" + [ -n "$LOGFILE" ] && echo "$s" >> "$LOGFILE" || echo "$s" + fi +} + +KSM_MONITOR_INTERVAL=${KSM_MONITOR_INTERVAL:-60} +KSM_NPAGES_BOOST=${KSM_NPAGES_BOOST:-300} +KSM_NPAGES_DECAY=${KSM_NPAGES_DECAY:--50} + +KSM_NPAGES_MIN=${KSM_NPAGES_MIN:-64} +KSM_NPAGES_MAX=${KSM_NPAGES_MAX:-1250} +# millisecond sleep between ksm scans for 16Gb server. Smaller servers sleep +# more, bigger sleep less. +KSM_SLEEP_MSEC=${KSM_SLEEP_MSEC:-10} + +KSM_THRES_COEF=${KSM_THRES_COEF:-20} +KSM_THRES_CONST=${KSM_THRES_CONST:-2048} + +total=`awk '/^MemTotal:/ {print $2}' /proc/meminfo` +debug total $total + +npages=0 +sleep=$(( $KSM_SLEEP_MSEC * 16 * 1024 * 1024 / $total )) +[ $sleep -le 10 ] && sleep=10 +debug sleep $sleep +thres=$(( $total * $KSM_THRES_COEF / 100 )) +if [ $KSM_THRES_CONST -gt $thres ]; then + thres=$KSM_THRES_CONST +fi +debug thres $thres + +KSMCTL () { + case x$1 in + xstop) + echo 0 > /sys/kernel/mm/ksm/run + ;; + xstart) + echo $2 > /sys/kernel/mm/ksm/pages_to_scan + echo $3 > /sys/kernel/mm/ksm/sleep_millisecs + echo 1 > /sys/kernel/mm/ksm/run + ;; + esac +} + +committed_memory () { + # calculate how much memory is committed to running qemu processes + local progname + progname=${1:-qemu-kvm} + ps -C "$progname" -o rsz | awk '{ sum += $1 }; END { print sum }' +} + +free_memory () { + awk '/^(MemFree|Buffers|Cached):/ {free += $2}; END {print free}' \ + /proc/meminfo +} + +increase_npages() { + local delta + delta=${1:-0} + npages=$(( $npages + $delta )) + if [ $npages -lt $KSM_NPAGES_MIN ]; then + npages=$KSM_NPAGES_MIN + elif [ $npages -gt $KSM_NPAGES_MAX ]; then + npages=$KSM_NPAGES_MAX + fi + echo $npages +} + + +adjust () { + local free committed + free=`free_memory` + committed=`committed_memory` + debug committed $committed free $free + if [ $(( $committed + $thres )) -lt $total -a $free -gt $thres ]; then + KSMCTL stop + debug "$(( $committed + $thres )) < $total and free > $thres, stop ksm" + return 1 + fi + debug "$(( $committed + $thres )) > $total, start ksm" + if [ $free -lt $thres ]; then + npages=`increase_npages $KSM_NPAGES_BOOST` + debug "$free < $thres, boost" + else + npages=`increase_npages $KSM_NPAGES_DECAY` + debug "$free > $thres, decay" + fi + KSMCTL start $npages $sleep + debug "KSMCTL start $npages $sleep" + return 0 +} + +nothing () { + : +} + +loop () { + trap nothing USR1 + while true + do + sleep $KSM_MONITOR_INTERVAL & + wait $! + adjust + done +} + +PIDFILE=${PIDFILE-/var/run/ksmtune.pid} +if touch "$PIDFILE"; then + loop & + echo $! > "$PIDFILE" +fi diff --git a/ksmtuned.conf b/ksmtuned.conf new file mode 100644 index 0000000..fc4518c --- /dev/null +++ b/ksmtuned.conf @@ -0,0 +1,21 @@ +# Configuration file for ksmtuned. + +# How long ksmtuned should sleep between tuning adjustments +# KSM_MONITOR_INTERVAL=60 + +# Millisecond sleep between ksm scans for 16Gb server. +# Smaller servers sleep more, bigger sleep less. +# KSM_SLEEP_MSEC=10 + +# KSM_NPAGES_BOOST=300 +# KSM_NPAGES_DECAY=-50 +# KSM_NPAGES_MIN=64 +# KSM_NPAGES_MAX=1250 + +# KSM_THRES_COEF=20 +# KSM_THRES_CONST=2048 + +# uncomment the following if you want ksmtuned debug info + +# LOGFILE=/var/log/ksmtuned +# DEBUG=1 diff --git a/ksmtuned.service b/ksmtuned.service new file mode 100644 index 0000000..39febcc --- /dev/null +++ b/ksmtuned.service @@ -0,0 +1,12 @@ +[Unit] +Description=Kernel Samepage Merging (KSM) Tuning Daemon +After=ksm.service +Requires=ksm.service + +[Service] +ExecStart=/usr/sbin/ksmtuned +ExecReload=/bin/kill -USR1 $MAINPID +Type=forking + +[Install] +WantedBy=multi-user.target diff --git a/qemu.spec b/qemu.spec index 2d6ca3f..9d10c0b 100644 --- a/qemu.spec +++ b/qemu.spec @@ -23,6 +23,13 @@ License: GPL v2+ Group: Applications/Emulators Source0: http://wiki.qemu-project.org/download/%{name}-%{version}.tar.bz2 # Source0-md5: 3a897d722457c5a895cd6ac79a28fda0 +# KSM control scripts +Source5: ksm.service +Source6: ksm.sysconfig +Source7: ksmctl.c +Source8: ksmtuned.service +Source9: ksmtuned +Source10: ksmtuned.conf Source11: qemu-guest-agent.service Source12: 99-qemu-guest-agent.rules Patch0: %{name}-cflags.patch @@ -625,6 +632,8 @@ ln -s ../error.h qapi/error.h # rebuild patched vesa tables with additional widescreen modes. %{__make} -C roms/vgabios stdvga-bios +%{__cc} %{SOURCE7} %{rpmcflags} -o ksmctl + %install rm -rf $RPM_BUILD_ROOT install -d $RPM_BUILD_ROOT{%{systemdunitdir},/usr/lib/binfmt.d} \ @@ -641,6 +650,14 @@ cat <<'EOF' > $RPM_BUILD_ROOT%{_sysconfdir}/qemu-ifup EOF +install -p %{SOURCE5} $RPM_BUILD_ROOT%{systemdunitdir}/ksm.service +install -p %{SOURCE6} $RPM_BUILD_ROOT/etc/sysconfig/ksm +install -p ksmctl $RPM_BUILD_ROOT%{_sbindir} + +install -p %{SOURCE8} $RPM_BUILD_ROOT%{systemdunitdir}/ksmtuned.service +install -p %{SOURCE9} $RPM_BUILD_ROOT%{_sbindir}/ksmtuned +install -p %{SOURCE10} $RPM_BUILD_ROOT%{_sysconfdir}/ksmtuned.conf + # For the qemu-guest-agent subpackage install the systemd # service and udev rules. install -p %{SOURCE11} $RPM_BUILD_ROOT%{systemdunitdir} @@ -664,11 +681,24 @@ rm -rf $RPM_BUILD_ROOT %groupadd -g 276 qemu %useradd -u 276 -g qemu -c "QEMU User" qemu +%post common +%systemd_post ksm.service +%systemd_post ksmtuned.service + +%preun common +%systemd_preun ksm.service +%systemd_preun ksmtuned.service + %postun common if [ "$1" = "0" ]; then %userremove qemu %groupremove qemu fi +%systemd_reload + +%triggerpostun common -- qemu-common < 1.6.1-4 +%systemd_trigger ksm.service +%systemd_trigger ksmtuned.service %post guest-agent %systemd_reload -- 2.44.0