#!/bin/bash
#################################################################
#
#   BSD LICENSE
# 
#   Copyright(c) 2007-2023 Intel Corporation. All rights reserved.
#   All rights reserved.
# 
#   Redistribution and use in source and binary forms, with or without
#   modification, are permitted provided that the following conditions
#   are met:
# 
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in
#       the documentation and/or other materials provided with the
#       distribution.
#     * Neither the name of Intel Corporation nor the names of its
#       contributors may be used to endorse or promote products derived
#       from this software without specific prior written permission.
# 
#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
#  version: QAT20.L.1.2.30-00078
#
#################################################################
#
### BEGIN INIT INFO
# Provides: QAT
# Required-Start: $ALL
# Required-Stop:
# Default-Start: 2 3 5
# Default-Stop: 0 1 4 6
# Description:  Intel QAT service
### END INIT INFO
#
# qat_service          Start/Stop the Intel QAT.
#
# chkconfig: 345 99 99
# description: modprobe the QAT modules, which loads dependant \
#       modules, before calling the user space \
#       utility to pass configuration parameters

test -f /etc/default/qat && . /etc/default/qat
SRIOV_ENABLE=${SRIOV_ENABLE-0}
LEGACY_LOADED=${LEGACY_LOADED-0}
DO_ENABLE_SRIOV=${DO_ENABLE_SRIOV-0}

# to protect parallel qat-service run instances
for pid in $(pidof -x qat_service); do
    if [ $pid != $$ ]; then
        echo "[$(date)] : qat_service : Process is already running with PID $pid"
        exit 1
    fi
done

INTEL_VENDORID="8086"
DH895_DEVICE_PCI_ID="0435"
DH895_DEVICE_PCI_ID_VM="0443"
C62X_DEVICE_PCI_ID="37c8"
C62X_DEVICE_PCI_ID_VM="37c9"
C3XX_DEVICE_PCI_ID="19e2"
C3XX_DEVICE_PCI_ID_VM="19e3"
D15XX_DEVICE_PCI_ID="6f54"
D15XX_DEVICE_PCI_ID_VM="6f55"
C4XX_DEVICE_PCI_ID="18a0"
C4XX_DEVICE_PCI_ID_VM="18a1"
QAT_4XXX_DEVICE_PCI_ID="4940"
QAT_4XXX_DEVICE_PCI_ID_VM="4941"
QAT_401XX_DEVICE_PCI_ID="4942"
QAT_401XX_DEVICE_PCI_ID_VM="4943"
QAT_402XX_DEVICE_PCI_ID="4944"
QAT_402XX_DEVICE_PCI_ID_VM="4945"
VQAT_DEVICE_PCI_ID="0da5"

INTREE_DRIVERS=(qat_dh895xcc
    qat_dh895xccvf
    qat_c3xxx
    qat_c3xxxvf
    qat_c62x
    qat_c62xvf
    qat_4xxx
    qat_4xxxvf
    intel_qat)

usage()
{
    echo
    echo --------------------------------------------------------
    echo USAGE:
    echo --------------------------------------------------------
    echo "#  $0 start||stop||status||restart||shutdown"
    echo --------------------------------------------------------
    echo " Note: If there is more devices in the system"
    echo " you can start, stop or restart separate device by "
    echo " passing the dev to be restarted or stopped as a"
    echo " parameter for instance: "
    echo " $0 stop qat_dev<N>"
    echo " where N is device number."
    echo " To see all devices in the system use:"
    echo " $0 status"
    echo --------------------------------------------------------
    exit 1
}

ADF_CTL=/usr/sbin/adf_ctl
OOT_ONLY=/dev/qat_dev_processes

# store the total number of each type of device
numDh895xDevicesPF=$(lspci -n | egrep -c "$INTEL_VENDORID:$DH895_DEVICE_PCI_ID")
numDh895xDevicesVF=$(lspci -n | egrep -c "$INTEL_VENDORID:$DH895_DEVICE_PCI_ID_VM")
numC62xDevicesPF=$(lspci -n | egrep -c "$INTEL_VENDORID:$C62X_DEVICE_PCI_ID")
numC62xDevicesVF=$(lspci -n | egrep -c "$INTEL_VENDORID:$C62X_DEVICE_PCI_ID_VM")
numC3xxDevicesPF=$(lspci -n | egrep -c "$INTEL_VENDORID:$C3XX_DEVICE_PCI_ID")
numC3xxDevicesVF=$(lspci -n | egrep -c "$INTEL_VENDORID:$C3XX_DEVICE_PCI_ID_VM")
numD15xxDevicesPF=$(lspci -n | egrep -c "$INTEL_VENDORID:$D15XX_DEVICE_PCI_ID")
numD15xxDevicesVF=$(lspci -n | egrep -c "$INTEL_VENDORID:$D15XX_DEVICE_PCI_ID_VM")
numC4xxDevicesPF=$(lspci -n | egrep -c "$INTEL_VENDORID:$C4XX_DEVICE_PCI_ID")
numC4xxDevicesVF=$(lspci -n | egrep -c "$INTEL_VENDORID:$C4XX_DEVICE_PCI_ID_VM")
num4xxxDevicesPF=$(lspci -n | egrep -c "$INTEL_VENDORID:($QAT_4XXX_DEVICE_PCI_ID|$QAT_401XX_DEVICE_PCI_ID|$QAT_402XX_DEVICE_PCI_ID)")
num4xxxDevicesVF=$(lspci -n | egrep -c "$INTEL_VENDORID:($QAT_4XXX_DEVICE_PCI_ID_VM|$QAT_401XX_DEVICE_PCI_ID_VM|$QAT_402XX_DEVICE_PCI_ID_VM)")
numVQATDevices=$(lspci -n | egrep -c "$INTEL_VENDORID:$VQAT_DEVICE_PCI_ID")

disable_sriov()
{
    PF_LIST=$($ADF_CTL $1 status | grep -e "^ *qat_dev" | grep -v "vf," | grep -v "vqat-adi," | awk '{print $1}')

    for PF_DEV in $PF_LIST; do
        # Extract the BSF to build the path to /sys/bus/.../sriov)_numvfs
        BSF=$($ADF_CTL $PF_DEV status | tail -1 | awk '{print $10}' | awk 'BEGIN{FS=","}{print $1}')
        D=$(echo $BSF | awk 'BEGIN{FS=":"}{print $1}')
        B=$(echo $BSF | awk 'BEGIN{FS=":"}{print $2}')

        # Get a list of all the VFs for this PF and bring them down
        VF_LIST=$($ADF_CTL status | grep "bsf: $D:$B" | grep "vf," | awk '{print $1}')
        for VF_DEV in $VF_LIST; do
            $ADF_CTL $VF_DEV down
        done
    done
}

enable_sriov()
{
    PF_LIST=$($ADF_CTL $1 status | grep -e "^ *qat_dev" | grep -v "vf," | grep -v "vqat-adi," | awk '{print $1}')

    for PF_DEV in $PF_LIST; do
        # Extract the BSF to build the path to /sys/bus/.../sriov)_numvfs
        BSF=$($ADF_CTL $PF_DEV status | tail -1 | awk '{print $10}' | awk 'BEGIN{FS=","}{print $1}')
        D=$(echo $BSF | awk 'BEGIN{FS=":"}{print $1}')
        B=$(echo $BSF | awk 'BEGIN{FS=":"}{print $2}')
        SF=$(echo $BSF | awk 'BEGIN{FS=":"}{print $3}')
        S=$(echo $SF | awk 'BEGIN{FS="."}{print $1}')
        F=$(echo $SF | awk 'BEGIN{FS="."}{print $2}')
        DEV_TYPE=$($ADF_CTL $PF_DEV status | tail -1 | awk 'BEGIN{FS="[, ]"}{print $5}')
        INST_ID=$($ADF_CTL $PF_DEV status | tail -1 | awk 'BEGIN{FS="[, ]"}{print $9}')
        NumberAdis=$(grep 'NumberAdis' /etc/${DEV_TYPE}_dev${INST_ID}.conf | grep -v -e "vf" -e "#.*NumberAdis" | awk 'BEGIN{FS="="}{print $2}')
        SYSFS_DIR=/sys/bus/pci/devices/${D}:${B}:${S}.${F}

        if [ -z "$NumberAdis" ] || [ "$NumberAdis" -eq 0 ]; then
            if [ ! -e ${SYSFS_DIR}/sriov_numvfs ]; then
                echo "Cannot enable SRIOV for $PF_DEV. No sriov_numvs file"
                exit 1
            fi

            NUMVFS=$(cat ${SYSFS_DIR}/sriov_numvfs)
            if [ $NUMVFS != 0 ]; then
                echo "SRIOV is already enabled for $PF_DEV"
                exit 1
            fi

            cat ${SYSFS_DIR}/sriov_totalvfs > ${SYSFS_DIR}/sriov_numvfs
            if [ $? != 0 ]; then
                echo "Could not enable SRIOV for $PF_DEV"
                exit 1
            fi
        elif [ "$NumberAdis" -gt 0 ]; then
            echo 0 > ${SYSFS_DIR}/sriov_numvfs
            if [ $? != 0 ]; then
                echo "Could not disable SRIOV for $PF_DEV"
                exit 1
            fi
        else
            echo "Invalid ADI number for $PF_DEV"
            exit 1
        fi

        # Get a list of all the VFs for this PF and bring then down
        VF_LIST=$($ADF_CTL status | grep "bsf: $D:$B" | grep "vf," | awk '{print $1}')
        for VF_DEV in $VF_LIST; do
            $ADF_CTL $VF_DEV restart
        done
    done
}

check_sriov()
{
    # Check if sriov should be enabled.
    if [ $SRIOV_ENABLE == 1 ]; then
        # If a specific device ($1), or any (empty $1) is specified that is a pf, then enable sriov.
        $ADF_CTL $1 status | grep -e "^ *qat_dev" | grep -v vf > /dev/null
        if [ $? == 0 ]; then
            DO_ENABLE_SRIOV=1
        else
            DO_ENABLE_SRIOV=0
        fi
    else
        DO_ENABLE_SRIOV=0
    fi
}

# Remove in-tree modules if loaded
remove_intree()
{
    if [ ! -c $OOT_ONLY ]; then
        for intree_driver in ${INTREE_DRIVERS[*]}; do
            modprobe -q -r $intree_driver
        done
    fi
}

case $1 in
    Start | start)
        remove_intree
        # First check if the modules are already installed
        # install them as necessary
        if [ $numDh895xDevicesPF != 0 ] && [ $LEGACY_LOADED == 0 ]; then
            lsmod | grep qat_dh895xcc > /dev/null 2>&1 || modprobe qat_dh895xcc
        fi
        if [ $numC62xDevicesPF != 0 ]; then
            lsmod | grep qat_c62x > /dev/null 2>&1 || modprobe qat_c62x
        fi
        if [ $numC3xxDevicesPF != 0 ]; then
            lsmod | grep qat_c3xxx > /dev/null 2>&1 || modprobe qat_c3xxx
        fi
        if [ $numD15xxDevicesPF != 0 ]; then
            lsmod | grep qat_d15xx > /dev/null 2>&1 || modprobe qat_d15xx
        fi
        if [ $numC4xxDevicesPF != 0 ]; then
            lsmod | grep qat_c4xxx > /dev/null 2>&1 || modprobe qat_c4xxx
            # Allow system to load dependent modules for qat_c4xxx
            sleep 20
        fi
        if [ $num4xxxDevicesPF != 0 ]; then
            lsmod | grep qat_4xxx > /dev/null 2>&1 || modprobe qat_4xxx
        fi
        if [ $(lsmod | grep "usdm_drv" | wc -l) == "0" ]; then
            modprobe usdm_drv
        fi
        # Loading VF drivers as necessary
        # The VF devices only appear after SRIOV is enabled on the PF,
        #  therefore we can't use lspci to determine that the VF driver should be loaded.
        #  Instead, if we want SRIOV, and we have a specific PF device, then load the driver.
        if [ $SRIOV_ENABLE == 1 ]; then
            if [ $numDh895xDevicesPF != 0 -o $numDh895xDevicesVF != 0 ] && [ $LEGACY_LOADED == 0 ]; then
                lsmod | grep qat_dh895xccvf > /dev/null 2>&1 || modprobe qat_dh895xccvf
            fi
            if [ $numC62xDevicesPF != 0 -o $numC62xDevicesVF != 0 ]; then
                lsmod | grep qat_c62xvf > /dev/null 2>&1 || modprobe qat_c62xvf
            fi
            if [ $numC3xxDevicesPF != 0 -o $numC3xxDevicesVF != 0 ]; then
                lsmod | grep qat_c3xxxvf > /dev/null 2>&1 || modprobe qat_c3xxxvf
            fi
            if [ $numD15xxDevicesPF != 0 -o $numD15xxDevicesVF != 0 ]; then
                lsmod | grep qat_d15xxvf > /dev/null 2>&1 || modprobe qat_d15xxvf
            fi
            if [ $numC4xxDevicesPF != 0 -o $numC4xxDevicesVF != 0 ]; then
                lsmod | grep qat_c4xxxvf > /dev/null 2>&1 || modprobe qat_c4xxxvf
            fi
            if [ $num4xxxDevicesPF != 0 -o $num4xxxDevicesVF != 0 ]; then
                lsmod | grep qat_4xxxvf > /dev/null 2>&1 || modprobe qat_4xxxvf
            fi
            if [ $numVQATDevices != 0 ]; then
                lsmod | grep qat_vqat > /dev/null 2>&1 || modprobe qat_vqat
            fi
        fi

        $ADF_CTL $2 restart

        check_sriov $2
        if [ $DO_ENABLE_SRIOV == 1 ]; then
            echo enable sriov
            enable_sriov $2
        fi


        # Show device status
        /usr/sbin/adf_ctl $2 status
        ;;

    Shutdown | shutdown)
        check_sriov $2
        if [ $DO_ENABLE_SRIOV == 1 ]; then
            echo disable sriov
            disable_sriov $2
        fi

        # Remove in-tree modules if loaded
        intree_removed=false
        for intree_driver in ${INTREE_DRIVERS[*]}; do
            if lsmod | grep -w $intree_driver > /dev/null 2>&1; then
                if [ "$(modinfo -F intree $intree_driver)" == "Y" ]; then
                    modprobe -q -r $intree_driver
                    # Verify successful removal
                    if lsmod | grep -w $intree_driver > /dev/null 2>&1; then
                        echo "Failed to remove in-tree $intree_driver"
                        exit 1
                    else
                        echo "In-tree QAT module $intree_driver removed"
                        intree_removed=true
                    fi
                fi
            fi
        done

        shutdown_success=true
        if [ "$intree_removed" = false ]; then
            if lsmod | grep intel_qat &> /dev/null; then
                $ADF_CTL $2 down
                if [ "$?" -ne 0 ]; then
                    shutdown_success=false
                fi
            else
                shutdown_success=true
            fi
        else
            echo "Skip shutdown operation"
        fi

        if [ "$shutdown_success" = false ]; then
            echo "QAT device is busy, hence shutdown operation is not allowed."
        else
            modprobe -q -r qat_dh895xccvf
            modprobe -q -r qat_c62xvf
            modprobe -q -r qat_c3xxxvf
            modprobe -q -r qat_d15xxvf
            modprobe -q -r qat_c4xxxvf
            modprobe -q -r qat_4xxxvf
            modprobe -q -r qat_dh895xcc
            modprobe -q -r qat_c62x
            modprobe -q -r qat_c3xxx
            modprobe -q -r qat_d15xx
            modprobe -q -r qat_c4xxx
            modprobe -q -r qat_4xxx
            modprobe -q -r qat_vqat
            modprobe -q -r usdm_drv
            modprobe -q -r intel_qat
        fi
        ;;

    Stop | stop)
        check_sriov $2
        if [ $DO_ENABLE_SRIOV == 1 ]; then
            echo disable sriov
            disable_sriov $2
        fi
        /usr/sbin/adf_ctl $2 down
        ;;

    Restart | restart)
        /usr/sbin/adf_ctl $2 restart

        check_sriov $2
        if [ $DO_ENABLE_SRIOV == 1 ]; then
            echo restart sriov
            enable_sriov $2
        fi
        ;;

    Status | status)
        /usr/sbin/adf_ctl status
        if [ "$?" -ne 0 ]; then
            echo "No devices found. Please start the driver using:"
            echo "$0 start"
        fi
        ;;

    *)
        usage
        ;;

esac
exit 0
