#!/bin/bash
# shellcheck disable=SC1091

#
# Copyright (c) 2020 SUSE LLC
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# Optimise kernel parameters for running SAP HANA and HANA based products (such as Business One).
# The calculations are based on:
# - Parameters tuned by SAP installation wizard and configure_HANA.sh.
# - Various SAP Notes.
# Authors:
#   Angela Briel <abriel@suse.com>
#   Howard Guo <hguo@suse.com>

cd /usr/lib/sapconf || exit 1
. util.sh
. common.sh

start() {
    log "--- "
    log "--- starting sapconf ...."
    log "--- "
    # prevent a second start (apply of values) as this will override the
    # saved old values of the parameters and reverting back to the
    # original state will be impossible
    [ -f /var/run/sapconf/active ] && log "sapconf already active, no second start supported" && exit 0
    [ ! -d /var/run/sapconf ] && mkdir -p /var/run/sapconf
    touch /var/run/sapconf/active

    log "--- Going to apply SAP tuning techniques"
    # Common tuning techniques apply to HANA and NetWeaver
    tune_preparation
    # SAP Note 2578899 - Installation notes
    tune_uuidd_socket

    # Read value requirements from sysconfig
    # if value is not set in sysconfig file, log a message and keep the
    # current system value
    if [ -r /etc/sysconfig/sapconf ]; then
        source_sysconfig /etc/sysconfig/sapconf
    else
        log 'Failed to read /etc/sysconfig/sapconf'
        exit 1
    fi

    # paranoia: should not happen, because post script of package installation
    # should rewrite variable names. But....
    for par in SHMMNI_DEF DIRTY_BYTES_DEF DIRTY_BG_BYTES_DEF; do
        npar=${par%_*}
        if [ -n "${!par}" ] && [ -z "${!npar}" ]; then
            # the only interesting case:
            # the old variable name is declared in the sysconfig file, but
            # NOT the new variable name
            # So set the new variable name  with the value of the old one
            declare $npar=${!par}
        fi
    done

    # SAP Note 2534844
    save_value kernel.shmmni "$(sysctl -n kernel.shmmni)"
    chk_and_set_conf_val SHMMNI kernel.shmmni

    # SAP Note 2578899
    ps=$(getconf PAGESIZE)
    [ ! "$ps" ] && ps=4096 # fallback and set a default
    min_val=$(math "$ps * 2")
    if [ "$DIRTY_BYTES" != "" ] && [ "$DIRTY_BYTES" -lt "$min_val" ]; then
        log "ATTENTION: wrong value set in sysconfig file for 'DIRTY_BYTES'. It's '$DIRTY_BYTES', but need to be at least '$min_val'"
        log "Leaving vm.dirty_bytes unchanged"
    else
        save_value vm.dirty_bytes "$(sysctl -n vm.dirty_bytes)"
        save_value vm.dirty_ratio "$(sysctl -n vm.dirty_ratio)" # value needed for revert of vm.dirty_bytes
        chk_and_set_conf_val DIRTY_BYTES vm.dirty_bytes
    fi
    if [ "$DIRTY_BG_BYTES" != "" ] && [ "$DIRTY_BG_BYTES" -eq 0 ]; then
        log "ATTENTION: wrong value set in sysconfig file for 'DIRTY_BG_BYTES'. It's set to '0', but needs to be >0"
        log "Leaving vm.dirty_background_bytes unchanged"
    else
        save_value vm.dirty_background_bytes "$(sysctl -n vm.dirty_background_bytes)"
        save_value vm.dirty_background_ratio "$(sysctl -n vm.dirty_background_ratio)" # value needed for revert of vm.dirty_background_bytes
        chk_and_set_conf_val DIRTY_BG_BYTES vm.dirty_background_bytes
    fi

    # SAP Note 2382421
    cur_val=$(sysctl -n net.ipv4.tcp_slow_start_after_idle)
    TCP_SLOW_START=$(chk_conf_val TCP_SLOW_START "$cur_val")
    if [ "$cur_val" != "$TCP_SLOW_START" ]; then
        save_value net.ipv4.tcp_slow_start_after_idle "$cur_val"
        log "Change net.ipv4.tcp_slow_start_after_idle from $cur_val to $TCP_SLOW_START"
        sysctl -q -w net.ipv4.tcp_slow_start_after_idle="$TCP_SLOW_START"
    else
        log "Leaving net.ipv4.tcp_slow_start_after_idle unchanged at $cur_val"
    fi

    # SAP Note 2684254 - KSM and AutoNUMA both should be off
    save_and_set_sys_val ksm /sys/kernel/mm/ksm/run
    save_and_set_sys_val numa_balancing /proc/sys/kernel/numa_balancing

    # SAP Note 2684254 - Transparent Hugepage should be never
    # SAP Note 2055470 - Ignore transparent huge pages and c-state information given in the first two notes above. These technologies are different on IBM Power Servers. (Version 68 from Oct 11, 2017)
    # the restriction for Power systems was removed from the SAP Note with Version 69 from 15.03.2018
    cur_val=$(sed 's%.*\[\(.*\)\].*%\1%' /sys/kernel/mm/transparent_hugepage/enabled)
    THP=$(chk_conf_val THP "$cur_val")
    if [ "$cur_val" != "$THP" ]; then
        save_value thp "$cur_val"
        log "Change transparent_hugepage from $cur_val to $THP"
        echo "$THP" > /sys/kernel/mm/transparent_hugepage/enabled
    else
        log "Leaving transparent_hugepage unchanged at $cur_val"
    fi

    if [[ $(uname -m) == x86_64 ]]; then
        # SAP Note 2684254 - performance settings
        log "--- Going to apply performance settings"
        # latency settings
        set_force_latency
        # energy_perf_bias settings
        set_perf_bias
        # scaling governor settings
        set_governor
        # min_perf_pct settings
        set_min_perf_pct
        log "--- Finished application of performance settings"
    fi

    log "--- Finished application of SAP tuning techniques"
}

stop() {
    log "--- "
    log "--- stopping sapconf ...."
    log "--- "
    [ ! -f /var/run/sapconf/active ] && log "no active sapconf tuning, so nothing to revert" && exit 0

    log "--- Going to revert SAP tuning parameters"

    revert_preparation

    # Restore kernel.shmmni, vm.dirty_bytes, vm.dirty_background_bytes, net.ipv4.tcp_slow_start_after_idle
    # to revert vm.dirty_bytes first revert vm.dirty_ratio
    # to revert vm.dirty_background_ratio (reset during set of vm.dirty_background_bytes)
    # first revert vm.dirty_background_bytes, then vm.dirty_background_ratio
    for rest_value in kernel.shmmni vm.dirty_ratio vm.dirty_bytes vm.dirty_background_bytes vm.dirty_background_ratio net.ipv4.tcp_slow_start_after_idle; do
        TVAL=$(restore_value $rest_value)
        [ ! "$TVAL" ] && continue
        case "$rest_value" in
        vm.dirty_ratio|vm.dirty_background_bytes|vm.dirty_background_ratio)
            if [ "$TVAL" -eq 0 ]; then
                TVAL=""
            fi
        ;;
        vm.dirty_bytes)
            ps=$(getconf PAGESIZE)
            [ ! "$ps" ] && ps=4096 # fallback and set a default
            min_val=$(math "$ps * 2")
            if [ "$TVAL" -eq 0 ] || [ "$TVAL" -lt "$min_val" ]; then
                TVAL=""
            fi
        ;;
        esac
        [ "$TVAL" ] && log "Restoring $rest_value=$TVAL" && sysctl -q -w "$rest_value=$TVAL"
    done

    # Restore THP, KSM and AutoNUMA settings
    THP=$(restore_value thp)
    [ "$THP" ] && log "Restoring thp=$THP" && echo "$THP"  > /sys/kernel/mm/transparent_hugepage/enabled

    KSM=$(restore_value ksm)
    [ "$KSM" ] && log "Restoring ksm=$KSM" && echo "$KSM" > /sys/kernel/mm/ksm/run
    NUMA_BALANCING=$(restore_value numa_balancing)
    [ "$NUMA_BALANCING" ] && log "Restoring numa_balancing=$NUMA_BALANCING" && echo "$NUMA_BALANCING" > /proc/sys/kernel/numa_balancing

    if [[ $(uname -m) == x86_64 ]]; then
        # restore performance settings
        restore_force_latency
        restore_perf_bias
        restore_governor
        # restore min_perf_pct
        if [ -d /sys/devices/system/cpu/intel_pstate ]; then
            MIN_PERF_PCT=$(restore_value min_perf_pct)
            [ "$MIN_PERF_PCT" ] && log "Restoring min_perf_pct=$MIN_PERF_PCT" && echo "$MIN_PERF_PCT" > /sys/devices/system/cpu/intel_pstate/min_perf_pct
        fi
    fi

    rm -f /var/run/sapconf/active
    log "--- Finished reverting SAP tuning parameters"
}

# main
if [ -f /run/sapconf_during_pkg_inst ]; then
    # workaround to prevent service reload during preun/postun from a previous
    # sapconf (with tuned support) package, which gets triggered during package
    # update of sapconf
    exit 0
fi

log "--- /usr/sbin/sapconf called with '$1'"
(systemctl -q is-enabled tuned 2>/dev/null || systemctl -q is-active tuned) && log "ATTENTION: tuned service is enabled/active, so we may encounter conflicting tuning values"

if [ "$1" != "status" ]; then
    # service should fail, if saptune.service is enabled or has exited / save
    # state files are present
    # (jsc#SLE-10987 decision)
    chk_active_saptune || exit 1
fi

case "$1" in
start)
    start
    ;;
stop)
    stop
    ;;
status)
    systemctl status sapconf --no-pager && exit 0 || exit 1
    ;;
*)
    echo "Usage: $0 {start|stop|status}"
    exit 2
    ;;
esac
