From 9164464963a87499b1448d436821bd3f7a7646d5 Mon Sep 17 00:00:00 2001 From: Adrian Calianu Date: Thu, 21 May 2020 09:02:54 +0200 Subject: [PATCH] Upgrade i40e drivers to 2.11.29 Signed-off-by: Adrian Calianu --- drivers/net/ethernet/intel/i40e/Makefile | 209 +- .../net/ethernet/intel/i40e/Module.supported | 1 + drivers/net/ethernet/intel/i40e/common.mk | 305 + drivers/net/ethernet/intel/i40e/i40e.h | 507 +- drivers/net/ethernet/intel/i40e/i40e_adminq.c | 385 +- drivers/net/ethernet/intel/i40e/i40e_adminq.h | 33 +- .../net/ethernet/intel/i40e/i40e_adminq_cmd.h | 575 +- drivers/net/ethernet/intel/i40e/i40e_alloc.h | 27 +- drivers/net/ethernet/intel/i40e/i40e_client.c | 210 +- drivers/net/ethernet/intel/i40e/i40e_client.h | 41 +- drivers/net/ethernet/intel/i40e/i40e_common.c | 2968 +++- drivers/net/ethernet/intel/i40e/i40e_dcb.c | 579 +- drivers/net/ethernet/intel/i40e/i40e_dcb.h | 60 +- drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c | 46 +- drivers/net/ethernet/intel/i40e/i40e_ddp.c | 478 + .../net/ethernet/intel/i40e/i40e_debugfs.c | 1154 +- drivers/net/ethernet/intel/i40e/i40e_devids.h | 41 +- drivers/net/ethernet/intel/i40e/i40e_diag.c | 70 +- drivers/net/ethernet/intel/i40e/i40e_diag.h | 27 +- .../net/ethernet/intel/i40e/i40e_ethtool.c | 3837 ++++- .../ethernet/intel/i40e/i40e_ethtool_stats.h | 293 + .../net/ethernet/intel/i40e/i40e_filters.c | 40 + .../net/ethernet/intel/i40e/i40e_filters.h | 11 + drivers/net/ethernet/intel/i40e/i40e_helper.h | 128 + drivers/net/ethernet/intel/i40e/i40e_hmc.c | 68 +- drivers/net/ethernet/intel/i40e/i40e_hmc.h | 27 +- .../net/ethernet/intel/i40e/i40e_lan_hmc.c | 135 +- .../net/ethernet/intel/i40e/i40e_lan_hmc.h | 27 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 12375 +++++++++++----- drivers/net/ethernet/intel/i40e/i40e_nvm.c | 553 +- drivers/net/ethernet/intel/i40e/i40e_osdep.h | 99 +- .../net/ethernet/intel/i40e/i40e_prototype.h | 327 +- drivers/net/ethernet/intel/i40e/i40e_ptp.c | 1120 +- .../net/ethernet/intel/i40e/i40e_register.h | 172 +- drivers/net/ethernet/intel/i40e/i40e_status.h | 28 +- drivers/net/ethernet/intel/i40e/i40e_trace.h | 62 +- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 1348 +- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 171 +- drivers/net/ethernet/intel/i40e/i40e_type.h | 270 +- .../ethernet/intel/i40e/i40e_virtchnl_pf.c | 8342 ++++++++--- .../ethernet/intel/i40e/i40e_virtchnl_pf.h | 113 +- drivers/net/ethernet/intel/i40e/kcompat.c | 2761 ++++ drivers/net/ethernet/intel/i40e/kcompat.h | 6838 +++++++++ .../ethernet/intel/i40e/kcompat_overflow.h | 319 + drivers/net/ethernet/intel/i40e/kcompat_vfd.c | 2550 ++++ drivers/net/ethernet/intel/i40e/kcompat_vfd.h | 141 + drivers/net/ethernet/intel/i40e/virtchnl.h | 949 ++ 47 files changed, 41086 insertions(+), 9734 deletions(-) create mode 100644 drivers/net/ethernet/intel/i40e/Module.supported create mode 100644 drivers/net/ethernet/intel/i40e/common.mk create mode 100644 drivers/net/ethernet/intel/i40e/i40e_ddp.c create mode 100644 drivers/net/ethernet/intel/i40e/i40e_ethtool_stats.h create mode 100644 drivers/net/ethernet/intel/i40e/i40e_filters.c create mode 100644 drivers/net/ethernet/intel/i40e/i40e_filters.h create mode 100644 drivers/net/ethernet/intel/i40e/i40e_helper.h create mode 100644 drivers/net/ethernet/intel/i40e/kcompat.c create mode 100644 drivers/net/ethernet/intel/i40e/kcompat.h create mode 100644 drivers/net/ethernet/intel/i40e/kcompat_overflow.h create mode 100644 drivers/net/ethernet/intel/i40e/kcompat_vfd.c create mode 100644 drivers/net/ethernet/intel/i40e/kcompat_vfd.h create mode 100644 drivers/net/ethernet/intel/i40e/virtchnl.h diff --git a/drivers/net/ethernet/intel/i40e/Makefile b/drivers/net/ethernet/intel/i40e/Makefile index 3da482c3d..81f5ab908 100644 --- a/drivers/net/ethernet/intel/i40e/Makefile +++ b/drivers/net/ethernet/intel/i40e/Makefile @@ -1,31 +1,10 @@ -################################################################################ -# -# Intel Ethernet Controller XL710 Family Linux Driver -# Copyright(c) 2013 - 2015 Intel Corporation. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms and conditions of the GNU General Public License, -# version 2, as published by the Free Software Foundation. -# -# This program is distributed in the hope 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. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# The full GNU General Public License is included in this distribution in -# the file called "COPYING". -# -# Contact Information: -# e1000-devel Mailing List -# Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 -# -################################################################################ +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2013 - 2020 Intel Corporation. +ifneq ($(KERNELRELEASE),) +# kbuild part of makefile # -# Makefile for the Intel(R) Ethernet Connection XL710 (i40e.ko) driver +# Makefile for the Intel(R) 40-10 Gigabit Ethernet Connection Network Driver # ccflags-y += -I$(src) @@ -33,18 +12,170 @@ subdir-ccflags-y += -I$(src) obj-$(CONFIG_I40E) += i40e.o -i40e-objs := i40e_main.o \ - i40e_ethtool.o \ - i40e_adminq.o \ - i40e_common.o \ - i40e_hmc.o \ - i40e_lan_hmc.o \ - i40e_nvm.o \ - i40e_debugfs.o \ - i40e_diag.o \ - i40e_txrx.o \ - i40e_ptp.o \ - i40e_client.o \ +i40e-objs := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ + i40e_hmc.o \ + i40e_lan_hmc.o \ + i40e_nvm.o \ + i40e_debugfs.o \ + i40e_diag.o \ + i40e_txrx.o \ + i40e_ptp.o \ + i40e_filters.o \ + i40e_ddp.o \ + i40e_client.o \ i40e_virtchnl_pf.o -i40e-$(CONFIG_I40E_DCB) += i40e_dcb.o i40e_dcb_nl.o +i40e-$(CONFIG_I40E_DCB) += i40e_dcb.o i40e_dcb_nl.o +i40e-y += kcompat.o +i40e-y += kcompat_vfd.o + +else # ifneq($(KERNELRELEASE),) +# normal makefile + +DRIVER := i40e + +# If the user just wants to print the help output, don't include common.mk or +# perform any other checks. This ensures that running "make help" will always +# work even if kernel-devel is not installed, or if the common.mk fails under +# any other error condition. +ifneq ($(MAKECMDGOALS),help) +include common.mk + +# i40e does not support building on kernels older than 2.6.32 +$(call minimum_kver_check,2,6,32) +endif + +# Command to update initramfs or display a warning message +ifeq (${cmd_initrd},) +define cmd_initramfs +@echo "Unable to update initramfs. You may need to do this manaully." +endef +else +define cmd_initramfs +@echo "Updating initramfs..." +-@$(call cmd_initrd) +endef +endif + +############### +# Build rules # +############### + +# Standard compilation, with regular output +default: + @+$(call kernelbuild,modules) + +# Noisy output, for extra debugging +noisy: + @+$(call kernelbuild,modules,V=1) + +# Silence any output generated +silent: + @+$(call kernelbuild,modules,>/dev/null) + +# Enable higher warning level +checkwarnings: clean + @+$(call kernelbuild,modules,W=1) + +# Run sparse static analyzer +sparse: clean + @+$(call kernelbuild,modules,C=2 CF="-D__CHECK_ENDIAN__ -Wbitwise -Wcontext") + +# Run coccicheck static analyzer +ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + +# Build manfiles +manfile: + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + +# Clean the module subdirectories +clean: + @+$(call kernelbuild,clean) + @-rm -rf *.${MANSECTION}.gz *.ko + +mandocs_install: manfile + @echo "Copying manpages..." + @install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + +# Install kernel module files. This target is called by the RPM specfile when +# generating binary RPMs, and is not expected to modify files outside of the +# build root. Thus, it must not update initramfs, or run depmod. +modules_install: default + @echo "Installing modules..." + @+$(call kernelbuild,modules_install) + +# After installing all the files, perform necessary work to ensure the system +# will use the new modules. This includes running depmod to update module +# dependencies and updating the initramfs image in case the module is loaded +# during early boot. +install: modules_install + $(call cmd_depmod) + $(call cmd_initramfs) + $(MAKE) mandocs_install + +# Target used by rpmbuild spec file +rpm: modules_install + $(MAKE) mandocs_install + +mandocs_uninstall: + if [ -e ${INSTALL_MOD_PATH}${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ] ; then \ + rm -f ${INSTALL_MOD_PATH}${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ; \ + fi; + +# Remove installed module files. This target is called by the RPM specfile when +# generating binary RPMs, and is not expected to modify files outside of the +# build root. Thus, it must not update the initramfs image or run depmod. +modules_uninstall: + rm -f ${INSTALL_MOD_PATH}/lib/modules/${KVER}/${INSTALL_MOD_DIR}/${DRIVER}.ko; + +# After uninstalling all the files, perform necessary work to restore the +# system back to using the default kernel modules. This includes running depmod +# to update module dependencies and updating the initramfs image. +uninstall: modules_uninstall mandocs_uninstall + $(call cmd_depmod) + $(call cmd_initramfs) + +######## +# Help # +######## +help: + @echo 'Build targets:' + @echo ' default - Build module(s) with standard verbosity' + @echo ' noisy - Build module(s) with V=1 verbosity -- very noisy' + @echo ' silent - Build module(s), squelching all output' + @echo '' + @echo 'Static Analysis:' + @echo ' checkwarnings - Clean, then build module(s) with W=1 warnings enabled' + @echo ' sparse - Clean, then check module(s) using sparse' + @echo ' ccc - Clean, then check module(s) using coccicheck' + @echo '' + @echo 'Cleaning targets:' + @echo ' clean - Clean files generated by kernel module build' + @echo '' + @echo 'Other targets:' + @echo ' manfile - Generate a gzipped manpage' + @echo ' modules_install - install the module(s) only' + @echo ' mandocs_install - install the manpage only' + @echo ' install - Build then install the module(s) and manpage, and update initramfs' + @echo ' modules_uninstall - uninstall the module(s) only' + @echo ' mandocs_uninstall - uninstall the manpage only' + @echo ' uninstall - Uninstall the module(s) and manpage, and update initramfs' + @echo ' help - Display this help message' + @echo '' + @echo 'Variables:' + @echo ' LINUX_VERSION - Debug tool to force kernel LINUX_VERSION_CODE. Use at your own risk.' + @echo ' W=N - Kernel variable for setting warning levels' + @echo ' V=N - Kernel variable for setting output verbosity' + @echo ' INSTALL_MOD_PATH - Add prefix for the module and manpage installation path' + @echo ' INSTALL_MOD_DIR - Use module directory other than updates/drivers/net/ethernet/intel/${DRIVER}' + @echo ' KSRC - Specifies the full path to the kernel tree to build against' + @echo ' Other variables may be available for tuning make process, see' + @echo ' Kernel Kbuild documentation for more information' + +.PHONY: default noisy clean manfile silent sparse ccc install uninstall help + +endif # ifneq($(KERNELRELEASE),) diff --git a/drivers/net/ethernet/intel/i40e/Module.supported b/drivers/net/ethernet/intel/i40e/Module.supported new file mode 100644 index 000000000..c0e31cf7f --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/Module.supported @@ -0,0 +1 @@ +i40e.ko external diff --git a/drivers/net/ethernet/intel/i40e/common.mk b/drivers/net/ethernet/intel/i40e/common.mk new file mode 100644 index 000000000..1b95715e7 --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/common.mk @@ -0,0 +1,305 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2013 - 2020 Intel Corporation. + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2015-2019 Intel Corporation +# +# common Makefile rules useful for out-of-tree Linux driver builds +# +# Usage: include common.mk +# +# After including, you probably want to add a minimum_kver_check call +# +# Required Variables: +# DRIVER +# -- Set to the lowercase driver name + +##################### +# Helpful functions # +##################### + +readlink = $(shell readlink -f ${1}) + +# helper functions for converting kernel version to version codes +get_kver = $(or $(word ${2},$(subst ., ,${1})),0) +get_kvercode = $(shell [ "${1}" -ge 0 -a "${1}" -le 255 2>/dev/null ] && \ + [ "${2}" -ge 0 -a "${2}" -le 255 2>/dev/null ] && \ + [ "${3}" -ge 0 -a "${3}" -le 255 2>/dev/null ] && \ + printf %d $$(( ( ${1} << 16 ) + ( ${2} << 8 ) + ( ${3} ) )) ) + +################ +# depmod Macro # +################ + +cmd_depmod = /sbin/depmod $(if ${SYSTEM_MAP_FILE},-e -F ${SYSTEM_MAP_FILE}) \ + $(if $(strip ${INSTALL_MOD_PATH}),-b ${INSTALL_MOD_PATH}) \ + -a ${KVER} + +################ +# dracut Macro # +################ + +cmd_initrd := $(shell \ + if which dracut > /dev/null 2>&1 ; then \ + echo "dracut --force"; \ + elif which update-initramfs > /dev/null 2>&1 ; then \ + echo "update-initramfs -u"; \ + fi ) + +##################### +# Environment tests # +##################### + +DRIVER_UPPERCASE := $(shell echo ${DRIVER} | tr "[:lower:]" "[:upper:]") + +ifeq (,${BUILD_KERNEL}) +BUILD_KERNEL=$(shell uname -r) +endif + +# Kernel Search Path +# All the places we look for kernel source +KSP := /lib/modules/${BUILD_KERNEL}/source \ + /lib/modules/${BUILD_KERNEL}/build \ + /usr/src/linux-${BUILD_KERNEL} \ + /usr/src/linux-$(${BUILD_KERNEL} | sed 's/-.*//') \ + /usr/src/kernel-headers-${BUILD_KERNEL} \ + /usr/src/kernel-source-${BUILD_KERNEL} \ + /usr/src/linux-$(${BUILD_KERNEL} | sed 's/\([0-9]*\.[0-9]*\)\..*/\1/') \ + /usr/src/linux \ + /usr/src/kernels/${BUILD_KERNEL} \ + /usr/src/kernels + +# prune the list down to only values that exist and have an include/linux +# sub-directory. We can't use include/config because some older kernels don't +# have this. +test_dir = $(shell [ -e ${dir}/include/linux ] && echo ${dir}) +KSP := $(foreach dir, ${KSP}, ${test_dir}) + +# we will use this first valid entry in the search path +ifeq (,${KSRC}) + KSRC := $(firstword ${KSP}) +endif + +ifeq (,${KSRC}) + $(warning *** Kernel header files not in any of the expected locations.) + $(warning *** Install the appropriate kernel development package, e.g.) + $(error kernel-devel, for building kernel modules and try again) +else +ifeq (/lib/modules/${BUILD_KERNEL}/source, ${KSRC}) + KOBJ := /lib/modules/${BUILD_KERNEL}/build +else + KOBJ := ${KSRC} +endif +endif + +# Version file Search Path +VSP := ${KOBJ}/include/generated/utsrelease.h \ + ${KOBJ}/include/linux/utsrelease.h \ + ${KOBJ}/include/linux/version.h \ + ${KOBJ}/include/generated/uapi/linux/version.h \ + /boot/vmlinuz.version.h + +# Config file Search Path +CSP := ${KOBJ}/include/generated/autoconf.h \ + ${KOBJ}/include/linux/autoconf.h \ + /boot/vmlinuz.autoconf.h + +# System.map Search Path (for depmod) +MSP := ${KSRC}/System.map \ + /boot/System.map-${BUILD_KERNEL} + +# prune the lists down to only files that exist +test_file = $(shell [ -f ${file} ] && echo ${file}) +VSP := $(foreach file, ${VSP}, ${test_file}) +CSP := $(foreach file, ${CSP}, ${test_file}) +MSP := $(foreach file, ${MSP}, ${test_file}) + + +# and use the first valid entry in the Search Paths +ifeq (,${VERSION_FILE}) + VERSION_FILE := $(firstword ${VSP}) +endif + +ifeq (,${CONFIG_FILE}) + CONFIG_FILE := $(firstword ${CSP}) +endif + +ifeq (,${SYSTEM_MAP_FILE}) + SYSTEM_MAP_FILE := $(firstword ${MSP}) +endif + +ifeq (,$(wildcard ${VERSION_FILE})) + $(error Linux kernel source not configured - missing version header file) +endif + +ifeq (,$(wildcard ${CONFIG_FILE})) + $(error Linux kernel source not configured - missing autoconf.h) +endif + +ifeq (,$(wildcard ${SYSTEM_MAP_FILE})) + $(warning Missing System.map file - depmod will not check for missing symbols) +endif + +ifneq ($(words $(subst :, ,$(CURDIR))), 1) + $(error Sources directory '$(CURDIR)' cannot contain spaces nor colons. Rename directory or move sources to another path) +endif + +####################### +# Linux Version Setup # +####################### + +# The following command line parameter is intended for development of KCOMPAT +# against upstream kernels such as net-next which have broken or non-updated +# version codes in their Makefile. They are intended for debugging and +# development purpose only so that we can easily test new KCOMPAT early. If you +# don't know what this means, you do not need to set this flag. There is no +# arcane magic here. + +# Convert LINUX_VERSION into LINUX_VERSION_CODE +ifneq (${LINUX_VERSION},) + LINUX_VERSION_CODE=$(call get_kvercode,$(call get_kver,${LINUX_VERSION},1),$(call get_kver,${LINUX_VERSION},2),$(call get_kver,${LINUX_VERSION},3)) +endif + +# Honor LINUX_VERSION_CODE +ifneq (${LINUX_VERSION_CODE},) + $(warning Forcing target kernel to build with LINUX_VERSION_CODE of ${LINUX_VERSION_CODE}$(if ${LINUX_VERSION}, from LINUX_VERSION=${LINUX_VERSION}). Do this at your own risk.) + KVER_CODE := ${LINUX_VERSION_CODE} + EXTRA_CFLAGS += -DLINUX_VERSION_CODE=${LINUX_VERSION_CODE} +endif + +# Determine SLE_LOCALVERSION_CODE for SuSE SLE >= 11 (needed by kcompat) +# This assumes SuSE will continue setting CONFIG_LOCALVERSION to the string +# appended to the stable kernel version on which their kernel is based with +# additional versioning information (up to 3 numbers), a possible abbreviated +# git SHA1 commit id and a kernel type, e.g. CONFIG_LOCALVERSION=-1.2.3-default +# or CONFIG_LOCALVERSION=-999.gdeadbee-default +ifeq (1,$(shell ${CC} -E -dM ${CONFIG_FILE} 2> /dev/null |\ + grep -m 1 CONFIG_SUSE_KERNEL | awk '{ print $$3 }')) + +ifneq (10,$(shell ${CC} -E -dM ${CONFIG_FILE} 2> /dev/null |\ + grep -m 1 CONFIG_SLE_VERSION | awk '{ print $$3 }')) + + LOCALVERSION := $(shell ${CC} -E -dM ${CONFIG_FILE} 2> /dev/null |\ + grep -m 1 CONFIG_LOCALVERSION | awk '{ print $$3 }' |\ + cut -d'-' -f2 | sed 's/\.g[[:xdigit:]]\{7\}//') + LOCALVER_A := $(shell echo ${LOCALVERSION} | cut -d'.' -f1) + LOCALVER_B := $(shell echo ${LOCALVERSION} | cut -s -d'.' -f2) + LOCALVER_C := $(shell echo ${LOCALVERSION} | cut -s -d'.' -f3) + SLE_LOCALVERSION_CODE := $(shell expr ${LOCALVER_A} \* 65536 + \ + 0${LOCALVER_B} \* 256 + 0${LOCALVER_C}) + EXTRA_CFLAGS += -DSLE_LOCALVERSION_CODE=${SLE_LOCALVERSION_CODE} +endif +endif + +EXTRA_CFLAGS += ${CFLAGS_EXTRA} + +# get the kernel version - we use this to find the correct install path +KVER := $(shell ${CC} ${EXTRA_CFLAGS} -E -dM ${VERSION_FILE} | grep UTS_RELEASE | \ + awk '{ print $$3 }' | sed 's/\"//g') + +# assume source symlink is the same as build, otherwise adjust KOBJ +ifneq (,$(wildcard /lib/modules/${KVER}/build)) + ifneq (${KSRC},$(call readlink,/lib/modules/${KVER}/build)) + KOBJ=/lib/modules/${KVER}/build + endif +endif + +ifeq (${KVER_CODE},) + KVER_CODE := $(shell ${CC} ${EXTRA_CFLAGS} -E -dM ${VSP} 2> /dev/null |\ + grep -m 1 LINUX_VERSION_CODE | awk '{ print $$3 }' | sed 's/\"//g') +endif + +# minimum_kver_check +# +# helper function to provide uniform output for different drivers to abort the +# build based on kernel version check. Usage: "$(call minimum_kver_check,2,6,XX)". +define _minimum_kver_check +ifeq (0,$(shell [ ${KVER_CODE} -lt $(call get_kvercode,${1},${2},${3}) ]; echo "$$?")) + $$(warning *** Aborting the build.) + $$(error This driver is not supported on kernel versions older than ${1}.${2}.${3}) +endif +endef +minimum_kver_check = $(eval $(call _minimum_kver_check,${1},${2},${3})) + +################ +# Manual Pages # +################ + +MANSECTION = 7 + +ifeq (,${MANDIR}) + # find the best place to install the man page + MANPATH := $(shell (manpath 2>/dev/null || echo $MANPATH) | sed 's/:/ /g') + ifneq (,${MANPATH}) + # test based on inclusion in MANPATH + test_dir = $(findstring ${dir}, ${MANPATH}) + else + # no MANPATH, test based on directory existence + test_dir = $(shell [ -e ${dir} ] && echo ${dir}) + endif + # our preferred install path + # should /usr/local/man be in here ? + MANDIR := /usr/share/man /usr/man + MANDIR := $(foreach dir, ${MANDIR}, ${test_dir}) + MANDIR := $(firstword ${MANDIR}) +endif +ifeq (,${MANDIR}) + # fallback to /usr/man + MANDIR := /usr/man +endif + +#################### +# CCFLAGS variable # +#################### + +# set correct CCFLAGS variable for kernels older than 2.6.24 +ifeq (0,$(shell [ ${KVER_CODE} -lt $(call get_kvercode,2,6,24) ]; echo $$?)) +CCFLAGS_VAR := EXTRA_CFLAGS +else +CCFLAGS_VAR := ccflags-y +endif + +################# +# KBUILD_OUTPUT # +################# + +# Only set KBUILD_OUTPUT if the real paths of KOBJ and KSRC differ +ifneq ($(call readlink,${KSRC}),$(call readlink,${KOBJ})) +export KBUILD_OUTPUT ?= ${KOBJ} +endif + +############################ +# Module Install Directory # +############################ + +# Default to using updates/drivers/net/ethernet/intel/ path, since depmod since +# v3.1 defaults to checking updates folder first, and only checking kernels/ +# and extra afterwards. We use updates instead of kernel/* due to desire to +# prevent over-writing built-in modules files. +export INSTALL_MOD_DIR ?= updates/drivers/net/ethernet/intel/${DRIVER} + +###################### +# Kernel Build Macro # +###################### + +# kernel build function +# ${1} is the kernel build target +# ${2} may contain any extra rules to pass directly to the sub-make process +# +# This function is expected to be executed by +# @+$(call kernelbuild,,) +# from within a Makefile recipe. +# +# The following variables are expected to be defined for its use: +# GCC_I_SYS -- if set it will enable use of gcc-i-sys.sh wrapper to use -isystem +# CCFLAGS_VAR -- the CCFLAGS variable to set extra CFLAGS +# EXTRA_CFLAGS -- a set of extra CFLAGS to pass into the ccflags-y variable +# KSRC -- the location of the kernel source tree to build against +# DRIVER_UPPERCASE -- the uppercase name of the kernel module, set from DRIVER +# +kernelbuild = ${MAKE} $(if ${GCC_I_SYS},CC="${GCC_I_SYS}") \ + ${CCFLAGS_VAR}="${EXTRA_CFLAGS}" \ + -C "${KSRC}" \ + CONFIG_${DRIVER_UPPERCASE}=m \ + M="${CURDIR}" \ + ${2} ${1} diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index d0c1bf544..776bb99b6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2017 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_H_ #define _I40E_H_ @@ -36,10 +13,9 @@ #include #include #include -#include #include #include -#include +#include #include #include #include @@ -47,36 +23,61 @@ #include #include #include +#include #include +#ifdef SIOCETHTOOL #include +#endif #include #include +#include "kcompat.h" +#ifdef HAVE_IOMMU_PRESENT +#include +#endif +#ifdef HAVE_SCTP +#include +#endif +#ifdef HAVE_PTP_1588_CLOCK #include #include #include +#endif /* HAVE_PTP_1588_CLOCK */ +#ifdef __TC_MQPRIO_MODE_MAX +#include +#endif #include "i40e_type.h" #include "i40e_prototype.h" #include "i40e_client.h" -#include +#include "virtchnl.h" #include "i40e_virtchnl_pf.h" #include "i40e_txrx.h" #include "i40e_dcb.h" +#ifdef HAVE_XDP_SUPPORT +#include +#endif + /* Useful i40e defaults */ #define I40E_MAX_VEB 16 #define I40E_MAX_NUM_DESCRIPTORS 4096 #define I40E_MAX_CSR_SPACE (4 * 1024 * 1024 - 64 * 1024) #define I40E_DEFAULT_NUM_DESCRIPTORS 512 +#define L4_MODE_UDP 0 +#define L4_MODE_TCP 1 +#define L4_MODE_BOTH 2 +#define L4_MODE_DISABLED -1 +bool i40e_is_l4mode_enabled(void); #define I40E_REQ_DESCRIPTOR_MULTIPLE 32 #define I40E_MIN_NUM_DESCRIPTORS 64 #define I40E_MIN_MSIX 2 #define I40E_DEFAULT_NUM_VMDQ_VSI 8 /* max 256 VSIs */ -#define I40E_MIN_VSI_ALLOC 83 /* LAN, ATR, FCOE, 64 VF */ +#define I40E_MIN_VSI_ALLOC 83 /* LAN, ATR, FCOE, 64 VF, 16 VMDQ */ /* max 16 qps */ #define i40e_default_queues_per_vmdq(pf) \ (((pf)->hw_features & I40E_HW_RSS_AQ_CAPABLE) ? 4 : 1) #define I40E_DEFAULT_QUEUES_PER_VF 4 +#define I40E_MAX_VF_QUEUES 16 #define I40E_DEFAULT_QUEUES_PER_TC 1 /* should be a power of 2 */ #define i40e_pf_get_max_q_per_tc(pf) \ (((pf)->hw_features & I40E_HW_128_QP_RSS_CAPABLE) ? 128 : 64) @@ -85,6 +86,10 @@ #define I40E_MAX_AQ_BUF_SIZE 4096 #define I40E_AQ_LEN 256 #define I40E_AQ_WORK_LIMIT 66 /* max number of VFs + a little */ +/* + * If I40E_MAX_USER_PRIORITY is updated please also update + * I40E_CLIENT_MAX_USER_PRIORITY in i40e_client.h and i40evf_client.h + */ #define I40E_MAX_USER_PRIORITY 8 #define I40E_DEFAULT_TRAFFIC_CLASS BIT(0) #define I40E_DEFAULT_MSG_ENABLE 4 @@ -114,7 +119,7 @@ #define I40E_CURRENT_NVM_VERSION_LO 0x40 #define I40E_RX_DESC(R, i) \ - (&(((union i40e_32byte_rx_desc *)((R)->desc))[i])) + (&(((union i40e_rx_desc *)((R)->desc))[i])) #define I40E_TX_DESC(R, i) \ (&(((struct i40e_tx_desc *)((R)->desc))[i])) #define I40E_TX_CTXTDESC(R, i) \ @@ -125,6 +130,10 @@ /* default to trying for four seconds */ #define I40E_TRY_LINK_TIMEOUT (4 * HZ) +/* BW rate limiting */ +#define I40E_BW_CREDIT_DIVISOR 50 /* 50Mbps per BW credit */ +#define I40E_MAX_BW_INACTIVE_ACCUM 4 /* accumulate 4 credits max */ + /* driver state flags */ enum i40e_state_t { __I40E_TESTING, @@ -136,6 +145,8 @@ enum i40e_state_t { __I40E_MDD_EVENT_PENDING, __I40E_VFLR_EVENT_PENDING, __I40E_RESET_RECOVERY_PENDING, + __I40E_TIMEOUT_RECOVERY_PENDING, + __I40E_MISC_IRQ_REQUESTED, __I40E_RESET_INTR_RECEIVED, __I40E_REINIT_REQUESTED, __I40E_PF_RESET_REQUESTED, @@ -144,17 +155,31 @@ enum i40e_state_t { __I40E_EMP_RESET_REQUESTED, __I40E_EMP_RESET_INTR_RECEIVED, __I40E_SUSPENDED, - __I40E_PTP_TX_IN_PROGRESS, __I40E_BAD_EEPROM, + __I40E_DEBUG_MODE, __I40E_DOWN_REQUESTED, __I40E_FD_FLUSH_REQUESTED, + __I40E_FD_ATR_AUTO_DISABLED, + __I40E_FD_SB_AUTO_DISABLED, __I40E_RESET_FAILED, __I40E_PORT_SUSPENDED, + __I40E_PTP_TX_IN_PROGRESS, __I40E_VF_DISABLE, + __I40E_RECOVERY_MODE, + __I40E_MACVLAN_SYNC_PENDING, + __I40E_UDP_FILTER_SYNC_PENDING, + __I40E_TEMP_LINK_POLLING, + __I40E_CLIENT_SERVICE_REQUESTED, + __I40E_CLIENT_L2_CHANGE, + __I40E_CLIENT_RESET, + __I40E_VIRTCHNL_OP_PENDING, + __I40E_VFS_RELEASING, /* This must be last as it determines the size of the BITMAP */ __I40E_STATE_SIZE__, }; +#define I40E_PF_RESET_FLAG BIT_ULL(__I40E_PF_RESET_REQUESTED) + /* VSI state flags */ enum i40e_vsi_state_t { __I40E_VSI_DOWN, @@ -163,6 +188,7 @@ enum i40e_vsi_state_t { __I40E_VSI_OVERFLOW_PROMISC, __I40E_VSI_REINIT_REQUESTED, __I40E_VSI_DOWN_REQUESTED, + __I40E_VSI_RELEASING, /* This must be last as it determines the size of the BITMAP */ __I40E_VSI_STATE_SIZE__, }; @@ -183,6 +209,10 @@ struct i40e_lump_tracking { #define I40E_DEFAULT_ATR_SAMPLE_RATE 20 #define I40E_FDIR_MAX_RAW_PACKET_SIZE 512 +#define I40E_TCPIP_DUMMY_PACKET_LEN 54 +#define I40E_SCTPIP_DUMMY_PACKET_LEN 46 +#define I40E_UDPIP_DUMMY_PACKET_LEN 42 +#define I40E_IP_DUMMY_PACKET_LEN 34 #define I40E_FDIR_BUFFER_FULL_MARGIN 10 #define I40E_FDIR_BUFFER_HEAD_ROOM 32 #define I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR (I40E_FDIR_BUFFER_HEAD_ROOM * 4) @@ -205,10 +235,20 @@ enum i40e_fd_stat_idx { #define I40E_FD_ATR_TUNNEL_STAT_IDX(pf_id) \ (I40E_FD_STAT_PF_IDX(pf_id) + I40E_FD_STAT_ATR_TUNNEL) +/* get PTP pins for ioctl */ +#define SIOCGPINS (SIOCDEVPRIVATE + 0) +/* set PTP pins for ioctl */ +#define SIOCSPINS (SIOCDEVPRIVATE + 1) + /* The following structure contains the data parsed from the user-defined * field of the ethtool_rx_flow_spec structure. */ struct i40e_rx_flow_userdef { + bool cloud_filter; + bool tenant_id_valid; + u32 tenant_id; + bool tunnel_type_valid; + u8 tunnel_type; bool flex_filter; u16 flex_word; u16 flex_offset; @@ -216,7 +256,7 @@ struct i40e_rx_flow_userdef { struct i40e_fdir_filter { struct hlist_node fdir_node; - /* filter ipnut set */ + /* filter input set */ u8 flow_type; u8 ip4_proto; /* TX packet view of src and dst */ @@ -233,7 +273,6 @@ struct i40e_fdir_filter { /* filter control */ u16 q_index; - u8 flex_off; u8 pctype; u16 dest_vsi; u8 dest_ctl; @@ -242,6 +281,65 @@ struct i40e_fdir_filter { u32 fd_id; }; +#define I40E_CLOUD_FIELD_OMAC BIT(0) +#define I40E_CLOUD_FIELD_IMAC BIT(1) +#define I40E_CLOUD_FIELD_IVLAN BIT(2) +#define I40E_CLOUD_FIELD_TEN_ID BIT(3) +#define I40E_CLOUD_FIELD_IIP BIT(4) + +#define I40E_CLOUD_FILTER_FLAGS_OMAC I40E_CLOUD_FIELD_OMAC +#define I40E_CLOUD_FILTER_FLAGS_IMAC I40E_CLOUD_FIELD_IMAC +#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN (I40E_CLOUD_FIELD_IMAC | \ + I40E_CLOUD_FIELD_IVLAN) +#define I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID (I40E_CLOUD_FIELD_IMAC | \ + I40E_CLOUD_FIELD_TEN_ID) +#define I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC (I40E_CLOUD_FIELD_OMAC | \ + I40E_CLOUD_FIELD_IMAC | \ + I40E_CLOUD_FIELD_TEN_ID) +#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID (I40E_CLOUD_FIELD_IMAC | \ + I40E_CLOUD_FIELD_IVLAN | \ + I40E_CLOUD_FIELD_TEN_ID) +#define I40E_CLOUD_FILTER_FLAGS_IIP I40E_CLOUD_FIELD_IIP + +struct i40e_cloud_filter { + struct hlist_node cloud_node; + unsigned long cookie; + /* cloud filter input set follows */ + u8 outer_mac[ETH_ALEN]; + u8 inner_mac[ETH_ALEN]; + __be16 inner_vlan; + __be32 inner_ip[4]; + u16 queue_id; + u32 id; + /* cloud filter input set follows */ + u8 dst_mac[ETH_ALEN]; + u8 src_mac[ETH_ALEN]; + __be16 vlan_id; + u16 seid; /* filter control */ + __be16 dst_port; + __be16 src_port; + u32 tenant_id; + union { + struct { + struct in_addr dst_ip; + struct in_addr src_ip; + } v4; + struct { + struct in6_addr dst_ip6; + struct in6_addr src_ip6; + } v6; + } ip; +#define dst_ipv6 ip.v6.dst_ip6.s6_addr32 +#define src_ipv6 ip.v6.src_ip6.s6_addr32 +#define dst_ipv4 ip.v4.dst_ip.s_addr +#define src_ipv4 ip.v4.src_ip.s_addr + u16 n_proto; /* Ethernet Protocol */ + u8 ip_proto; /* IPPROTO value */ + u8 flags; +#define I40E_CLOUD_TNL_TYPE_NONE 0xff + u8 tunnel_type; +}; + #define I40E_ETH_P_LLDP 0x88cc #define I40E_DCB_PRIO_TYPE_STRICT 0 @@ -261,10 +359,34 @@ struct i40e_tc_configuration { struct i40e_tc_info tc_info[I40E_MAX_TRAFFIC_CLASS]; }; +#define I40E_UDP_PORT_INDEX_UNUSED 255 struct i40e_udp_port_config { /* AdminQ command interface expects port number in Host byte order */ u16 port; u8 type; + u8 filter_index; +}; + +#define I40_DDP_FLASH_REGION 100 +#define I40E_PROFILE_INFO_SIZE 48 +#define I40E_MAX_PROFILE_NUM 16 +#define I40E_PROFILE_LIST_SIZE \ + (I40E_PROFILE_INFO_SIZE * I40E_MAX_PROFILE_NUM + 4) +#define I40E_DDP_PROFILE_PATH "intel/i40e/ddp/" + +int i40e_ddp_load(struct net_device *netdev, const u8 *data, size_t size, + bool is_add); +int i40e_ddp_flash(struct net_device *netdev, struct ethtool_flash *flash); + +struct i40e_ddp_profile_list { + u32 p_count; + struct i40e_profile_info p_info[0]; +}; + +struct i40e_ddp_old_profile_list { + struct list_head list; + size_t old_ddp_size; + u8 old_ddp_buf[0]; }; /* macros related to FLX_PIT */ @@ -330,12 +452,49 @@ struct i40e_udp_port_config { I40E_FLEX_54_MASK | I40E_FLEX_55_MASK | \ I40E_FLEX_56_MASK | I40E_FLEX_57_MASK) +#define I40E_QINT_TQCTL_VAL(qp, vector, nextq_type) \ + (I40E_QINT_TQCTL_CAUSE_ENA_MASK | \ + (I40E_TX_ITR << I40E_QINT_TQCTL_ITR_INDX_SHIFT) | \ + ((vector) << I40E_QINT_TQCTL_MSIX_INDX_SHIFT) | \ + ((qp) << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT) | \ + (I40E_QUEUE_TYPE_##nextq_type << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT)) + +#define I40E_QINT_RQCTL_VAL(qp, vector, nextq_type) \ + (I40E_QINT_RQCTL_CAUSE_ENA_MASK | \ + (I40E_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT) | \ + ((vector) << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) | \ + ((qp) << I40E_QINT_RQCTL_NEXTQ_INDX_SHIFT) | \ + (I40E_QUEUE_TYPE_##nextq_type << I40E_QINT_RQCTL_NEXTQ_TYPE_SHIFT)) + struct i40e_flex_pit { struct list_head list; u16 src_offset; u8 pit_index; }; +struct i40e_channel { + struct list_head list; + bool initialized; + u8 type; + u16 vsi_number; /* Assigned VSI number from AQ 'Add VSI' response */ + u16 stat_counter_idx; + u16 base_queue; + u16 num_queue_pairs; /* Requested by user */ + u16 seid; + + u8 enabled_tc; + struct i40e_aqc_vsi_properties_data info; + + u64 max_tx_rate; + + /* track this channel belongs to which VSI */ + struct i40e_vsi *parent_vsi; +}; + +#ifdef HAVE_PTP_1588_CLOCK +struct i40e_ptp_pins_settings; +#endif /* HAVE_PTP_1588_CLOCK */ + /* struct that defines the Ethernet device */ struct i40e_pf { struct pci_dev *pdev; @@ -390,6 +549,9 @@ struct i40e_pf { struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS]; u16 pending_udp_bitmap; + struct hlist_head cloud_filter_list; + u16 num_cloud_filters; + enum i40e_interrupt_policy int_policy; u16 rx_itr_default; u16 tx_itr_default; @@ -401,55 +563,93 @@ struct i40e_pf { struct timer_list service_timer; struct work_struct service_task; - u64 hw_features; -#define I40E_HW_RSS_AQ_CAPABLE BIT_ULL(0) -#define I40E_HW_128_QP_RSS_CAPABLE BIT_ULL(1) -#define I40E_HW_ATR_EVICT_CAPABLE BIT_ULL(2) -#define I40E_HW_WB_ON_ITR_CAPABLE BIT_ULL(3) -#define I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT_ULL(4) -#define I40E_HW_NO_PCI_LINK_CHECK BIT_ULL(5) -#define I40E_HW_100M_SGMII_CAPABLE BIT_ULL(6) -#define I40E_HW_NO_DCB_SUPPORT BIT_ULL(7) -#define I40E_HW_USE_SET_LLDP_MIB BIT_ULL(8) -#define I40E_HW_GENEVE_OFFLOAD_CAPABLE BIT_ULL(9) -#define I40E_HW_PTP_L4_CAPABLE BIT_ULL(10) -#define I40E_HW_WOL_MC_MAGIC_PKT_WAKE BIT_ULL(11) -#define I40E_HW_MPLS_HDR_OFFLOAD_CAPABLE BIT_ULL(12) -#define I40E_HW_HAVE_CRT_RETIMER BIT_ULL(13) -#define I40E_HW_OUTER_UDP_CSUM_CAPABLE BIT_ULL(14) -#define I40E_HW_PHY_CONTROLS_LEDS BIT_ULL(15) -#define I40E_HW_STOP_FW_LLDP BIT_ULL(16) -#define I40E_HW_PORT_ID_VALID BIT_ULL(17) -#define I40E_HW_RESTART_AUTONEG BIT_ULL(18) + u32 hw_features; +#define I40E_HW_RSS_AQ_CAPABLE BIT(0) +#define I40E_HW_128_QP_RSS_CAPABLE BIT(1) +#define I40E_HW_ATR_EVICT_CAPABLE BIT(2) +#define I40E_HW_WB_ON_ITR_CAPABLE BIT(3) +#define I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT(4) +#define I40E_HW_NO_PCI_LINK_CHECK BIT(5) +#define I40E_HW_100M_SGMII_CAPABLE BIT(6) +#define I40E_HW_NO_DCB_SUPPORT BIT(7) +#define I40E_HW_USE_SET_LLDP_MIB BIT(8) +#define I40E_HW_GENEVE_OFFLOAD_CAPABLE BIT(9) +#define I40E_HW_PTP_L4_CAPABLE BIT(10) +#define I40E_HW_WOL_MC_MAGIC_PKT_WAKE BIT(11) +#define I40E_HW_MPLS_HDR_OFFLOAD_CAPABLE BIT(12) +#define I40E_HW_HAVE_CRT_RETIMER BIT(13) +#define I40E_HW_OUTER_UDP_CSUM_CAPABLE BIT(14) +#define I40E_HW_PHY_CONTROLS_LEDS BIT(15) +#define I40E_HW_STOP_FW_LLDP BIT(16) +#define I40E_HW_PORT_ID_VALID BIT(17) +#define I40E_HW_RESTART_AUTONEG BIT(18) u64 flags; -#define I40E_FLAG_RX_CSUM_ENABLED BIT_ULL(1) -#define I40E_FLAG_MSI_ENABLED BIT_ULL(2) -#define I40E_FLAG_MSIX_ENABLED BIT_ULL(3) -#define I40E_FLAG_HW_ATR_EVICT_ENABLED BIT_ULL(4) -#define I40E_FLAG_RSS_ENABLED BIT_ULL(6) -#define I40E_FLAG_VMDQ_ENABLED BIT_ULL(7) -#define I40E_FLAG_IWARP_ENABLED BIT_ULL(10) -#define I40E_FLAG_FILTER_SYNC BIT_ULL(15) -#define I40E_FLAG_SERVICE_CLIENT_REQUESTED BIT_ULL(16) -#define I40E_FLAG_SRIOV_ENABLED BIT_ULL(19) -#define I40E_FLAG_DCB_ENABLED BIT_ULL(20) -#define I40E_FLAG_FD_SB_ENABLED BIT_ULL(21) -#define I40E_FLAG_FD_ATR_ENABLED BIT_ULL(22) -#define I40E_FLAG_FD_SB_AUTO_DISABLED BIT_ULL(23) -#define I40E_FLAG_FD_ATR_AUTO_DISABLED BIT_ULL(24) -#define I40E_FLAG_PTP BIT_ULL(25) -#define I40E_FLAG_MFP_ENABLED BIT_ULL(26) -#define I40E_FLAG_UDP_FILTER_SYNC BIT_ULL(27) -#define I40E_FLAG_DCB_CAPABLE BIT_ULL(29) -#define I40E_FLAG_VEB_STATS_ENABLED BIT_ULL(37) -#define I40E_FLAG_LINK_POLLING_ENABLED BIT_ULL(39) -#define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40) -#define I40E_FLAG_TRUE_PROMISC_SUPPORT BIT_ULL(51) -#define I40E_FLAG_CLIENT_RESET BIT_ULL(54) -#define I40E_FLAG_TEMP_LINK_POLLING BIT_ULL(55) -#define I40E_FLAG_CLIENT_L2_CHANGE BIT_ULL(56) -#define I40E_FLAG_LEGACY_RX BIT_ULL(58) +#define I40E_FLAG_RX_CSUM_ENABLED BIT(0) +#define I40E_FLAG_MSI_ENABLED BIT(1) +#define I40E_FLAG_MSIX_ENABLED BIT(2) +#define I40E_FLAG_RSS_ENABLED BIT(3) +#define I40E_FLAG_VMDQ_ENABLED BIT(4) +#define I40E_FLAG_SRIOV_ENABLED BIT(5) +#define I40E_FLAG_DCB_CAPABLE BIT(6) +#define I40E_FLAG_DCB_ENABLED BIT(7) +#define I40E_FLAG_FD_SB_ENABLED BIT(8) +#define I40E_FLAG_FD_ATR_ENABLED BIT(9) +#define I40E_FLAG_MFP_ENABLED BIT(10) +#define I40E_FLAG_HW_ATR_EVICT_ENABLED BIT(11) +#define I40E_FLAG_VEB_MODE_ENABLED BIT(12) +#define I40E_FLAG_VEB_STATS_ENABLED BIT(13) +#define I40E_FLAG_LINK_POLLING_ENABLED BIT(14) +#define I40E_FLAG_TRUE_PROMISC_SUPPORT BIT(15) +#define I40E_FLAG_LEGACY_RX BIT(16) +#ifdef HAVE_PTP_1588_CLOCK +#define I40E_FLAG_PTP BIT(17) +#endif /* HAVE_PTP_1588_CLOCK */ +#define I40E_FLAG_IWARP_ENABLED BIT(18) +#define I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED BIT(19) +#define I40E_FLAG_SOURCE_PRUNING_DISABLED BIT(20) +#define I40E_FLAG_TC_MQPRIO BIT(21) +#define I40E_FLAG_FD_SB_INACTIVE BIT(22) +#define I40E_FLAG_FD_SB_TO_CLOUD_FILTER BIT(23) +#define I40E_FLAG_DISABLE_FW_LLDP BIT(24) +#define I40E_FLAG_RS_FEC BIT(25) +#define I40E_FLAG_BASE_R_FEC BIT(26) +#define I40E_FLAG_TOTAL_PORT_SHUTDOWN BIT(27) + +/* GPIO defines used by PTP */ +#define I40E_SDP3_2 18 +#define I40E_SDP3_3 19 +#define I40E_GPIO_4 20 +#define I40E_LED2_0 26 +#define I40E_LED2_1 27 +#define I40E_LED3_0 28 +#define I40E_LED3_1 29 +#define I40E_GPIO_SET_HIGH BIT(5) +#define I40E_GPIO_SET_LOW 0 +#define I40E_DRIVE_SDP_ON BIT(6) +#define I40E_DRIVE_SDP_OFF 0 +#define I40E_GPIO_PRT_NUM_0 0 +#define I40E_GPIO_PRT_NUM_1 1 +#define I40E_GPIO_PRT_NUM_2 2 +#define I40E_GPIO_PRT_NUM_3 3 +#define I40E_GPIO_RESERVED_2 BIT(2) +#define I40E_GPIO_PIN_DIR_OUT BIT(4) +#define I40E_GPIO_PIN_DIR_IN 0 +#define I40E_GPIO_TRI_CTL_OFF BIT(5) +#define I40E_GPIO_CTL_OUT_HIGH BIT(6) +#define I40E_GPIO_TIMESYNC_0 3 << 7 +#define I40E_GPIO_TIMESYNC_1 4 << 7 +#define I40E_GPIO_PHY_PIN_NA_ME_NO_SDP 0x3F << 20 +#define I40E_PORT_0_TIMESYNC_1 0x3F00184 +#define I40E_PORT_1_TIMESYNC_1 0x3F00185 +#define I40E_PORT_0_OUT_HIGH_TIMESYNC_0 0x3F00274 +#define I40E_PORT_1_OUT_HIGH_TIMESYNC_1 0x3F00275 +#define I40E_PTP_HALF_SECOND 500000000LL /* nano seconds */ +#define I40E_PRTTSYN_CTL0_FFFB_MASK 0xFFFFFFFB +#define I40E_PTP_2_SEC_DELAY 2 + + /* flag to enable/disable vf base mode support */ + bool vf_base_mode_only; struct i40e_client_instance *cinst; bool stat_offsets_loaded; @@ -507,22 +707,50 @@ struct i40e_pf { u16 dcbx_cap; struct i40e_filter_control_settings filter_settings; +#ifdef HAVE_PTP_1588_CLOCK struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_caps; struct sk_buff *ptp_tx_skb; unsigned long ptp_tx_start; struct hwtstamp_config tstamp_config; + struct timespec64 ptp_prev_hw_time; + struct work_struct ptp_pps_work; + struct work_struct ptp_extts0_work; + struct work_struct ptp_extts1_work; + ktime_t ptp_reset_start; struct mutex tmreg_lock; /* Used to protect the SYSTIME registers. */ - u64 ptp_base_adj; + u32 ptp_adj_mult; u32 tx_hwtstamp_timeouts; u32 tx_hwtstamp_skipped; u32 rx_hwtstamp_cleared; u32 latch_event_flags; + u64 ptp_pps_start; + u32 pps_delay; spinlock_t ptp_rx_lock; /* Used to protect Rx timestamp registers. */ + struct ptp_pin_desc ptp_pin[3]; unsigned long latch_events[4]; bool ptp_tx; bool ptp_rx; + struct i40e_ptp_pins_settings *ptp_pins; + struct kobject *ptp_kobj; +#endif /* HAVE_PTP_1588_CLOCK */ +#ifdef I40E_ADD_PROBES + u64 tcp_segs; + u64 tx_tcp_cso; + u64 tx_udp_cso; + u64 tx_sctp_cso; + u64 tx_ip4_cso; + u64 rx_tcp_cso; + u64 rx_udp_cso; + u64 rx_sctp_cso; + u64 rx_ip4_cso; + u64 hw_csum_rx_outer; + u64 rx_tcp_cso_err; + u64 rx_udp_cso_err; + u64 rx_sctp_cso_err; + u64 rx_ip4_cso_err; +#endif u16 rss_table_size; /* HW RSS table size */ u32 max_bw; u32 min_bw; @@ -530,6 +758,18 @@ struct i40e_pf { u32 ioremap_len; u32 fd_inv; u16 phy_led_val; + u16 last_sw_conf_flags; + u16 last_sw_conf_valid_flags; + + u16 override_q_count; + struct vfd_objects *vfd_obj; + u16 ingress_rule_id; + int ingress_vlan; + u16 egress_rule_id; + int egress_vlan; + bool vf_bw_applied; + /* List to keep previous DDP profiles to be rolled back in the future */ + struct list_head ddp_old_prof; }; /** @@ -605,7 +845,11 @@ struct i40e_veb { /* struct that defines a VSI, associated with a dev */ struct i40e_vsi { struct net_device *netdev; +#ifdef HAVE_VLAN_RX_REGISTER + struct vlan_group *vlgrp; +#else unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; +#endif bool netdev_registered; bool stat_offsets_loaded; @@ -613,7 +857,7 @@ struct i40e_vsi { DECLARE_BITMAP(state, __I40E_VSI_STATE_SIZE__); #define I40E_VSI_FLAG_FILTER_CHANGED BIT(0) #define I40E_VSI_FLAG_VEB_OWNER BIT(1) - unsigned long flags; + u64 flags; /* Per VSI lock to protect elements/hash (MAC filter) */ spinlock_t mac_filter_hash_lock; @@ -622,8 +866,13 @@ struct i40e_vsi { bool has_vlan_filter; /* VSI stats */ +#ifdef HAVE_NDO_GET_STATS64 struct rtnl_link_stats64 net_stats; struct rtnl_link_stats64 net_stats_offsets; +#else + struct net_device_stats net_stats; + struct net_device_stats net_stats_offsets; +#endif struct i40e_eth_stats eth_stats; struct i40e_eth_stats eth_stats_offsets; u32 tx_restart; @@ -649,7 +898,6 @@ struct i40e_vsi { u8 *rss_hkey_user; /* User configured hash keys */ u8 *rss_lut_user; /* User configured lookup table entries */ - u16 max_frame; u16 rx_buf_len; @@ -669,10 +917,13 @@ struct i40e_vsi { u16 alloc_queue_pairs; /* Allocated Tx/Rx queues */ u16 req_queue_pairs; /* User requested queue pairs */ u16 num_queue_pairs; /* Used tx and rx pairs */ - u16 num_desc; + u16 num_tx_desc; + u16 num_rx_desc; enum i40e_vsi_type type; /* VSI type, e.g., LAN, FCoE, etc */ s16 vf_id; /* Virtual function ID for SRIOV VSIs */ - +#ifdef __TC_MQPRIO_MODE_MAX + struct tc_mqprio_qopt_offload mqprio_qopt; /* queue parameters */ +#endif struct i40e_tc_configuration tc_config; struct i40e_aqc_vsi_properties_data info; @@ -693,11 +944,25 @@ struct i40e_vsi { struct kobject *kobj; /* sysfs object */ bool current_isup; /* Sync 'link up' logging */ enum i40e_aq_link_speed current_speed; /* Sync link speed logging */ + /* channel specific fields */ + u16 cnt_q_avail; /* num of queues available for channel usage */ + u16 orig_rss_size; + u16 current_rss_size; + bool reconfig_rss; + + u16 next_base_queue; /* next queue to be used for channel setup */ + + struct list_head ch_list; + u16 tc_seid_map[I40E_MAX_TRAFFIC_CLASS]; void *priv; /* client driver data reference. */ + bool block_tx_timeout; /* VSI specific handlers */ irqreturn_t (*irq_handler)(int irq, void *data); +#ifdef ETHTOOL_GRXRINGS +#endif + } ____cacheline_internodealigned_in_smp; struct i40e_netdev_priv { @@ -716,16 +981,17 @@ struct i40e_q_vector { struct i40e_ring_container rx; struct i40e_ring_container tx; + u8 itr_countdown; /* when 0 should adjust adaptive ITR */ u8 num_ringpairs; /* total number of ring pairs in vector */ +#ifdef HAVE_IRQ_AFFINITY_NOTIFY cpumask_t affinity_mask; struct irq_affinity_notify affinity_notify; +#endif struct rcu_head rcu; /* to avoid race with update stats on free */ char name[I40E_INT_NAME_STR_LEN]; bool arm_wb_state; -#define ITR_COUNTDOWN_START 100 - u8 itr_countdown; /* when 0 should adjust ITR */ } ____cacheline_internodealigned_in_smp; /* lan device */ @@ -845,7 +1111,7 @@ static inline void i40e_write_fd_input_set(struct i40e_pf *pf, /* needed by i40e_ethtool.c */ int i40e_up(struct i40e_vsi *vsi); void i40e_down(struct i40e_vsi *vsi); -extern const char i40e_driver_name[]; +extern char i40e_driver_name[]; extern const char i40e_driver_version_str[]; void i40e_do_reset_safe(struct i40e_pf *pf, u32 reset_flags); void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags, bool lock_acquired); @@ -854,6 +1120,7 @@ int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size); void i40e_fill_rss_lut(struct i40e_pf *pf, u8 *lut, u16 rss_table_size, u16 rss_size); struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id); +struct i40e_vsi *i40e_find_vsi_from_seid(struct i40e_pf *pf, u16 seid); /** * i40e_find_vsi_by_type - Find and return Flow Director VSI * @pf: PF to search for VSI @@ -874,8 +1141,13 @@ i40e_find_vsi_by_type(struct i40e_pf *pf, u16 type) return NULL; } void i40e_update_stats(struct i40e_vsi *vsi); +void i40e_update_veb_stats(struct i40e_veb *veb); void i40e_update_eth_stats(struct i40e_vsi *vsi); +#ifdef HAVE_NDO_GET_STATS64 struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi); +#else +struct net_device_stats *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi); +#endif int i40e_fetch_switch_configuration(struct i40e_pf *pf, bool printconfig); @@ -888,6 +1160,8 @@ u32 i40e_get_current_atr_cnt(struct i40e_pf *pf); u32 i40e_get_global_fd_count(struct i40e_pf *pf); bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features); void i40e_set_ethtool_ops(struct net_device *netdev); +struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi, + const u8 *macaddr, s16 vlan); struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, const u8 *macaddr, s16 vlan); void __i40e_del_filter(struct i40e_vsi *vsi, struct i40e_mac_filter *f); @@ -896,14 +1170,26 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi); struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, u16 uplink, u32 param1); int i40e_vsi_release(struct i40e_vsi *vsi); +int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type); +int i40e_vsi_setup_rx_resources(struct i40e_vsi *vsi); +int i40e_vsi_setup_tx_resources(struct i40e_vsi *vsi); +int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc); +int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename); void i40e_service_event_schedule(struct i40e_pf *pf); void i40e_notify_client_of_vf_msg(struct i40e_vsi *vsi, u32 vf_id, u8 *msg, u16 len); +int i40e_control_wait_tx_q(int seid, struct i40e_pf *pf, int pf_q, bool is_xdp, + bool enable); +int i40e_control_wait_rx_q(struct i40e_pf *pf, int pf_q, bool enable); int i40e_vsi_start_rings(struct i40e_vsi *vsi); void i40e_vsi_stop_rings(struct i40e_vsi *vsi); void i40e_vsi_stop_rings_no_wait(struct i40e_vsi *vsi); int i40e_vsi_wait_queues_disabled(struct i40e_vsi *vsi); +void i40e_quiesce_vsi(struct i40e_vsi *vsi); +void i40e_unquiesce_vsi(struct i40e_vsi *vsi); +void i40e_pf_quiesce_all_vsi(struct i40e_pf *pf); +void i40e_pf_unquiesce_all_vsi(struct i40e_pf *pf); int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count); struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags, u16 uplink_seid, u16 downlink_seid, u8 enabled_tc); @@ -912,6 +1198,7 @@ void i40e_veb_release(struct i40e_veb *veb); int i40e_veb_config_tc(struct i40e_veb *veb, u8 enabled_tc); int i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid); void i40e_vsi_remove_pvid(struct i40e_vsi *vsi); +int i40e_get_cloud_filter_type(u8 flags, u16 *type); void i40e_vsi_reset_stats(struct i40e_vsi *vsi); void i40e_pf_reset_stats(struct i40e_pf *pf); #ifdef CONFIG_DEBUG_FS @@ -933,6 +1220,7 @@ void i40e_notify_client_of_l2_param_changes(struct i40e_vsi *vsi); void i40e_notify_client_of_netdev_close(struct i40e_vsi *vsi, bool reset); void i40e_notify_client_of_vf_enable(struct i40e_pf *pf, u32 num_vfs); void i40e_notify_client_of_vf_reset(struct i40e_pf *pf, u32 vf_id); +void i40e_client_update_msix_info(struct i40e_pf *pf); int i40e_vf_client_capable(struct i40e_pf *pf, u32 vf_id); /** * i40e_irq_dynamic_enable - Enable default interrupt generation settings @@ -945,9 +1233,6 @@ static inline void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector) struct i40e_hw *hw = &pf->hw; u32 val; - /* definitely clear the PBA here, as this function is meant to - * clean out all previous interrupts AND enable the interrupt - */ val = I40E_PFINT_DYN_CTLN_INTENA_MASK | I40E_PFINT_DYN_CTLN_CLEARPBA_MASK | (I40E_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT); @@ -956,8 +1241,9 @@ static inline void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector) } void i40e_irq_dynamic_disable_icr0(struct i40e_pf *pf); -void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf, bool clearpba); +void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf); int i40e_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd); +int i40e_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd); int i40e_open(struct net_device *netdev); int i40e_close(struct net_device *netdev); int i40e_vsi_open(struct i40e_vsi *vsi); @@ -970,18 +1256,22 @@ struct i40e_mac_filter *i40e_add_mac_filter(struct i40e_vsi *vsi, const u8 *macaddr); int i40e_del_mac_filter(struct i40e_vsi *vsi, const u8 *macaddr); bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi); +int i40e_count_filters(struct i40e_vsi *vsi); struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, const u8 *macaddr); void i40e_vlan_stripping_enable(struct i40e_vsi *vsi); -#ifdef CONFIG_I40E_DCB +#ifdef CONFIG_DCB +#ifdef HAVE_DCBNL_IEEE void i40e_dcbnl_flush_apps(struct i40e_pf *pf, struct i40e_dcbx_config *old_cfg, struct i40e_dcbx_config *new_cfg); void i40e_dcbnl_set_all(struct i40e_vsi *vsi); void i40e_dcbnl_setup(struct i40e_vsi *vsi); +#endif /* HAVE_DCBNL_IEEE */ bool i40e_dcb_need_reconfig(struct i40e_pf *pf, struct i40e_dcbx_config *old_cfg, struct i40e_dcbx_config *new_cfg); -#endif /* CONFIG_I40E_DCB */ +#endif /* CONFIG_DCB */ +#ifdef HAVE_PTP_1588_CLOCK void i40e_ptp_rx_hang(struct i40e_pf *pf); void i40e_ptp_tx_hang(struct i40e_pf *pf); void i40e_ptp_tx_hwtstamp(struct i40e_pf *pf); @@ -989,16 +1279,45 @@ void i40e_ptp_rx_hwtstamp(struct i40e_pf *pf, struct sk_buff *skb, u8 index); void i40e_ptp_set_increment(struct i40e_pf *pf); int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr); int i40e_ptp_get_ts_config(struct i40e_pf *pf, struct ifreq *ifr); +int i40e_ptp_set_pins_ioctl(struct i40e_pf *pf, struct ifreq *ifr); +int i40e_ptp_get_pins(struct i40e_pf *pf, struct ifreq *ifr); +void i40e_ptp_save_hw_time(struct i40e_pf *pf); +void i40e_ptp_restore_hw_time(struct i40e_pf *pf); void i40e_ptp_init(struct i40e_pf *pf); void i40e_ptp_stop(struct i40e_pf *pf); +int i40e_ptp_alloc_pins(struct i40e_pf *pf); +#endif /* HAVE_PTP_1588_CLOCK */ +u8 i40e_pf_get_num_tc(struct i40e_pf *pf); int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi); i40e_status i40e_get_partition_bw_setting(struct i40e_pf *pf); i40e_status i40e_set_partition_bw_setting(struct i40e_pf *pf); i40e_status i40e_commit_partition_bw_setting(struct i40e_pf *pf); +int i40e_set_bw_limit(struct i40e_vsi *vsi, u16 seid, u64 max_tx_rate); +int i40e_add_del_cloud_filter_ex(struct i40e_pf *pf, + struct i40e_cloud_filter *filter, + bool add); +int i40e_add_del_cloud_filter(struct i40e_vsi *vsi, + struct i40e_cloud_filter *filter, + bool add); +int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi, + struct i40e_cloud_filter *filter, + bool add); void i40e_print_link_message(struct i40e_vsi *vsi, bool isup); +int i40e_create_queue_channel(struct i40e_vsi *vsi, struct i40e_channel *ch); +int i40e_get_link_speed(struct i40e_vsi *vsi); + +void i40e_set_fec_in_flags(u8 fec_cfg, u64 *flags); + +#ifdef HAVE_XDP_SUPPORT +int i40e_queue_pair_disable(struct i40e_vsi *vsi, int queue_pair); +int i40e_queue_pair_enable(struct i40e_vsi *vsi, int queue_pair); +#endif static inline bool i40e_enabled_xdp_vsi(struct i40e_vsi *vsi) { return !!vsi->xdp_prog; } +int i40e_restore_ingress_egress_mirror(struct i40e_vsi *src_vsi, int mirror, u16 rule_type, + u16 *rule_id); + #endif /* _I40E_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index ba04988e0..15b9d9c89 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2016 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #include "i40e_status.h" #include "i40e_type.h" @@ -30,8 +7,6 @@ #include "i40e_adminq.h" #include "i40e_prototype.h" -static void i40e_resume_aq(struct i40e_hw *hw); - /** * i40e_adminq_init_regs - Initialize AdminQ registers * @hw: pointer to the hardware structure @@ -119,6 +94,7 @@ static i40e_status i40e_alloc_adminq_arq_ring(struct i40e_hw *hw) **/ static void i40e_free_adminq_asq(struct i40e_hw *hw) { + i40e_free_virt_mem(hw, &hw->aq.asq.cmd_buf); i40e_free_dma_mem(hw, &hw->aq.asq.desc_buf); } @@ -169,21 +145,21 @@ static i40e_status i40e_alloc_arq_bufs(struct i40e_hw *hw) /* now configure the descriptors for use */ desc = I40E_ADMINQ_DESC(hw->aq.arq, i); - desc->flags = cpu_to_le16(I40E_AQ_FLAG_BUF); + desc->flags = CPU_TO_LE16(I40E_AQ_FLAG_BUF); if (hw->aq.arq_buf_size > I40E_AQ_LARGE_BUF) - desc->flags |= cpu_to_le16(I40E_AQ_FLAG_LB); + desc->flags |= CPU_TO_LE16(I40E_AQ_FLAG_LB); desc->opcode = 0; /* This is in accordance with Admin queue design, there is no * register for buffer size configuration */ - desc->datalen = cpu_to_le16((u16)bi->size); + desc->datalen = CPU_TO_LE16((u16)bi->size); desc->retval = 0; desc->cookie_high = 0; desc->cookie_low = 0; desc->params.external.addr_high = - cpu_to_le32(upper_32_bits(bi->pa)); + CPU_TO_LE32(upper_32_bits(bi->pa)); desc->params.external.addr_low = - cpu_to_le32(lower_32_bits(bi->pa)); + CPU_TO_LE32(lower_32_bits(bi->pa)); desc->params.external.param0 = 0; desc->params.external.param1 = 0; } @@ -291,7 +267,7 @@ static void i40e_free_asq_bufs(struct i40e_hw *hw) **/ static i40e_status i40e_config_asq_regs(struct i40e_hw *hw) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; u32 reg = 0; /* Clear Head and Tail */ @@ -320,7 +296,7 @@ static i40e_status i40e_config_asq_regs(struct i40e_hw *hw) **/ static i40e_status i40e_config_arq_regs(struct i40e_hw *hw) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; u32 reg = 0; /* Clear Head and Tail */ @@ -359,7 +335,7 @@ static i40e_status i40e_config_arq_regs(struct i40e_hw *hw) **/ static i40e_status i40e_init_asq(struct i40e_hw *hw) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; if (hw->aq.asq.count > 0) { /* queue already initialized */ @@ -379,18 +355,18 @@ static i40e_status i40e_init_asq(struct i40e_hw *hw) /* allocate the ring memory */ ret_code = i40e_alloc_adminq_asq_ring(hw); - if (ret_code) + if (ret_code != I40E_SUCCESS) goto init_adminq_exit; /* allocate buffers in the rings */ ret_code = i40e_alloc_asq_bufs(hw); - if (ret_code) + if (ret_code != I40E_SUCCESS) goto init_adminq_free_rings; /* initialize base registers */ ret_code = i40e_config_asq_regs(hw); - if (ret_code) - goto init_adminq_free_rings; + if (ret_code != I40E_SUCCESS) + goto init_config_regs; /* success! */ hw->aq.asq.count = hw->aq.num_asq_entries; @@ -398,6 +374,10 @@ static i40e_status i40e_init_asq(struct i40e_hw *hw) init_adminq_free_rings: i40e_free_adminq_asq(hw); + return ret_code; + +init_config_regs: + i40e_free_asq_bufs(hw); init_adminq_exit: return ret_code; @@ -418,7 +398,7 @@ init_adminq_exit: **/ static i40e_status i40e_init_arq(struct i40e_hw *hw) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; if (hw->aq.arq.count > 0) { /* queue already initialized */ @@ -438,17 +418,17 @@ static i40e_status i40e_init_arq(struct i40e_hw *hw) /* allocate the ring memory */ ret_code = i40e_alloc_adminq_arq_ring(hw); - if (ret_code) + if (ret_code != I40E_SUCCESS) goto init_adminq_exit; /* allocate buffers in the rings */ ret_code = i40e_alloc_arq_bufs(hw); - if (ret_code) + if (ret_code != I40E_SUCCESS) goto init_adminq_free_rings; /* initialize base registers */ ret_code = i40e_config_arq_regs(hw); - if (ret_code) + if (ret_code != I40E_SUCCESS) goto init_adminq_free_rings; /* success! */ @@ -470,9 +450,9 @@ init_adminq_exit: **/ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; - mutex_lock(&hw->aq.asq_mutex); + i40e_acquire_spinlock(&hw->aq.asq_spinlock); if (hw->aq.asq.count == 0) { ret_code = I40E_ERR_NOT_READY; @@ -492,7 +472,7 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) i40e_free_asq_bufs(hw); shutdown_asq_out: - mutex_unlock(&hw->aq.asq_mutex); + i40e_release_spinlock(&hw->aq.asq_spinlock); return ret_code; } @@ -504,9 +484,9 @@ shutdown_asq_out: **/ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; - mutex_lock(&hw->aq.arq_mutex); + i40e_acquire_spinlock(&hw->aq.arq_spinlock); if (hw->aq.arq.count == 0) { ret_code = I40E_ERR_NOT_READY; @@ -526,10 +506,86 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) i40e_free_arq_bufs(hw); shutdown_arq_out: - mutex_unlock(&hw->aq.arq_mutex); + i40e_release_spinlock(&hw->aq.arq_spinlock); return ret_code; } +/** + * i40e_resume_aq - resume AQ processing from 0 + * @hw: pointer to the hardware structure + **/ +static void i40e_resume_aq(struct i40e_hw *hw) +{ + /* Registers are reset after PF reset */ + hw->aq.asq.next_to_use = 0; + hw->aq.asq.next_to_clean = 0; + + i40e_config_asq_regs(hw); + + hw->aq.arq.next_to_use = 0; + hw->aq.arq.next_to_clean = 0; + + i40e_config_arq_regs(hw); +} + +/** + * i40e_set_hw_flags - set HW flags + * @hw: pointer to the hardware structure + **/ +static void i40e_set_hw_flags(struct i40e_hw *hw) +{ + struct i40e_adminq_info *aq = &hw->aq; + + hw->flags = 0; + + switch (hw->mac.type) { + case I40E_MAC_XL710: + if (aq->api_maj_ver > 1 || + (aq->api_maj_ver == 1 && + aq->api_min_ver >= I40E_MINOR_VER_GET_LINK_INFO_XL710)) { + hw->flags |= I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE; + hw->flags |= I40E_HW_FLAG_FW_LLDP_STOPPABLE; + /* The ability to RX (not drop) 802.1ad frames */ + hw->flags |= I40E_HW_FLAG_802_1AD_CAPABLE; + } + break; + case I40E_MAC_X722: + hw->flags |= I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE | + I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK; + + if (aq->api_maj_ver > 1 || + (aq->api_maj_ver == 1 && + aq->api_min_ver >= I40E_MINOR_VER_FW_LLDP_STOPPABLE_X722)) + hw->flags |= I40E_HW_FLAG_FW_LLDP_STOPPABLE; + + if (aq->api_maj_ver > 1 || + (aq->api_maj_ver == 1 && + aq->api_min_ver >= I40E_MINOR_VER_GET_LINK_INFO_X722)) + hw->flags |= I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE; + /* fall through */ + default: + break; + } + + /* Newer versions of firmware require lock when reading the NVM */ + if (aq->api_maj_ver > 1 || + (aq->api_maj_ver == 1 && + aq->api_min_ver >= 5)) + hw->flags |= I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK; + + if (aq->api_maj_ver > 1 || + (aq->api_maj_ver == 1 && + aq->api_min_ver >= 8)) { + hw->flags |= I40E_HW_FLAG_FW_LLDP_PERSISTENT; + hw->flags |= I40E_HW_FLAG_DROP_MODE; + } + + if (aq->api_maj_ver > 1 || + (aq->api_maj_ver == 1 && + aq->api_min_ver >= 9)) + hw->flags |= I40E_HW_FLAG_AQ_PHY_ACCESS_EXTENDED; +} + /** * i40e_init_adminq - main initialization routine for Admin Queue * @hw: pointer to the hardware structure @@ -543,19 +599,22 @@ shutdown_arq_out: **/ i40e_status i40e_init_adminq(struct i40e_hw *hw) { + struct i40e_adminq_info *aq = &hw->aq; + i40e_status ret_code; u16 cfg_ptr, oem_hi, oem_lo; u16 eetrack_lo, eetrack_hi; - i40e_status ret_code; int retry = 0; /* verify input for valid configuration */ - if ((hw->aq.num_arq_entries == 0) || - (hw->aq.num_asq_entries == 0) || - (hw->aq.arq_buf_size == 0) || - (hw->aq.asq_buf_size == 0)) { + if (aq->num_arq_entries == 0 || + aq->num_asq_entries == 0 || + aq->arq_buf_size == 0 || + aq->asq_buf_size == 0) { ret_code = I40E_ERR_CONFIG; goto init_adminq_exit; } + i40e_init_spinlock(&aq->asq_spinlock); + i40e_init_spinlock(&aq->arq_spinlock); /* Set up register offsets */ i40e_adminq_init_regs(hw); @@ -565,12 +624,12 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw) /* allocate the ASQ */ ret_code = i40e_init_asq(hw); - if (ret_code) - goto init_adminq_destroy_locks; + if (ret_code != I40E_SUCCESS) + goto init_adminq_destroy_spinlocks; /* allocate the ARQ */ ret_code = i40e_init_arq(hw); - if (ret_code) + if (ret_code != I40E_SUCCESS) goto init_adminq_free_asq; /* There are some cases where the firmware may not be quite ready @@ -579,11 +638,11 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw) */ do { ret_code = i40e_aq_get_firmware_version(hw, - &hw->aq.fw_maj_ver, - &hw->aq.fw_min_ver, - &hw->aq.fw_build, - &hw->aq.api_maj_ver, - &hw->aq.api_min_ver, + &aq->fw_maj_ver, + &aq->fw_min_ver, + &aq->fw_build, + &aq->api_maj_ver, + &aq->api_min_ver, NULL); if (ret_code != I40E_ERR_ADMIN_QUEUE_TIMEOUT) break; @@ -594,6 +653,12 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw) if (ret_code != I40E_SUCCESS) goto init_adminq_free_arq; + /* + * Some features were introduced in different FW API version + * for different MAC type. + */ + i40e_set_hw_flags(hw); + /* get the NVM version info */ i40e_read_nvm_word(hw, I40E_SR_NVM_DEV_STARTER_VERSION, &hw->nvm.version); @@ -601,13 +666,11 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw) i40e_read_nvm_word(hw, I40E_SR_NVM_EETRACK_HI, &eetrack_hi); hw->nvm.eetrack = (eetrack_hi << 16) | eetrack_lo; i40e_read_nvm_word(hw, I40E_SR_BOOT_CONFIG_PTR, &cfg_ptr); - i40e_read_nvm_word(hw, (cfg_ptr + I40E_NVM_OEM_VER_OFF), - &oem_hi); - i40e_read_nvm_word(hw, (cfg_ptr + (I40E_NVM_OEM_VER_OFF + 1)), - &oem_lo); + i40e_read_nvm_word(hw, (cfg_ptr + I40E_NVM_OEM_VER_OFF), &oem_hi); + i40e_read_nvm_word(hw, (cfg_ptr + (I40E_NVM_OEM_VER_OFF + 1)), &oem_lo); hw->nvm.oem_ver = ((u32)oem_hi << 16) | oem_lo; - if (hw->aq.api_maj_ver > I40E_FW_API_VERSION_MAJOR) { + if (aq->api_maj_ver > I40E_FW_API_VERSION_MAJOR) { ret_code = I40E_ERR_FIRMWARE_API_VERSION; goto init_adminq_free_arq; } @@ -617,7 +680,7 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw) hw->nvm_release_on_done = false; hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; - ret_code = 0; + ret_code = I40E_SUCCESS; /* success! */ goto init_adminq_exit; @@ -626,7 +689,9 @@ init_adminq_free_arq: i40e_shutdown_arq(hw); init_adminq_free_asq: i40e_shutdown_asq(hw); -init_adminq_destroy_locks: +init_adminq_destroy_spinlocks: + i40e_destroy_spinlock(&aq->asq_spinlock); + i40e_destroy_spinlock(&aq->arq_spinlock); init_adminq_exit: return ret_code; @@ -638,13 +703,15 @@ init_adminq_exit: **/ i40e_status i40e_shutdown_adminq(struct i40e_hw *hw) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; if (i40e_check_asq_alive(hw)) i40e_aq_queue_shutdown(hw, true); i40e_shutdown_asq(hw); i40e_shutdown_arq(hw); + i40e_destroy_spinlock(&hw->aq.asq_spinlock); + i40e_destroy_spinlock(&hw->aq.arq_spinlock); if (hw->nvm_buff.va) i40e_free_virt_mem(hw, &hw->nvm_buff); @@ -669,17 +736,18 @@ static u16 i40e_clean_asq(struct i40e_hw *hw) desc = I40E_ADMINQ_DESC(*asq, ntc); details = I40E_ADMINQ_DETAILS(*asq, ntc); while (rd32(hw, hw->aq.asq.head) != ntc) { - i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, + i40e_debug(hw, I40E_DEBUG_AQ_COMMAND, "ntc %d head %d.\n", ntc, rd32(hw, hw->aq.asq.head)); if (details->callback) { I40E_ADMINQ_CALLBACK cb_func = (I40E_ADMINQ_CALLBACK)details->callback; - desc_cb = *desc; + i40e_memcpy(&desc_cb, desc, sizeof(struct i40e_aq_desc), + I40E_DMA_TO_DMA); cb_func(hw, &desc_cb); } - memset(desc, 0, sizeof(*desc)); - memset(details, 0, sizeof(*details)); + i40e_memset(desc, 0, sizeof(*desc), I40E_DMA_MEM); + i40e_memset(details, 0, sizeof(*details), I40E_NONDMA_MEM); ntc++; if (ntc == asq->count) ntc = 0; @@ -709,23 +777,26 @@ static bool i40e_asq_done(struct i40e_hw *hw) } /** - * i40e_asq_send_command - send command to Admin Queue + * i40e_asq_send_command_atomic - send command to Admin Queue * @hw: pointer to the hw struct * @desc: prefilled descriptor describing the command (non DMA mem) * @buff: buffer to use for indirect commands * @buff_size: size of buffer for indirect commands * @cmd_details: pointer to command details structure + * @is_atomic_context: is the function called in an atomic context? * * This is the main send command driver routine for the Admin Queue send * queue. It runs the queue, cleans the queue, etc **/ -i40e_status i40e_asq_send_command(struct i40e_hw *hw, - struct i40e_aq_desc *desc, - void *buff, /* can be NULL */ - u16 buff_size, - struct i40e_asq_cmd_details *cmd_details) +enum i40e_status_code +i40e_asq_send_command_atomic(struct i40e_hw *hw, + struct i40e_aq_desc *desc, + void *buff, /* can be NULL */ + u16 buff_size, + struct i40e_asq_cmd_details *cmd_details, + bool is_atomic_context) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; struct i40e_dma_mem *dma_buff = NULL; struct i40e_asq_cmd_details *details; struct i40e_aq_desc *desc_on_ring; @@ -733,7 +804,9 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, u16 retval = 0; u32 val = 0; - mutex_lock(&hw->aq.asq_mutex); + i40e_acquire_spinlock(&hw->aq.asq_spinlock); + + hw->aq.asq_last_status = I40E_AQ_RC_OK; if (hw->aq.asq.count == 0) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, @@ -742,37 +815,40 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, goto asq_send_command_error; } - hw->aq.asq_last_status = I40E_AQ_RC_OK; - val = rd32(hw, hw->aq.asq.head); if (val >= hw->aq.num_asq_entries) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQTX: head overrun at %d\n", val); - status = I40E_ERR_QUEUE_EMPTY; + status = I40E_ERR_ADMIN_QUEUE_FULL; goto asq_send_command_error; } details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use); if (cmd_details) { - *details = *cmd_details; + i40e_memcpy(details, + cmd_details, + sizeof(struct i40e_asq_cmd_details), + I40E_NONDMA_TO_NONDMA); /* If the cmd_details are defined copy the cookie. The - * cpu_to_le32 is not needed here because the data is ignored + * CPU_TO_LE32 is not needed here because the data is ignored * by the FW, only used by the driver */ if (details->cookie) { desc->cookie_high = - cpu_to_le32(upper_32_bits(details->cookie)); + CPU_TO_LE32(upper_32_bits(details->cookie)); desc->cookie_low = - cpu_to_le32(lower_32_bits(details->cookie)); + CPU_TO_LE32(lower_32_bits(details->cookie)); } } else { - memset(details, 0, sizeof(struct i40e_asq_cmd_details)); + i40e_memset(details, 0, + sizeof(struct i40e_asq_cmd_details), + I40E_NONDMA_MEM); } /* clear requested flags and then set additional flags if defined */ - desc->flags &= ~cpu_to_le16(details->flags_dis); - desc->flags |= cpu_to_le16(details->flags_ena); + desc->flags &= ~CPU_TO_LE16(details->flags_dis); + desc->flags |= CPU_TO_LE16(details->flags_ena); if (buff_size > hw->aq.asq_buf_size) { i40e_debug(hw, @@ -810,26 +886,28 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, desc_on_ring = I40E_ADMINQ_DESC(hw->aq.asq, hw->aq.asq.next_to_use); /* if the desc is available copy the temp desc to the right place */ - *desc_on_ring = *desc; + i40e_memcpy(desc_on_ring, desc, sizeof(struct i40e_aq_desc), + I40E_NONDMA_TO_DMA); /* if buff is not NULL assume indirect command */ if (buff != NULL) { dma_buff = &(hw->aq.asq.r.asq_bi[hw->aq.asq.next_to_use]); /* copy the user buff into the respective DMA buff */ - memcpy(dma_buff->va, buff, buff_size); - desc_on_ring->datalen = cpu_to_le16(buff_size); + i40e_memcpy(dma_buff->va, buff, buff_size, + I40E_NONDMA_TO_DMA); + desc_on_ring->datalen = CPU_TO_LE16(buff_size); /* Update the address values in the desc with the pa value * for respective buffer */ desc_on_ring->params.external.addr_high = - cpu_to_le32(upper_32_bits(dma_buff->pa)); + CPU_TO_LE32(upper_32_bits(dma_buff->pa)); desc_on_ring->params.external.addr_low = - cpu_to_le32(lower_32_bits(dma_buff->pa)); + CPU_TO_LE32(lower_32_bits(dma_buff->pa)); } /* bump the tail */ - i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQTX: desc and buffer:\n"); + i40e_debug(hw, I40E_DEBUG_AQ_COMMAND, "AQTX: desc and buffer:\n"); i40e_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc_on_ring, buff, buff_size); (hw->aq.asq.next_to_use)++; @@ -850,17 +928,22 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, */ if (i40e_asq_done(hw)) break; - udelay(50); + if (is_atomic_context) + udelay(50); + else + usleep_range(40, 60); total_delay += 50; } while (total_delay < hw->aq.asq_cmd_timeout); } /* if ready, copy the desc back to temp */ if (i40e_asq_done(hw)) { - *desc = *desc_on_ring; + i40e_memcpy(desc, desc_on_ring, sizeof(struct i40e_aq_desc), + I40E_DMA_TO_NONDMA); if (buff != NULL) - memcpy(buff, dma_buff->va, buff_size); - retval = le16_to_cpu(desc->retval); + i40e_memcpy(buff, dma_buff->va, buff_size, + I40E_DMA_TO_NONDMA); + retval = LE16_TO_CPU(desc->retval); if (retval != 0) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, @@ -872,34 +955,54 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, } cmd_completed = true; if ((enum i40e_admin_queue_err)retval == I40E_AQ_RC_OK) - status = 0; + status = I40E_SUCCESS; + else if ((enum i40e_admin_queue_err)retval == I40E_AQ_RC_EBUSY) + status = I40E_ERR_NOT_READY; else status = I40E_ERR_ADMIN_QUEUE_ERROR; hw->aq.asq_last_status = (enum i40e_admin_queue_err)retval; } - i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, + i40e_debug(hw, I40E_DEBUG_AQ_COMMAND, "AQTX: desc and buffer writeback:\n"); i40e_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, buff, buff_size); /* save writeback aq if requested */ if (details->wb_desc) - *details->wb_desc = *desc_on_ring; + i40e_memcpy(details->wb_desc, desc_on_ring, + sizeof(struct i40e_aq_desc), I40E_DMA_TO_NONDMA); /* update the error if time out occurred */ if ((!cmd_completed) && (!details->async && !details->postpone)) { - i40e_debug(hw, - I40E_DEBUG_AQ_MESSAGE, - "AQTX: Writeback timeout.\n"); - status = I40E_ERR_ADMIN_QUEUE_TIMEOUT; + if (rd32(hw, hw->aq.asq.len) & I40E_GL_ATQLEN_ATQCRIT_MASK) { + i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, + "AQTX: AQ Critical error.\n"); + status = I40E_ERR_ADMIN_QUEUE_CRITICAL_ERROR; + } else { + i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, + "AQTX: Writeback timeout.\n"); + status = I40E_ERR_ADMIN_QUEUE_TIMEOUT; + } } asq_send_command_error: - mutex_unlock(&hw->aq.asq_mutex); + i40e_release_spinlock(&hw->aq.asq_spinlock); return status; } +// inline function with previous signature to avoid modifying +// all existing calls to i40e_asq_send_command +inline i40e_status i40e_asq_send_command(struct i40e_hw *hw, + struct i40e_aq_desc *desc, + void *buff, /* can be NULL */ + u16 buff_size, + struct i40e_asq_cmd_details *cmd_details) +{ + return i40e_asq_send_command_atomic(hw, desc, buff, buff_size, + cmd_details, false); +} + /** * i40e_fill_default_direct_cmd_desc - AQ descriptor helper function * @desc: pointer to the temp descriptor (non DMA mem) @@ -911,9 +1014,10 @@ void i40e_fill_default_direct_cmd_desc(struct i40e_aq_desc *desc, u16 opcode) { /* zero out the desc */ - memset((void *)desc, 0, sizeof(struct i40e_aq_desc)); - desc->opcode = cpu_to_le16(opcode); - desc->flags = cpu_to_le16(I40E_AQ_FLAG_SI); + i40e_memset((void *)desc, 0, sizeof(struct i40e_aq_desc), + I40E_NONDMA_MEM); + desc->opcode = CPU_TO_LE16(opcode); + desc->flags = CPU_TO_LE16(I40E_AQ_FLAG_SI); } /** @@ -930,7 +1034,7 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw, struct i40e_arq_event_info *e, u16 *pending) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; u16 ntc = hw->aq.arq.next_to_clean; struct i40e_aq_desc *desc; struct i40e_dma_mem *bi; @@ -940,10 +1044,10 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw, u16 ntu; /* pre-clean the event info */ - memset(&e->desc, 0, sizeof(e->desc)); + i40e_memset(&e->desc, 0, sizeof(e->desc), I40E_NONDMA_MEM); /* take the lock before we start messing with the ring */ - mutex_lock(&hw->aq.arq_mutex); + i40e_acquire_spinlock(&hw->aq.arq_spinlock); if (hw->aq.arq.count == 0) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, @@ -953,7 +1057,7 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw, } /* set next_to_use to head */ - ntu = (rd32(hw, hw->aq.arq.head) & I40E_PF_ARQH_ARQH_MASK); + ntu = rd32(hw, hw->aq.arq.head) & I40E_PF_ARQH_ARQH_MASK; if (ntu == ntc) { /* nothing to do - shouldn't need to update ring's values */ ret_code = I40E_ERR_ADMIN_QUEUE_NO_WORK; @@ -965,8 +1069,8 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw, desc_idx = ntc; hw->aq.arq_last_status = - (enum i40e_admin_queue_err)le16_to_cpu(desc->retval); - flags = le16_to_cpu(desc->flags); + (enum i40e_admin_queue_err)LE16_TO_CPU(desc->retval); + flags = LE16_TO_CPU(desc->flags); if (flags & I40E_AQ_FLAG_ERR) { ret_code = I40E_ERR_ADMIN_QUEUE_ERROR; i40e_debug(hw, @@ -975,14 +1079,16 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw, hw->aq.arq_last_status); } - e->desc = *desc; - datalen = le16_to_cpu(desc->datalen); + i40e_memcpy(&e->desc, desc, sizeof(struct i40e_aq_desc), + I40E_DMA_TO_NONDMA); + datalen = LE16_TO_CPU(desc->datalen); e->msg_len = min(datalen, e->buf_len); if (e->msg_buf != NULL && (e->msg_len != 0)) - memcpy(e->msg_buf, hw->aq.arq.r.arq_bi[desc_idx].va, - e->msg_len); + i40e_memcpy(e->msg_buf, + hw->aq.arq.r.arq_bi[desc_idx].va, + e->msg_len, I40E_DMA_TO_NONDMA); - i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQRX: desc and buffer:\n"); + i40e_debug(hw, I40E_DEBUG_AQ_COMMAND, "AQRX: desc and buffer:\n"); i40e_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, e->msg_buf, hw->aq.arq_buf_size); @@ -991,14 +1097,14 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw, * size */ bi = &hw->aq.arq.r.arq_bi[ntc]; - memset((void *)desc, 0, sizeof(struct i40e_aq_desc)); + i40e_memset((void *)desc, 0, sizeof(struct i40e_aq_desc), I40E_DMA_MEM); - desc->flags = cpu_to_le16(I40E_AQ_FLAG_BUF); + desc->flags = CPU_TO_LE16(I40E_AQ_FLAG_BUF); if (hw->aq.arq_buf_size > I40E_AQ_LARGE_BUF) - desc->flags |= cpu_to_le16(I40E_AQ_FLAG_LB); - desc->datalen = cpu_to_le16((u16)bi->size); - desc->params.external.addr_high = cpu_to_le32(upper_32_bits(bi->pa)); - desc->params.external.addr_low = cpu_to_le32(lower_32_bits(bi->pa)); + desc->flags |= CPU_TO_LE16(I40E_AQ_FLAG_LB); + desc->datalen = CPU_TO_LE16((u16)bi->size); + desc->params.external.addr_high = CPU_TO_LE32(upper_32_bits(bi->pa)); + desc->params.external.addr_low = CPU_TO_LE32(lower_32_bits(bi->pa)); /* set tail = the last cleaned desc index. */ wr32(hw, hw->aq.arq.tail, ntc); @@ -1009,27 +1115,14 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw, hw->aq.arq.next_to_clean = ntc; hw->aq.arq.next_to_use = ntu; - i40e_nvmupd_check_wait_event(hw, le16_to_cpu(e->desc.opcode)); + i40e_nvmupd_check_wait_event(hw, LE16_TO_CPU(e->desc.opcode), &e->desc); clean_arq_element_out: /* Set pending if needed, unlock and return */ - if (pending) + if (pending != NULL) *pending = (ntc > ntu ? hw->aq.arq.count : 0) + (ntu - ntc); clean_arq_element_err: - mutex_unlock(&hw->aq.arq_mutex); + i40e_release_spinlock(&hw->aq.arq_spinlock); return ret_code; } -static void i40e_resume_aq(struct i40e_hw *hw) -{ - /* Registers are reset after PF reset */ - hw->aq.asq.next_to_use = 0; - hw->aq.asq.next_to_clean = 0; - - i40e_config_asq_regs(hw); - - hw->aq.arq.next_to_use = 0; - hw->aq.arq.next_to_clean = 0; - - i40e_config_arq_regs(hw); -} diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.h b/drivers/net/ethernet/intel/i40e/i40e_adminq.h index 2349fbe04..f0af697c4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.h @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_ADMINQ_H_ #define _I40E_ADMINQ_H_ @@ -98,8 +75,8 @@ struct i40e_adminq_info { u16 api_maj_ver; /* api major version */ u16 api_min_ver; /* api minor version */ - struct mutex asq_mutex; /* Send queue lock */ - struct mutex arq_mutex; /* Receive queue lock */ + struct i40e_spinlock asq_spinlock; /* Send queue spinlock */ + struct i40e_spinlock arq_spinlock; /* Receive queue spinlock */ /* last status values on send and receive queues */ enum i40e_admin_queue_err asq_last_status; @@ -111,7 +88,7 @@ struct i40e_adminq_info { * aq_ret: AdminQ handler error code can override aq_rc * aq_rc: AdminQ firmware error code to convert **/ -static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc) +static INLINE int i40e_aq_rc_to_posix(int aq_ret, int aq_rc) { int aq_to_posix[] = { 0, /* I40E_AQ_RC_OK */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 5d5f422cb..f4940fe53 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2017 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_ADMINQ_CMD_H_ #define _I40E_ADMINQ_CMD_H_ @@ -34,7 +11,19 @@ */ #define I40E_FW_API_VERSION_MAJOR 0x0001 -#define I40E_FW_API_VERSION_MINOR 0x0005 +#define I40E_FW_API_VERSION_MINOR_X722 0x0009 +#define I40E_FW_API_VERSION_MINOR_X710 0x000A + +#define I40E_FW_MINOR_VERSION(_h) ((_h)->mac.type == I40E_MAC_XL710 ? \ + I40E_FW_API_VERSION_MINOR_X710 : \ + I40E_FW_API_VERSION_MINOR_X722) + +/* API version 1.7 implements additional link and PHY-specific APIs */ +#define I40E_MINOR_VER_GET_LINK_INFO_XL710 0x0007 +/* API version 1.9 for X722 implements additional link and PHY-specific APIs */ +#define I40E_MINOR_VER_GET_LINK_INFO_X722 0x0009 +/* API version 1.6 for X722 devices adds ability to stop FW LLDP agent */ +#define I40E_MINOR_VER_FW_LLDP_STOPPABLE_X722 0x0006 struct i40e_aq_desc { __le16 flags; @@ -78,17 +67,17 @@ struct i40e_aq_desc { #define I40E_AQ_FLAG_EI_SHIFT 14 #define I40E_AQ_FLAG_FE_SHIFT 15 -#define I40E_AQ_FLAG_DD BIT(I40E_AQ_FLAG_DD_SHIFT) /* 0x1 */ -#define I40E_AQ_FLAG_CMP BIT(I40E_AQ_FLAG_CMP_SHIFT) /* 0x2 */ -#define I40E_AQ_FLAG_ERR BIT(I40E_AQ_FLAG_ERR_SHIFT) /* 0x4 */ -#define I40E_AQ_FLAG_VFE BIT(I40E_AQ_FLAG_VFE_SHIFT) /* 0x8 */ -#define I40E_AQ_FLAG_LB BIT(I40E_AQ_FLAG_LB_SHIFT) /* 0x200 */ -#define I40E_AQ_FLAG_RD BIT(I40E_AQ_FLAG_RD_SHIFT) /* 0x400 */ -#define I40E_AQ_FLAG_VFC BIT(I40E_AQ_FLAG_VFC_SHIFT) /* 0x800 */ -#define I40E_AQ_FLAG_BUF BIT(I40E_AQ_FLAG_BUF_SHIFT) /* 0x1000 */ -#define I40E_AQ_FLAG_SI BIT(I40E_AQ_FLAG_SI_SHIFT) /* 0x2000 */ -#define I40E_AQ_FLAG_EI BIT(I40E_AQ_FLAG_EI_SHIFT) /* 0x4000 */ -#define I40E_AQ_FLAG_FE BIT(I40E_AQ_FLAG_FE_SHIFT) /* 0x8000 */ +#define I40E_AQ_FLAG_DD (1 << I40E_AQ_FLAG_DD_SHIFT) /* 0x1 */ +#define I40E_AQ_FLAG_CMP (1 << I40E_AQ_FLAG_CMP_SHIFT) /* 0x2 */ +#define I40E_AQ_FLAG_ERR (1 << I40E_AQ_FLAG_ERR_SHIFT) /* 0x4 */ +#define I40E_AQ_FLAG_VFE (1 << I40E_AQ_FLAG_VFE_SHIFT) /* 0x8 */ +#define I40E_AQ_FLAG_LB (1 << I40E_AQ_FLAG_LB_SHIFT) /* 0x200 */ +#define I40E_AQ_FLAG_RD (1 << I40E_AQ_FLAG_RD_SHIFT) /* 0x400 */ +#define I40E_AQ_FLAG_VFC (1 << I40E_AQ_FLAG_VFC_SHIFT) /* 0x800 */ +#define I40E_AQ_FLAG_BUF (1 << I40E_AQ_FLAG_BUF_SHIFT) /* 0x1000 */ +#define I40E_AQ_FLAG_SI (1 << I40E_AQ_FLAG_SI_SHIFT) /* 0x2000 */ +#define I40E_AQ_FLAG_EI (1 << I40E_AQ_FLAG_EI_SHIFT) /* 0x4000 */ +#define I40E_AQ_FLAG_FE (1 << I40E_AQ_FLAG_FE_SHIFT) /* 0x8000 */ /* error codes */ enum i40e_admin_queue_err { @@ -146,6 +135,7 @@ enum i40e_admin_queue_opc { /* WoL commands */ i40e_aqc_opc_set_wol_filter = 0x0120, i40e_aqc_opc_get_wake_reason = 0x0121, + i40e_aqc_opc_clear_all_wol_filters = 0x025E, /* internal switch commands */ i40e_aqc_opc_get_switch_config = 0x0200, @@ -186,17 +176,19 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_add_cloud_filters = 0x025C, i40e_aqc_opc_remove_cloud_filters = 0x025D, i40e_aqc_opc_clear_wol_switch_filters = 0x025E, + i40e_aqc_opc_replace_cloud_filters = 0x025F, i40e_aqc_opc_add_mirror_rule = 0x0260, i40e_aqc_opc_delete_mirror_rule = 0x0261, - /* Pipeline Personalization Profile */ + /* Dynamic Device Personalization */ i40e_aqc_opc_write_personalization_profile = 0x0270, i40e_aqc_opc_get_personalization_profile_list = 0x0271, /* DCB commands */ i40e_aqc_opc_dcb_ignore_pfc = 0x0301, i40e_aqc_opc_dcb_updated = 0x0302, + i40e_aqc_opc_set_dcb_parameters = 0x0303, /* TX scheduler */ i40e_aqc_opc_configure_vsi_bw_limit = 0x0400, @@ -221,6 +213,8 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_query_hmc_resource_profile = 0x0500, i40e_aqc_opc_set_hmc_resource_profile = 0x0501, + /* phy commands*/ + /* phy commands*/ i40e_aqc_opc_get_phy_abilities = 0x0600, i40e_aqc_opc_set_phy_config = 0x0601, @@ -236,6 +230,8 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_set_phy_debug = 0x0622, i40e_aqc_opc_upload_ext_phy_fm = 0x0625, i40e_aqc_opc_run_phy_activity = 0x0626, + i40e_aqc_opc_set_phy_register = 0x0628, + i40e_aqc_opc_get_phy_register = 0x0629, /* NVM commands */ i40e_aqc_opc_nvm_read = 0x0701, @@ -243,6 +239,7 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_nvm_update = 0x0703, i40e_aqc_opc_nvm_config_read = 0x0704, i40e_aqc_opc_nvm_config_write = 0x0705, + i40e_aqc_opc_nvm_progress = 0x0706, i40e_aqc_opc_oem_post_update = 0x0720, i40e_aqc_opc_thermal_sensor = 0x0721, @@ -271,6 +268,7 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_get_cee_dcb_cfg = 0x0A07, i40e_aqc_opc_lldp_set_local_mib = 0x0A08, i40e_aqc_opc_lldp_stop_start_spec_agent = 0x0A09, + i40e_aqc_opc_lldp_restore = 0x0A0A, /* Tunnel commands */ i40e_aqc_opc_add_udp_tunnel = 0x0B00, @@ -531,6 +529,7 @@ struct i40e_aqc_mac_address_read { #define I40E_AQC_PORT_ADDR_VALID 0x40 #define I40E_AQC_WOL_ADDR_VALID 0x80 #define I40E_AQC_MC_MAG_EN_VALID 0x100 +#define I40E_AQC_WOL_PRESERVE_STATUS 0x200 #define I40E_AQC_ADDR_VALID_MASK 0x3F0 u8 reserved[6]; __le32 addr_high; @@ -591,6 +590,7 @@ struct i40e_aqc_set_wol_filter { __le16 cmd_flags; #define I40E_AQC_SET_WOL_FILTER 0x8000 #define I40E_AQC_SET_WOL_FILTER_NO_TCO_WOL 0x4000 +#define I40E_AQC_SET_WOL_FILTER_WOL_PRESERVE_ON_PFR 0x2000 #define I40E_AQC_SET_WOL_FILTER_ACTION_CLEAR 0 #define I40E_AQC_SET_WOL_FILTER_ACTION_SET 1 __le16 valid_flags; @@ -764,8 +764,52 @@ struct i40e_aqc_set_switch_config { /* flags used for both fields below */ #define I40E_AQ_SET_SWITCH_CFG_PROMISC 0x0001 #define I40E_AQ_SET_SWITCH_CFG_L2_FILTER 0x0002 +#define I40E_AQ_SET_SWITCH_CFG_HW_ATR_EVICT 0x0004 __le16 valid_flags; - u8 reserved[12]; + /* The ethertype in switch_tag is dropped on ingress and used + * internally by the switch. Set this to zero for the default + * of 0x88a8 (802.1ad). Should be zero for firmware API + * versions lower than 1.7. + */ + __le16 switch_tag; + /* The ethertypes in first_tag and second_tag are used to + * match the outer and inner VLAN tags (respectively) when HW + * double VLAN tagging is enabled via the set port parameters + * AQ command. Otherwise these are both ignored. Set them to + * zero for their defaults of 0x8100 (802.1Q). Should be zero + * for firmware API versions lower than 1.7. + */ + __le16 first_tag; + __le16 second_tag; + /* Next byte is split into following: + * Bit 7 : 0 : No action, 1: Switch to mode defined by bits 6:0 + * Bit 6 : 0 : Destination Port, 1: source port + * Bit 5..4 : L4 type + * 0: rsvd + * 1: TCP + * 2: UDP + * 3: Both TCP and UDP + * Bits 3:0 Mode + * 0: default mode + * 1: L4 port only mode + * 2: non-tunneled mode + * 3: tunneled mode + */ +#define I40E_AQ_SET_SWITCH_BIT7_VALID 0x80 + +#define I40E_AQ_SET_SWITCH_L4_SRC_PORT 0x40 + +#define I40E_AQ_SET_SWITCH_L4_TYPE_RSVD 0x00 +#define I40E_AQ_SET_SWITCH_L4_TYPE_TCP 0x10 +#define I40E_AQ_SET_SWITCH_L4_TYPE_UDP 0x20 +#define I40E_AQ_SET_SWITCH_L4_TYPE_BOTH 0x30 + +#define I40E_AQ_SET_SWITCH_MODE_DEFAULT 0x00 +#define I40E_AQ_SET_SWITCH_MODE_L4_PORT 0x01 +#define I40E_AQ_SET_SWITCH_MODE_NON_TUNNEL 0x02 +#define I40E_AQ_SET_SWITCH_MODE_TUNNEL 0x03 + u8 mode; + u8 rsvd5[5]; }; I40E_CHECK_CMD_LENGTH(i40e_aqc_set_switch_config); @@ -1318,14 +1362,17 @@ struct i40e_aqc_add_remove_cloud_filters { #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT 0 #define I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_MASK (0x3FF << \ I40E_AQC_ADD_CLOUD_CMD_SEID_NUM_SHIFT) - u8 reserved2[4]; + u8 big_buffer_flag; +#define I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER 1 +#define I40E_AQC_ADD_CLOUD_CMD_BB 1 + u8 reserved2[3]; __le32 addr_high; __le32 addr_low; }; I40E_CHECK_CMD_LENGTH(i40e_aqc_add_remove_cloud_filters); -struct i40e_aqc_add_remove_cloud_filters_element_data { +struct i40e_aqc_cloud_filters_element_data { u8 outer_mac[6]; u8 inner_mac[6]; __le16 inner_vlan; @@ -1337,13 +1384,16 @@ struct i40e_aqc_add_remove_cloud_filters_element_data { struct { u8 data[16]; } v6; + struct { + __le16 data[8]; + } raw_v6; } ipaddr; __le16 flags; #define I40E_AQC_ADD_CLOUD_FILTER_SHIFT 0 #define I40E_AQC_ADD_CLOUD_FILTER_MASK (0x3F << \ I40E_AQC_ADD_CLOUD_FILTER_SHIFT) /* 0x0000 reserved */ -#define I40E_AQC_ADD_CLOUD_FILTER_OIP 0x0001 +/* 0x0001 reserved */ /* 0x0002 reserved */ #define I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN 0x0003 #define I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN_TEN_ID 0x0004 @@ -1355,6 +1405,13 @@ struct i40e_aqc_add_remove_cloud_filters_element_data { #define I40E_AQC_ADD_CLOUD_FILTER_IMAC 0x000A #define I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC 0x000B #define I40E_AQC_ADD_CLOUD_FILTER_IIP 0x000C +/* 0x000D reserved */ +/* 0x000E reserved */ +/* 0x000F reserved */ +/* 0x0010 to 0x0017 is for custom filters */ +#define I40E_AQC_ADD_CLOUD_FILTER_IP_PORT 0x0010 /* Dest IP + L4 Port */ +#define I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT 0x0011 /* Dest MAC + L4 Port */ +#define I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT 0x0012 /* Dest MAC + VLAN + L4 Port */ #define I40E_AQC_ADD_CLOUD_FLAGS_TO_QUEUE 0x0080 #define I40E_AQC_ADD_CLOUD_VNK_SHIFT 6 @@ -1389,6 +1446,88 @@ struct i40e_aqc_add_remove_cloud_filters_element_data { u8 response_reserved[7]; }; +/* i40e_aqc_add_rm_cloud_filt_elem_ext is used when + * I40E_AQC_ADD_REM_CLOUD_CMD_BIG_BUFFER flag is set. + */ +struct i40e_aqc_add_rm_cloud_filt_elem_ext { + struct i40e_aqc_cloud_filters_element_data element; + u16 general_fields[32]; +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD0 0 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1 1 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD2 2 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0 3 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1 4 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2 5 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD0 6 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD1 7 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD2 8 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD0 9 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD1 10 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD2 11 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD0 12 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD1 13 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD2 14 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0 15 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD1 16 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD2 17 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD3 18 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD4 19 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD5 20 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD6 21 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD7 22 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD0 23 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD1 24 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD2 25 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD3 26 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD4 27 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD5 28 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD6 29 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD7 30 +}; + +I40E_CHECK_STRUCT_LEN(0x40, i40e_aqc_cloud_filters_element_data); + +/* i40e_aqc_cloud_filters_element_bb is used when + * I40E_AQC_CLOUD_CMD_BB flag is set. + */ +struct i40e_aqc_cloud_filters_element_bb { + struct i40e_aqc_cloud_filters_element_data element; + u16 general_fields[32]; +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD0 0 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD1 1 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X10_WORD2 2 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD0 3 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD1 4 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X11_WORD2 5 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD0 6 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD1 7 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X12_WORD2 8 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD0 9 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD1 10 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X13_WORD2 11 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD0 12 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD1 13 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X14_WORD2 14 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0 15 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD1 16 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD2 17 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD3 18 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD4 19 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD5 20 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD6 21 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD7 22 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD0 23 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD1 24 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD2 25 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD3 26 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD4 27 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD5 28 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD6 29 +#define I40E_AQC_ADD_CLOUD_FV_FLU_0X17_WORD7 30 +}; + +I40E_CHECK_STRUCT_LEN(0x80, i40e_aqc_cloud_filters_element_bb); + struct i40e_aqc_remove_cloud_filters_completion { __le16 perfect_ovlan_used; __le16 perfect_ovlan_free; @@ -1400,6 +1539,61 @@ struct i40e_aqc_remove_cloud_filters_completion { I40E_CHECK_CMD_LENGTH(i40e_aqc_remove_cloud_filters_completion); +/* Replace filter Command 0x025F + * uses the i40e_aqc_replace_cloud_filters, + * and the generic indirect completion structure + */ +struct i40e_filter_data { + u8 filter_type; + u8 input[3]; +}; + +I40E_CHECK_STRUCT_LEN(4, i40e_filter_data); + +struct i40e_aqc_replace_cloud_filters_cmd { + u8 valid_flags; +#define I40E_AQC_REPLACE_L1_FILTER 0x0 +#define I40E_AQC_REPLACE_CLOUD_FILTER 0x1 +#define I40E_AQC_GET_CLOUD_FILTERS 0x2 +#define I40E_AQC_MIRROR_CLOUD_FILTER 0x4 +#define I40E_AQC_HIGH_PRIORITY_CLOUD_FILTER 0x8 + u8 old_filter_type; + u8 new_filter_type; + u8 tr_bit; + u8 tr_bit2; + u8 reserved[3]; + __le32 addr_high; + __le32 addr_low; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_replace_cloud_filters_cmd); + +struct i40e_aqc_replace_cloud_filters_cmd_buf { + u8 data[32]; +/* Filter type INPUT codes*/ +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_ENTRIES_MAX 3 +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_VALIDATED (1 << 7UL) + +/* Field Vector offsets */ +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_MAC_DA 0 +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_ETH 6 +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG 7 +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_VLAN 8 +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_OVLAN 9 +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_STAG_IVLAN 10 +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_TUNNLE_KEY 11 +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IMAC 12 +/* big FLU */ +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_IP_DA 14 +/* big FLU */ +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_OIP_DA 15 + +#define I40E_AQC_REPLACE_CLOUD_CMD_INPUT_FV_INNER_VLAN 37 + struct i40e_filter_data filters[8]; +}; + +I40E_CHECK_STRUCT_LEN(0x40, i40e_aqc_replace_cloud_filters_cmd_buf); + /* Add Mirror Rule (indirect or direct 0x0260) * Delete Mirror Rule (indirect or direct 0x0261) * note: some rule types (4,5) do not use an external buffer. @@ -1435,7 +1629,7 @@ struct i40e_aqc_add_delete_mirror_rule_completion { I40E_CHECK_CMD_LENGTH(i40e_aqc_add_delete_mirror_rule_completion); -/* Pipeline Personalization Profile */ +/* Dynamic Device Personalization */ struct i40e_aqc_write_personalization_profile { u8 flags; u8 reserved[3]; @@ -1446,7 +1640,7 @@ struct i40e_aqc_write_personalization_profile { I40E_CHECK_CMD_LENGTH(i40e_aqc_write_personalization_profile); -struct i40e_aqc_write_ppp_resp { +struct i40e_aqc_write_ddp_resp { __le32 error_offset; __le32 error_info; __le32 addr_high; @@ -1455,8 +1649,8 @@ struct i40e_aqc_write_ppp_resp { struct i40e_aqc_get_applied_profiles { u8 flags; -#define I40E_AQC_GET_PPP_GET_CONF 0x1 -#define I40E_AQC_GET_PPP_GET_RDPU_CONF 0x2 +#define I40E_AQC_GET_DDP_GET_CONF 0x1 +#define I40E_AQC_GET_DDP_GET_RDPU_CONF 0x2 u8 rsv[3]; __le32 reserved; __le32 addr_high; @@ -1726,6 +1920,8 @@ enum i40e_aq_phy_type { I40E_PHY_TYPE_10GBASE_CR1_CU = 0xB, I40E_PHY_TYPE_10GBASE_AOC = 0xC, I40E_PHY_TYPE_40GBASE_AOC = 0xD, + I40E_PHY_TYPE_UNRECOGNIZED = 0xE, + I40E_PHY_TYPE_UNSUPPORTED = 0xF, I40E_PHY_TYPE_100BASE_TX = 0x11, I40E_PHY_TYPE_1000BASE_T = 0x12, I40E_PHY_TYPE_10GBASE_T = 0x13, @@ -1744,24 +1940,74 @@ enum i40e_aq_phy_type { I40E_PHY_TYPE_25GBASE_CR = 0x20, I40E_PHY_TYPE_25GBASE_SR = 0x21, I40E_PHY_TYPE_25GBASE_LR = 0x22, - I40E_PHY_TYPE_MAX -}; - + I40E_PHY_TYPE_25GBASE_AOC = 0x23, + I40E_PHY_TYPE_25GBASE_ACC = 0x24, + I40E_PHY_TYPE_2_5GBASE_T = 0x30, + I40E_PHY_TYPE_5GBASE_T = 0x31, + I40E_PHY_TYPE_MAX, + I40E_PHY_TYPE_NOT_SUPPORTED_HIGH_TEMP = 0xFD, + I40E_PHY_TYPE_EMPTY = 0xFE, + I40E_PHY_TYPE_DEFAULT = 0xFF, +}; + +#define I40E_PHY_TYPES_BITMASK (BIT_ULL(I40E_PHY_TYPE_SGMII) | \ + BIT_ULL(I40E_PHY_TYPE_1000BASE_KX) | \ + BIT_ULL(I40E_PHY_TYPE_10GBASE_KX4) | \ + BIT_ULL(I40E_PHY_TYPE_10GBASE_KR) | \ + BIT_ULL(I40E_PHY_TYPE_40GBASE_KR4) | \ + BIT_ULL(I40E_PHY_TYPE_XAUI) | \ + BIT_ULL(I40E_PHY_TYPE_XFI) | \ + BIT_ULL(I40E_PHY_TYPE_SFI) | \ + BIT_ULL(I40E_PHY_TYPE_XLAUI) | \ + BIT_ULL(I40E_PHY_TYPE_XLPPI) | \ + BIT_ULL(I40E_PHY_TYPE_40GBASE_CR4_CU) | \ + BIT_ULL(I40E_PHY_TYPE_10GBASE_CR1_CU) | \ + BIT_ULL(I40E_PHY_TYPE_10GBASE_AOC) | \ + BIT_ULL(I40E_PHY_TYPE_40GBASE_AOC) | \ + BIT_ULL(I40E_PHY_TYPE_UNRECOGNIZED) | \ + BIT_ULL(I40E_PHY_TYPE_UNSUPPORTED) | \ + BIT_ULL(I40E_PHY_TYPE_100BASE_TX) | \ + BIT_ULL(I40E_PHY_TYPE_1000BASE_T) | \ + BIT_ULL(I40E_PHY_TYPE_10GBASE_T) | \ + BIT_ULL(I40E_PHY_TYPE_10GBASE_SR) | \ + BIT_ULL(I40E_PHY_TYPE_10GBASE_LR) | \ + BIT_ULL(I40E_PHY_TYPE_10GBASE_SFPP_CU) | \ + BIT_ULL(I40E_PHY_TYPE_10GBASE_CR1) | \ + BIT_ULL(I40E_PHY_TYPE_40GBASE_CR4) | \ + BIT_ULL(I40E_PHY_TYPE_40GBASE_SR4) | \ + BIT_ULL(I40E_PHY_TYPE_40GBASE_LR4) | \ + BIT_ULL(I40E_PHY_TYPE_1000BASE_SX) | \ + BIT_ULL(I40E_PHY_TYPE_1000BASE_LX) | \ + BIT_ULL(I40E_PHY_TYPE_1000BASE_T_OPTICAL) | \ + BIT_ULL(I40E_PHY_TYPE_20GBASE_KR2) | \ + BIT_ULL(I40E_PHY_TYPE_25GBASE_KR) | \ + BIT_ULL(I40E_PHY_TYPE_25GBASE_CR) | \ + BIT_ULL(I40E_PHY_TYPE_25GBASE_SR) | \ + BIT_ULL(I40E_PHY_TYPE_25GBASE_LR) | \ + BIT_ULL(I40E_PHY_TYPE_25GBASE_AOC) | \ + BIT_ULL(I40E_PHY_TYPE_25GBASE_ACC) | \ + BIT_ULL(I40E_PHY_TYPE_2_5GBASE_T) | \ + BIT_ULL(I40E_PHY_TYPE_5GBASE_T)) + +#define I40E_LINK_SPEED_2_5GB_SHIFT 0x0 #define I40E_LINK_SPEED_100MB_SHIFT 0x1 #define I40E_LINK_SPEED_1000MB_SHIFT 0x2 #define I40E_LINK_SPEED_10GB_SHIFT 0x3 #define I40E_LINK_SPEED_40GB_SHIFT 0x4 #define I40E_LINK_SPEED_20GB_SHIFT 0x5 #define I40E_LINK_SPEED_25GB_SHIFT 0x6 +#define I40E_LINK_SPEED_5GB_SHIFT 0x7 enum i40e_aq_link_speed { I40E_LINK_SPEED_UNKNOWN = 0, - I40E_LINK_SPEED_100MB = BIT(I40E_LINK_SPEED_100MB_SHIFT), - I40E_LINK_SPEED_1GB = BIT(I40E_LINK_SPEED_1000MB_SHIFT), - I40E_LINK_SPEED_10GB = BIT(I40E_LINK_SPEED_10GB_SHIFT), - I40E_LINK_SPEED_40GB = BIT(I40E_LINK_SPEED_40GB_SHIFT), - I40E_LINK_SPEED_20GB = BIT(I40E_LINK_SPEED_20GB_SHIFT), - I40E_LINK_SPEED_25GB = BIT(I40E_LINK_SPEED_25GB_SHIFT), + I40E_LINK_SPEED_100MB = (1 << I40E_LINK_SPEED_100MB_SHIFT), + I40E_LINK_SPEED_1GB = (1 << I40E_LINK_SPEED_1000MB_SHIFT), + I40E_LINK_SPEED_2_5GB = (1 << I40E_LINK_SPEED_2_5GB_SHIFT), + I40E_LINK_SPEED_5GB = (1 << I40E_LINK_SPEED_5GB_SHIFT), + I40E_LINK_SPEED_10GB = (1 << I40E_LINK_SPEED_10GB_SHIFT), + I40E_LINK_SPEED_40GB = (1 << I40E_LINK_SPEED_40GB_SHIFT), + I40E_LINK_SPEED_20GB = (1 << I40E_LINK_SPEED_20GB_SHIFT), + I40E_LINK_SPEED_25GB = (1 << I40E_LINK_SPEED_25GB_SHIFT), }; struct i40e_aqc_module_desc { @@ -1787,20 +2033,27 @@ struct i40e_aq_get_phy_abilities_resp { #define I40E_AQ_PHY_FEC_ABILITY_KR 0x40 #define I40E_AQ_PHY_FEC_ABILITY_RS 0x80 __le16 eee_capability; +#define I40E_AQ_EEE_AUTO 0x0001 #define I40E_AQ_EEE_100BASE_TX 0x0002 #define I40E_AQ_EEE_1000BASE_T 0x0004 #define I40E_AQ_EEE_10GBASE_T 0x0008 #define I40E_AQ_EEE_1000BASE_KX 0x0010 #define I40E_AQ_EEE_10GBASE_KX4 0x0020 #define I40E_AQ_EEE_10GBASE_KR 0x0040 +#define I40E_AQ_EEE_2_5GBASE_T 0x0100 +#define I40E_AQ_EEE_5GBASE_T 0x0200 __le32 eeer_val; u8 d3_lpan; #define I40E_AQ_SET_PHY_D3_LPAN_ENA 0x01 u8 phy_type_ext; -#define I40E_AQ_PHY_TYPE_EXT_25G_KR 0X01 -#define I40E_AQ_PHY_TYPE_EXT_25G_CR 0X02 +#define I40E_AQ_PHY_TYPE_EXT_25G_KR 0x01 +#define I40E_AQ_PHY_TYPE_EXT_25G_CR 0x02 #define I40E_AQ_PHY_TYPE_EXT_25G_SR 0x04 #define I40E_AQ_PHY_TYPE_EXT_25G_LR 0x08 +#define I40E_AQ_PHY_TYPE_EXT_25G_AOC 0x10 +#define I40E_AQ_PHY_TYPE_EXT_25G_ACC 0x20 +#define I40E_AQ_PHY_TYPE_EXT_2_5GBASE_T 0x40 +#define I40E_AQ_PHY_TYPE_EXT_5GBASE_T 0x80 u8 fec_cfg_curr_mod_ext_info; #define I40E_AQ_ENABLE_FEC_KR 0x01 #define I40E_AQ_ENABLE_FEC_RS 0x02 @@ -1834,10 +2087,6 @@ struct i40e_aq_set_phy_config { /* same bits as above in all */ __le32 eeer; u8 low_power_ctrl; u8 phy_type_ext; -#define I40E_AQ_PHY_TYPE_EXT_25G_KR 0X01 -#define I40E_AQ_PHY_TYPE_EXT_25G_CR 0X02 -#define I40E_AQ_PHY_TYPE_EXT_25G_SR 0x04 -#define I40E_AQ_PHY_TYPE_EXT_25G_LR 0x08 u8 fec_config; #define I40E_AQ_SET_FEC_ABILITY_KR BIT(0) #define I40E_AQ_SET_FEC_ABILITY_RS BIT(1) @@ -1855,20 +2104,21 @@ I40E_CHECK_CMD_LENGTH(i40e_aq_set_phy_config); struct i40e_aq_set_mac_config { __le16 max_frame_size; u8 params; -#define I40E_AQ_SET_MAC_CONFIG_CRC_EN 0x04 -#define I40E_AQ_SET_MAC_CONFIG_PACING_MASK 0x78 -#define I40E_AQ_SET_MAC_CONFIG_PACING_SHIFT 3 -#define I40E_AQ_SET_MAC_CONFIG_PACING_NONE 0x0 -#define I40E_AQ_SET_MAC_CONFIG_PACING_1B_13TX 0xF -#define I40E_AQ_SET_MAC_CONFIG_PACING_1DW_9TX 0x9 -#define I40E_AQ_SET_MAC_CONFIG_PACING_1DW_4TX 0x8 -#define I40E_AQ_SET_MAC_CONFIG_PACING_3DW_7TX 0x7 -#define I40E_AQ_SET_MAC_CONFIG_PACING_2DW_3TX 0x6 -#define I40E_AQ_SET_MAC_CONFIG_PACING_1DW_1TX 0x5 -#define I40E_AQ_SET_MAC_CONFIG_PACING_3DW_2TX 0x4 -#define I40E_AQ_SET_MAC_CONFIG_PACING_7DW_3TX 0x3 -#define I40E_AQ_SET_MAC_CONFIG_PACING_4DW_1TX 0x2 -#define I40E_AQ_SET_MAC_CONFIG_PACING_9DW_1TX 0x1 +#define I40E_AQ_SET_MAC_CONFIG_CRC_EN 0x04 +#define I40E_AQ_SET_MAC_CONFIG_PACING_MASK 0x78 +#define I40E_AQ_SET_MAC_CONFIG_PACING_SHIFT 3 +#define I40E_AQ_SET_MAC_CONFIG_PACING_NONE 0x0 +#define I40E_AQ_SET_MAC_CONFIG_PACING_1B_13TX 0xF +#define I40E_AQ_SET_MAC_CONFIG_PACING_1DW_9TX 0x9 +#define I40E_AQ_SET_MAC_CONFIG_PACING_1DW_4TX 0x8 +#define I40E_AQ_SET_MAC_CONFIG_PACING_3DW_7TX 0x7 +#define I40E_AQ_SET_MAC_CONFIG_PACING_2DW_3TX 0x6 +#define I40E_AQ_SET_MAC_CONFIG_PACING_1DW_1TX 0x5 +#define I40E_AQ_SET_MAC_CONFIG_PACING_3DW_2TX 0x4 +#define I40E_AQ_SET_MAC_CONFIG_PACING_7DW_3TX 0x3 +#define I40E_AQ_SET_MAC_CONFIG_PACING_4DW_1TX 0x2 +#define I40E_AQ_SET_MAC_CONFIG_PACING_9DW_1TX 0x1 +#define I40E_AQ_SET_MAC_CONFIG_DROP_BLOCKING_PACKET_EN 0x80 u8 tx_timer_priority; /* bitmap */ __le16 tx_timer_value; __le16 fc_refresh_threshold; @@ -1934,19 +2184,31 @@ struct i40e_aqc_get_link_status { #define I40E_AQ_25G_SERDES_UCODE_ERR 0X04 #define I40E_AQ_25G_NIMB_UCODE_ERR 0X05 u8 loopback; /* use defines from i40e_aqc_set_lb_mode */ +/* Since firmware API 1.7 loopback field keeps power class info as well */ +#define I40E_AQ_LOOPBACK_MASK 0x07 +#define I40E_AQ_PWR_CLASS_SHIFT_LB 6 +#define I40E_AQ_PWR_CLASS_MASK_LB (0x03 << I40E_AQ_PWR_CLASS_SHIFT_LB) __le16 max_frame_size; u8 config; #define I40E_AQ_CONFIG_FEC_KR_ENA 0x01 #define I40E_AQ_CONFIG_FEC_RS_ENA 0x02 #define I40E_AQ_CONFIG_CRC_ENA 0x04 #define I40E_AQ_CONFIG_PACING_MASK 0x78 - u8 power_desc; + union { + struct { + u8 power_desc; #define I40E_AQ_LINK_POWER_CLASS_1 0x00 #define I40E_AQ_LINK_POWER_CLASS_2 0x01 #define I40E_AQ_LINK_POWER_CLASS_3 0x02 #define I40E_AQ_LINK_POWER_CLASS_4 0x03 #define I40E_AQ_PWR_CLASS_MASK 0x03 - u8 reserved[4]; + u8 reserved[4]; + }; + struct { + u8 link_type[4]; + u8 link_type_ext; + }; + }; }; I40E_CHECK_CMD_LENGTH(i40e_aqc_get_link_status); @@ -1983,11 +2245,28 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_an_advt_reg); /* Set Loopback mode (0x0618) */ struct i40e_aqc_set_lb_mode { - __le16 lb_mode; + u8 lb_level; +#define I40E_AQ_LB_NONE 0 +#define I40E_AQ_LB_MAC 1 +#define I40E_AQ_LB_SERDES 2 +#define I40E_AQ_LB_PHY_INT 3 +#define I40E_AQ_LB_PHY_EXT 4 +#define I40E_AQ_LB_BASE_T_PCS 5 +#define I40E_AQ_LB_BASE_T_EXT 6 #define I40E_AQ_LB_PHY_LOCAL 0x01 #define I40E_AQ_LB_PHY_REMOTE 0x02 #define I40E_AQ_LB_MAC_LOCAL 0x04 - u8 reserved[14]; + u8 lb_type; +#define I40E_AQ_LB_LOCAL 0 +#define I40E_AQ_LB_FAR 0x01 + u8 speed; +#define I40E_AQ_LB_SPEED_NONE 0 +#define I40E_AQ_LB_SPEED_1G 1 +#define I40E_AQ_LB_SPEED_10G 2 +#define I40E_AQ_LB_SPEED_40G 3 +#define I40E_AQ_LB_SPEED_20G 4 + u8 force_speed; + u8 reserved[12]; }; I40E_CHECK_CMD_LENGTH(i40e_aqc_set_lb_mode); @@ -2004,7 +2283,7 @@ struct i40e_aqc_set_phy_debug { #define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SOFT 0x02 /* Disable link manageability on a single port */ #define I40E_AQ_PHY_DEBUG_DISABLE_LINK_FW 0x10 -/* Disable link manageability on all ports */ +/* Disable link manageability on all ports needs both bits 4 and 5 */ #define I40E_AQ_PHY_DEBUG_DISABLE_ALL_LINK_FW 0x20 u8 reserved[15]; }; @@ -2017,26 +2296,71 @@ enum i40e_aq_phy_reg_type { I40E_AQC_PHY_REG_EXERNAL_MODULE = 0x3 }; +#pragma pack(1) /* Run PHY Activity (0x0626) */ struct i40e_aqc_run_phy_activity { - __le16 activity_id; - u8 flags; - u8 reserved1; - __le32 control; - __le32 data; - u8 reserved2[4]; + u8 cmd_flags; + __le16 activity_id; +#define I40E_AQ_RUN_PHY_ACT_ID_USR_DFND 0x10 + u8 reserved; + union { + struct { + __le32 dnl_opcode; +#define I40E_AQ_RUN_PHY_ACT_DNL_OPCODE_GET_EEE_STAT_DUR 0x801a +#define I40E_AQ_RUN_PHY_ACT_DNL_OPCODE_GET_EEE_STAT 0x801b +#define I40E_AQ_RUN_PHY_ACT_DNL_OPCODE_GET_EEE_DUR 0x1801b + __le32 data; + u8 reserved2[4]; + } cmd; + struct { + __le32 cmd_status; +#define I40E_AQ_RUN_PHY_ACT_CMD_STAT_SUCC 0x4 +#define I40E_AQ_RUN_PHY_ACT_CMD_STAT_MASK 0xFFFF + __le32 data0; + __le32 data1; + } resp; + } params; }; +#pragma pack() I40E_CHECK_CMD_LENGTH(i40e_aqc_run_phy_activity); +/* Set PHY Register command (0x0628) */ +/* Get PHY Register command (0x0629) */ +struct i40e_aqc_phy_register_access { + u8 phy_interface; +#define I40E_AQ_PHY_REG_ACCESS_INTERNAL 0 +#define I40E_AQ_PHY_REG_ACCESS_EXTERNAL 1 +#define I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE 2 + u8 dev_addres; + u8 cmd_flags; +#define I40E_AQ_PHY_REG_ACCESS_DONT_CHANGE_QSFP_PAGE 0x01 +#define I40E_AQ_PHY_REG_ACCESS_SET_MDIO_IF_NUMBER 0x02 +#define I40E_AQ_PHY_REG_ACCESS_MDIO_IF_NUMBER_SHIFT 2 +#define I40E_AQ_PHY_REG_ACCESS_MDIO_IF_NUMBER_MASK (0x3 << \ + I40E_AQ_PHY_REG_ACCESS_MDIO_IF_NUMBER_SHIFT) + u8 reserved1; + __le32 reg_address; + __le32 reg_value; + u8 reserved2[4]; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_phy_register_access); + /* NVM Read command (indirect 0x0701) * NVM Erase commands (direct 0x0702) * NVM Update commands (indirect 0x0703) */ struct i40e_aqc_nvm_update { u8 command_flags; -#define I40E_AQ_NVM_LAST_CMD 0x01 -#define I40E_AQ_NVM_FLASH_ONLY 0x80 +#define I40E_AQ_NVM_LAST_CMD 0x01 +#define I40E_AQ_NVM_REARRANGE_TO_FLAT 0x20 +#define I40E_AQ_NVM_REARRANGE_TO_STRUCT 0x40 +#define I40E_AQ_NVM_FLASH_ONLY 0x80 +#define I40E_AQ_NVM_PRESERVATION_FLAGS_SHIFT 1 +#define I40E_AQ_NVM_PRESERVATION_FLAGS_MASK 0x03 +#define I40E_AQ_NVM_PRESERVATION_FLAGS_SELECTED 0x03 +#define I40E_AQ_NVM_PRESERVATION_FLAGS_ALL 0x01 u8 module_pointer; __le16 length; __le32 offset; @@ -2049,8 +2373,8 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_update); /* NVM Config Read (indirect 0x0704) */ struct i40e_aqc_nvm_config_read { __le16 cmd_flags; -#define I40E_AQ_ANVM_SINGLE_OR_MULTIPLE_FEATURES_MASK 1 -#define I40E_AQ_ANVM_READ_SINGLE_FEATURE 0 +#define I40E_AQ_ANVM_SINGLE_OR_MULTIPLE_FEATURES_MASK 1 +#define I40E_AQ_ANVM_READ_SINGLE_FEATURE 0 #define I40E_AQ_ANVM_READ_MULTIPLE_FEATURES 1 __le16 element_count; __le16 element_id; /* Feature/field ID */ @@ -2075,9 +2399,9 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_config_write); /* Used for 0x0704 as well as for 0x0705 commands */ #define I40E_AQ_ANVM_FEATURE_OR_IMMEDIATE_SHIFT 1 #define I40E_AQ_ANVM_FEATURE_OR_IMMEDIATE_MASK \ - BIT(I40E_AQ_ANVM_FEATURE_OR_IMMEDIATE_SHIFT) + (1 << I40E_AQ_ANVM_FEATURE_OR_IMMEDIATE_SHIFT) #define I40E_AQ_ANVM_FEATURE 0 -#define I40E_AQ_ANVM_IMMEDIATE_FIELD BIT(FEATURE_OR_IMMEDIATE_SHIFT) +#define I40E_AQ_ANVM_IMMEDIATE_FIELD (1 << FEATURE_OR_IMMEDIATE_SHIFT) struct i40e_aqc_nvm_config_data_feature { __le16 feature_id; #define I40E_AQ_ANVM_FEATURE_OPTION_OEM_ONLY 0x01 @@ -2279,23 +2603,35 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_update_tlv); /* Stop LLDP (direct 0x0A05) */ struct i40e_aqc_lldp_stop { u8 command; -#define I40E_AQ_LLDP_AGENT_STOP 0x0 -#define I40E_AQ_LLDP_AGENT_SHUTDOWN 0x1 +#define I40E_AQ_LLDP_AGENT_STOP 0x0 +#define I40E_AQ_LLDP_AGENT_SHUTDOWN 0x1 +#define I40E_AQ_LLDP_AGENT_STOP_PERSIST 0x2 u8 reserved[15]; }; I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_stop); /* Start LLDP (direct 0x0A06) */ - struct i40e_aqc_lldp_start { u8 command; -#define I40E_AQ_LLDP_AGENT_START 0x1 +#define I40E_AQ_LLDP_AGENT_START 0x1 +#define I40E_AQ_LLDP_AGENT_START_PERSIST 0x2 u8 reserved[15]; }; I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_start); +/* Set DCB (direct 0x0303) */ +struct i40e_aqc_set_dcb_parameters { + u8 command; +#define I40E_AQ_DCB_SET_AGENT 0x1 +#define I40E_DCB_VALID 0x1 + u8 valid_flags; + u8 reserved[14]; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_set_dcb_parameters); + /* Get CEE DCBX Oper Config (0x0A07) * uses the generic descriptor struct * returns below as indirect response @@ -2350,20 +2686,7 @@ struct i40e_aqc_get_cee_dcb_cfg_resp { u8 oper_tc_bw[8]; u8 oper_pfc_en; __le16 oper_app_prio; -#define I40E_AQC_CEE_APP_FCOE_SHIFT 0x0 -#define I40E_AQC_CEE_APP_FCOE_MASK (0x7 << I40E_AQC_CEE_APP_FCOE_SHIFT) -#define I40E_AQC_CEE_APP_ISCSI_SHIFT 0x3 -#define I40E_AQC_CEE_APP_ISCSI_MASK (0x7 << I40E_AQC_CEE_APP_ISCSI_SHIFT) -#define I40E_AQC_CEE_APP_FIP_SHIFT 0x8 -#define I40E_AQC_CEE_APP_FIP_MASK (0x7 << I40E_AQC_CEE_APP_FIP_SHIFT) -#define I40E_AQC_CEE_APP_FIP_MASK (0x7 << I40E_AQC_CEE_APP_FIP_SHIFT) __le32 tlv_status; -#define I40E_AQC_CEE_PG_STATUS_SHIFT 0x0 -#define I40E_AQC_CEE_PG_STATUS_MASK (0x7 << I40E_AQC_CEE_PG_STATUS_SHIFT) -#define I40E_AQC_CEE_PFC_STATUS_SHIFT 0x3 -#define I40E_AQC_CEE_PFC_STATUS_MASK (0x7 << I40E_AQC_CEE_PFC_STATUS_SHIFT) -#define I40E_AQC_CEE_APP_STATUS_SHIFT 0x8 -#define I40E_AQC_CEE_APP_STATUS_MASK (0x7 << I40E_AQC_CEE_APP_STATUS_SHIFT) u8 reserved[12]; }; @@ -2374,11 +2697,12 @@ I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_get_cee_dcb_cfg_resp); */ struct i40e_aqc_lldp_set_local_mib { #define SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT 0 -#define SET_LOCAL_MIB_AC_TYPE_DCBX_MASK BIT(SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT) +#define SET_LOCAL_MIB_AC_TYPE_DCBX_MASK (1 << \ + SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT) #define SET_LOCAL_MIB_AC_TYPE_LOCAL_MIB 0x0 #define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_SHIFT (1) -#define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_MASK \ - BIT(SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_SHIFT) +#define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_MASK (1 << \ + SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_SHIFT) #define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS 0x1 u8 type; u8 reserved0; @@ -2390,19 +2714,37 @@ struct i40e_aqc_lldp_set_local_mib { I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_set_local_mib); +struct i40e_aqc_lldp_set_local_mib_resp { +#define SET_LOCAL_MIB_RESP_EVENT_TRIGGERED_MASK 0x01 + u8 status; + u8 reserved[15]; +}; + +I40E_CHECK_STRUCT_LEN(0x10, i40e_aqc_lldp_set_local_mib_resp); + /* Stop/Start LLDP Agent (direct 0x0A09) * Used for stopping/starting specific LLDP agent. e.g. DCBx */ struct i40e_aqc_lldp_stop_start_specific_agent { #define I40E_AQC_START_SPECIFIC_AGENT_SHIFT 0 #define I40E_AQC_START_SPECIFIC_AGENT_MASK \ - BIT(I40E_AQC_START_SPECIFIC_AGENT_SHIFT) + (1 << I40E_AQC_START_SPECIFIC_AGENT_SHIFT) u8 command; u8 reserved[15]; }; I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_stop_start_specific_agent); +/* Restore LLDP Agent factory settings (direct 0x0A0A) */ +struct i40e_aqc_lldp_restore { + u8 command; +#define I40E_AQ_LLDP_AGENT_RESTORE_NOT 0x0 +#define I40E_AQ_LLDP_AGENT_RESTORE 0x1 + u8 reserved[15]; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_restore); + /* Add Udp Tunnel command and completion (direct 0x0B00) */ struct i40e_aqc_add_udp_tunnel { __le16 udp_port; @@ -2449,7 +2791,7 @@ struct i40e_aqc_del_udp_tunnel_completion { I40E_CHECK_CMD_LENGTH(i40e_aqc_del_udp_tunnel_completion); struct i40e_aqc_get_set_rss_key { -#define I40E_AQC_SET_RSS_KEY_VSI_VALID BIT(15) +#define I40E_AQC_SET_RSS_KEY_VSI_VALID (0x1 << 15) #define I40E_AQC_SET_RSS_KEY_VSI_ID_SHIFT 0 #define I40E_AQC_SET_RSS_KEY_VSI_ID_MASK (0x3FF << \ I40E_AQC_SET_RSS_KEY_VSI_ID_SHIFT) @@ -2469,13 +2811,14 @@ struct i40e_aqc_get_set_rss_key_data { I40E_CHECK_STRUCT_LEN(0x34, i40e_aqc_get_set_rss_key_data); struct i40e_aqc_get_set_rss_lut { -#define I40E_AQC_SET_RSS_LUT_VSI_VALID BIT(15) +#define I40E_AQC_SET_RSS_LUT_VSI_VALID (0x1 << 15) #define I40E_AQC_SET_RSS_LUT_VSI_ID_SHIFT 0 #define I40E_AQC_SET_RSS_LUT_VSI_ID_MASK (0x3FF << \ I40E_AQC_SET_RSS_LUT_VSI_ID_SHIFT) __le16 vsi_id; #define I40E_AQC_SET_RSS_LUT_TABLE_TYPE_SHIFT 0 -#define I40E_AQC_SET_RSS_LUT_TABLE_TYPE_MASK BIT(I40E_AQC_SET_RSS_LUT_TABLE_TYPE_SHIFT) +#define I40E_AQC_SET_RSS_LUT_TABLE_TYPE_MASK (0x1 << \ + I40E_AQC_SET_RSS_LUT_TABLE_TYPE_SHIFT) #define I40E_AQC_SET_RSS_LUT_TABLE_TYPE_VSI 0 #define I40E_AQC_SET_RSS_LUT_TABLE_TYPE_PF 1 diff --git a/drivers/net/ethernet/intel/i40e/i40e_alloc.h b/drivers/net/ethernet/intel/i40e/i40e_alloc.h index 926811ad4..d7feb645f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_alloc.h +++ b/drivers/net/ethernet/intel/i40e/i40e_alloc.h @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_ALLOC_H_ #define _I40E_ALLOC_H_ diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c index 1b1e2acbd..2877a1203 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.c +++ b/drivers/net/ethernet/intel/i40e/i40e_client.c @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2017 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #include #include @@ -53,17 +30,23 @@ static int i40e_client_update_vsi_ctxt(struct i40e_info *ldev, bool is_vf, u32 vf_id, u32 flag, u32 valid_flag); +static int i40e_client_device_register(struct i40e_info *ldev); + +static void i40e_client_device_unregister(struct i40e_info *ldev); + static struct i40e_ops i40e_lan_ops = { .virtchnl_send = i40e_client_virtchnl_send, .setup_qvlist = i40e_client_setup_qvlist, .request_reset = i40e_client_request_reset, .update_vsi_ctxt = i40e_client_update_vsi_ctxt, + .client_device_register = i40e_client_device_register, + .client_device_unregister = i40e_client_device_unregister, }; /** * i40e_client_get_params - Get the params that can change at runtime * @vsi: the VSI with the message - * @param: clinet param struct + * @params: clinet param struct * **/ static @@ -94,6 +77,10 @@ int i40e_client_get_params(struct i40e_vsi *vsi, struct i40e_params *params) return 0; } +static void i40e_client_device_release(struct device *dev) +{ +} + /** * i40e_notify_client_of_vf_msg - call the client vf message callback * @vsi: the VSI with the message @@ -287,21 +274,27 @@ out: return capable; } +void i40e_client_update_msix_info(struct i40e_pf *pf) +{ + struct i40e_client_instance *cdev = pf->cinst; + + if (!cdev || !cdev->client) + return; + + cdev->lan_info.msix_count = pf->num_iwarp_msix; + cdev->lan_info.msix_entries = &pf->msix_entries[pf->iwarp_base_vector]; +} + /** * i40e_client_add_instance - add a client instance struct to the instance list * @pf: pointer to the board struct - * @client: pointer to a client struct in the client list. - * @existing: if there was already an existing instance - * **/ static void i40e_client_add_instance(struct i40e_pf *pf) { struct i40e_client_instance *cdev = NULL; struct netdev_hw_addr *mac = NULL; struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; - - if (!registered_client || pf->cinst) - return; + struct platform_device *platform_dev; cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); if (!cdev) @@ -320,6 +313,12 @@ static void i40e_client_add_instance(struct i40e_pf *pf) cdev->lan_info.fw_maj_ver = pf->hw.aq.fw_maj_ver; cdev->lan_info.fw_min_ver = pf->hw.aq.fw_min_ver; cdev->lan_info.fw_build = pf->hw.aq.fw_build; + platform_dev = &cdev->lan_info.platform_dev; + platform_dev->name = "i40e_rdma"; + platform_dev->id = PLATFORM_DEVID_AUTO; + platform_dev->id_auto = true; + platform_dev->dev.release = i40e_client_device_release; + platform_dev->dev.parent = &pf->pdev->dev; set_bit(__I40E_CLIENT_INSTANCE_NONE, &cdev->state); if (i40e_client_get_params(vsi, &cdev->lan_info.params)) { @@ -328,9 +327,6 @@ static void i40e_client_add_instance(struct i40e_pf *pf) return; } - cdev->lan_info.msix_count = pf->num_iwarp_msix; - cdev->lan_info.msix_entries = &pf->msix_entries[pf->iwarp_base_vector]; - mac = list_first_entry(&cdev->lan_info.netdev->dev_addrs.list, struct netdev_hw_addr, list); if (mac) @@ -338,8 +334,12 @@ static void i40e_client_add_instance(struct i40e_pf *pf) else dev_err(&pf->pdev->dev, "MAC address list is empty!\n"); - cdev->client = registered_client; + cdev->client = NULL; pf->cinst = cdev; + + cdev->lan_info.msix_count = pf->num_iwarp_msix; + cdev->lan_info.msix_entries = &pf->msix_entries[pf->iwarp_base_vector]; + platform_device_register(platform_dev); } /** @@ -360,14 +360,13 @@ void i40e_client_del_instance(struct i40e_pf *pf) **/ void i40e_client_subtask(struct i40e_pf *pf) { - struct i40e_client *client = registered_client; + struct i40e_client *client; struct i40e_client_instance *cdev; struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; int ret = 0; - if (!(pf->flags & I40E_FLAG_SERVICE_CLIENT_REQUESTED)) + if (!test_and_clear_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state)) return; - pf->flags &= ~I40E_FLAG_SERVICE_CLIENT_REQUESTED; cdev = pf->cinst; /* If we're down or resetting, just bail */ @@ -375,14 +374,16 @@ void i40e_client_subtask(struct i40e_pf *pf) test_bit(__I40E_CONFIG_BUSY, pf->state)) return; - if (!client || !cdev) + if (!cdev || !cdev->client) return; - /* Here we handle client opens. If the client is down, but - * the netdev is up, then open the client. + client = cdev->client; + + /* Here we handle client opens. If the client is down, and + * the netdev is registered, then open the client. */ if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { - if (!test_bit(__I40E_VSI_DOWN, vsi->state) && + if (vsi->netdev_registered && client->ops && client->ops->open) { set_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); ret = client->ops->open(&cdev->lan_info, client); @@ -393,17 +394,19 @@ void i40e_client_subtask(struct i40e_pf *pf) i40e_client_del_instance(pf); } } - } else { - /* Likewise for client close. If the client is up, but the netdev - * is down, then close the client. - */ - if (test_bit(__I40E_VSI_DOWN, vsi->state) && - client->ops && client->ops->close) { - clear_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); - client->ops->close(&cdev->lan_info, client, false); - i40e_client_release_qvlist(&cdev->lan_info); - } } + + /* enable/disable PE TCP_ENA flag based on netdev down/up + */ + if (test_bit(__I40E_VSI_DOWN, vsi->state)) + i40e_client_update_vsi_ctxt(&cdev->lan_info, client, + 0, 0, 0, + I40E_CLIENT_VSI_FLAG_TCP_ENABLE); + else + i40e_client_update_vsi_ctxt(&cdev->lan_info, client, + 0, 0, + I40E_CLIENT_VSI_FLAG_TCP_ENABLE, + I40E_CLIENT_VSI_FLAG_TCP_ENABLE); } /** @@ -436,17 +439,8 @@ int i40e_lan_add_device(struct i40e_pf *pf) pf->hw.pf_id, pf->hw.bus.bus_id, pf->hw.bus.device, pf->hw.bus.func); - /* If a client has already been registered, we need to add an instance - * of it to our new LAN device. - */ - if (registered_client) - i40e_client_add_instance(pf); - - /* Since in some cases register may have happened before a device gets - * added, we can schedule a subtask to go initiate the clients if - * they can be launched at probe time. - */ - pf->flags |= I40E_FLAG_SERVICE_CLIENT_REQUESTED; + i40e_client_add_instance(pf); + set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state); i40e_service_event_schedule(pf); out: @@ -465,6 +459,8 @@ int i40e_lan_del_device(struct i40e_pf *pf) struct i40e_device *ldev, *tmp; int ret = -ENODEV; + platform_device_unregister(&pf->cinst->lan_info.platform_dev); + /* First, remove any client instance. */ i40e_client_del_instance(pf); @@ -480,6 +476,7 @@ int i40e_lan_del_device(struct i40e_pf *pf) break; } } + mutex_unlock(&i40e_device_mutex); return ret; } @@ -517,10 +514,7 @@ static void i40e_client_release(struct i40e_client *client) "Client %s instance for PF id %d closed\n", client->name, pf->hw.pf_id); } - /* delete the client instance */ - i40e_client_del_instance(pf); - dev_info(&pf->pdev->dev, "Deleted client instance of Client %s\n", - client->name); + cdev->client = NULL; clear_bit(__I40E_SERVICE_SCHED, pf->state); } mutex_unlock(&i40e_device_mutex); @@ -539,9 +533,9 @@ static void i40e_client_prepare(struct i40e_client *client) mutex_lock(&i40e_device_mutex); list_for_each_entry(ldev, &i40e_devices, list) { pf = ldev->pf; - i40e_client_add_instance(pf); + pf->cinst->client = registered_client; /* Start the client subtask */ - pf->flags |= I40E_FLAG_SERVICE_CLIENT_REQUESTED; + set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state); i40e_service_event_schedule(pf); } mutex_unlock(&i40e_device_mutex); @@ -566,7 +560,7 @@ static int i40e_client_virtchnl_send(struct i40e_info *ldev, i40e_status err; err = i40e_aq_send_msg_to_vf(hw, vf_id, VIRTCHNL_OP_IWARP, - 0, msg, len, NULL); + I40E_SUCCESS, msg, len, NULL); if (err) dev_err(&pf->pdev->dev, "Unable to send iWarp message to VF, error %d, aq status %d\n", err, hw->aq.asq_last_status); @@ -578,7 +572,7 @@ static int i40e_client_virtchnl_send(struct i40e_info *ldev, * i40e_client_setup_qvlist * @ldev: pointer to L2 context. * @client: Client pointer. - * @qv_info: queue and vector list + * @qvlist_info: queue and vector list * * Return 0 on success or < 0 on error **/ @@ -653,7 +647,7 @@ err: * i40e_client_request_reset * @ldev: pointer to L2 context. * @client: Client pointer. - * @level: reset level + * @reset_level: reset level **/ static void i40e_client_request_reset(struct i40e_info *ldev, struct i40e_client *client, @@ -717,13 +711,13 @@ static int i40e_client_update_vsi_ctxt(struct i40e_info *ldev, return -ENOENT; } - if ((valid_flag & I40E_CLIENT_VSI_FLAG_TCP_PACKET_ENABLE) && - (flag & I40E_CLIENT_VSI_FLAG_TCP_PACKET_ENABLE)) { + if ((valid_flag & I40E_CLIENT_VSI_FLAG_TCP_ENABLE) && + (flag & I40E_CLIENT_VSI_FLAG_TCP_ENABLE)) { ctxt.info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_QUEUE_OPT_VALID); ctxt.info.queueing_opt_flags |= I40E_AQ_VSI_QUE_OPT_TCP_ENA; - } else if ((valid_flag & I40E_CLIENT_VSI_FLAG_TCP_PACKET_ENABLE) && - !(flag & I40E_CLIENT_VSI_FLAG_TCP_PACKET_ENABLE)) { + } else if ((valid_flag & I40E_CLIENT_VSI_FLAG_TCP_ENABLE) && + !(flag & I40E_CLIENT_VSI_FLAG_TCP_ENABLE)) { ctxt.info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_QUEUE_OPT_VALID); ctxt.info.queueing_opt_flags &= ~I40E_AQ_VSI_QUE_OPT_TCP_ENA; @@ -747,6 +741,66 @@ static int i40e_client_update_vsi_ctxt(struct i40e_info *ldev, return err; } +static int i40e_client_device_register(struct i40e_info *ldev) +{ + struct i40e_client *client; + struct i40e_pf *pf; + + if (!ldev) { + pr_err("Failed to reg client dev: ldev ptr NULL\n"); + return -EINVAL; + } + + client = ldev->client; + pf = ldev->pf; + if (!client) { + pr_err("Failed to reg client dev: client ptr NULL\n"); + return -EINVAL; + } + + if (!ldev->ops || !client->ops) { + pr_err("Failed to reg client dev: client dev peer_ops/ops NULL\n"); + return -EINVAL; + } + + if (client->version.major != I40E_CLIENT_VERSION_MAJOR || + client->version.minor != I40E_CLIENT_VERSION_MINOR) { + pr_err("i40e: Failed to register client %s due to mismatched client interface version\n", + client->name); + pr_err("Client is using version: %02d.%02d.%02d while LAN driver supports %s\n", + client->version.major, client->version.minor, + client->version.build, + i40e_client_interface_version_str); + return -EINVAL; + } + + pf->cinst->client = ldev->client; + set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state); + i40e_service_event_schedule(pf); + + return 0; +} + +static void i40e_client_device_unregister(struct i40e_info *ldev) +{ + struct i40e_pf *pf = ldev->pf; + struct i40e_client_instance *cdev = pf->cinst; + + while (test_and_set_bit(__I40E_SERVICE_SCHED, pf->state)) + usleep_range(500, 1000); + + if (!cdev || !cdev->client || !cdev->client->ops || + !cdev->client->ops->close) { + dev_err(&pf->pdev->dev, "Cannot close client device\n"); + return; + } + cdev->client->ops->close(&cdev->lan_info, cdev->client, false); + clear_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); + i40e_client_release_qvlist(&cdev->lan_info); + pf->cinst->client = NULL; + clear_bit(__I40E_SERVICE_SCHED, pf->state); +} + /** * i40e_register_client - Register a i40e client driver with the L2 driver * @client: pointer to the i40e_client struct @@ -791,7 +845,7 @@ int i40e_register_client(struct i40e_client *client) i40e_client_prepare(client); - pr_info("i40e: Registered client %s\n", client->name); + pr_info("i40e: Registered client %s\n", client->name); out: return ret; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.h b/drivers/net/ethernet/intel/i40e/i40e_client.h index 15b21a531..40041ad26 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.h +++ b/drivers/net/ethernet/intel/i40e/i40e_client.h @@ -1,32 +1,11 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2015 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_CLIENT_H_ #define _I40E_CLIENT_H_ +#include + #define I40E_CLIENT_STR_LENGTH 10 /* Client interface version should be updated anytime there is a change in the @@ -128,11 +107,14 @@ struct i40e_info { u16 fw_maj_ver; /* firmware major version */ u16 fw_min_ver; /* firmware minor version */ u32 fw_build; /* firmware build number */ + + struct platform_device platform_dev; + struct i40e_client *client; }; #define I40E_CLIENT_RESET_LEVEL_PF 1 #define I40E_CLIENT_RESET_LEVEL_CORE 2 -#define I40E_CLIENT_VSI_FLAG_TCP_PACKET_ENABLE BIT(1) +#define I40E_CLIENT_VSI_FLAG_TCP_ENABLE BIT(1) struct i40e_ops { /* setup_q_vector_list enables queues with a particular vector */ @@ -155,6 +137,10 @@ struct i40e_ops { struct i40e_client *client, bool is_vf, u32 vf_id, u32 flag, u32 valid_flag); + + int (*client_device_register)(struct i40e_info *ldev); + + void (*client_device_unregister)(struct i40e_info *ldev); }; struct i40e_client_ops { @@ -211,7 +197,8 @@ struct i40e_client { #define I40E_TX_FLAGS_NOTIFY_OTHER_EVENTS BIT(2) u8 type; #define I40E_CLIENT_IWARP 0 - const struct i40e_client_ops *ops; /* client ops provided by the client */ + /* client ops provided by the client */ + const struct i40e_client_ops *ops; }; static inline bool i40e_client_is_registered(struct i40e_client *client) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 111426ba5..69fd8cc2e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1,33 +1,10 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2016 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #include "i40e_type.h" #include "i40e_adminq.h" #include "i40e_prototype.h" -#include +#include "virtchnl.h" /** * i40e_set_mac_type - Sets MAC type @@ -36,9 +13,9 @@ * This function sets the mac type of the adapter based on the * vendor ID and device ID stored in the hw structure. **/ -static i40e_status i40e_set_mac_type(struct i40e_hw *hw) +i40e_status i40e_set_mac_type(struct i40e_hw *hw) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; if (hw->vendor_id == PCI_VENDOR_ID_INTEL) { switch (hw->device_id) { @@ -51,10 +28,16 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw) case I40E_DEV_ID_QSFP_C: case I40E_DEV_ID_10G_BASE_T: case I40E_DEV_ID_10G_BASE_T4: + case I40E_DEV_ID_10G_BASE_T_BC: + case I40E_DEV_ID_10G_B: + case I40E_DEV_ID_10G_SFP: + case I40E_DEV_ID_5G_BASE_T_BC: case I40E_DEV_ID_20G_KR2: case I40E_DEV_ID_20G_KR2_A: case I40E_DEV_ID_25G_B: case I40E_DEV_ID_25G_SFP28: + case I40E_DEV_ID_X710_N3000: + case I40E_DEV_ID_XXV710_N3000: hw->mac.type = I40E_MAC_XL710; break; case I40E_DEV_ID_KX_X722: @@ -146,7 +129,7 @@ const char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) const char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err) { switch (stat_err) { - case 0: + case I40E_SUCCESS: return "OK"; case I40E_ERR_NVM: return "I40E_ERR_NVM"; @@ -278,6 +261,8 @@ const char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err) return "I40E_NOT_SUPPORTED"; case I40E_ERR_FIRMWARE_API_VERSION: return "I40E_ERR_FIRMWARE_API_VERSION"; + case I40E_ERR_ADMIN_QUEUE_CRITICAL_ERROR: + return "I40E_ERR_ADMIN_QUEUE_CRITICAL_ERROR"; } snprintf(hw->err_str, sizeof(hw->err_str), "%d", stat_err); @@ -298,47 +283,48 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc, void *buffer, u16 buf_len) { struct i40e_aq_desc *aq_desc = (struct i40e_aq_desc *)desc; - u16 len; + u32 effective_mask = hw->debug_mask & mask; u8 *buf = (u8 *)buffer; + u16 len; + char prefix[27]; - if ((!(mask & hw->debug_mask)) || (desc == NULL)) + if (!effective_mask || !desc) return; - len = le16_to_cpu(aq_desc->datalen); + len = LE16_TO_CPU(aq_desc->datalen); - i40e_debug(hw, mask, + i40e_debug(hw, mask & I40E_DEBUG_AQ_DESCRIPTOR, "AQ CMD: opcode 0x%04X, flags 0x%04X, datalen 0x%04X, retval 0x%04X\n", - le16_to_cpu(aq_desc->opcode), - le16_to_cpu(aq_desc->flags), - le16_to_cpu(aq_desc->datalen), - le16_to_cpu(aq_desc->retval)); - i40e_debug(hw, mask, "\tcookie (h,l) 0x%08X 0x%08X\n", - le32_to_cpu(aq_desc->cookie_high), - le32_to_cpu(aq_desc->cookie_low)); - i40e_debug(hw, mask, "\tparam (0,1) 0x%08X 0x%08X\n", - le32_to_cpu(aq_desc->params.internal.param0), - le32_to_cpu(aq_desc->params.internal.param1)); - i40e_debug(hw, mask, "\taddr (h,l) 0x%08X 0x%08X\n", - le32_to_cpu(aq_desc->params.external.addr_high), - le32_to_cpu(aq_desc->params.external.addr_low)); - - if ((buffer != NULL) && (aq_desc->datalen != 0)) { + LE16_TO_CPU(aq_desc->opcode), + LE16_TO_CPU(aq_desc->flags), + LE16_TO_CPU(aq_desc->datalen), + LE16_TO_CPU(aq_desc->retval)); + i40e_debug(hw, mask & I40E_DEBUG_AQ_DESCRIPTOR, + "\tcookie (h,l) 0x%08X 0x%08X\n", + LE32_TO_CPU(aq_desc->cookie_high), + LE32_TO_CPU(aq_desc->cookie_low)); + i40e_debug(hw, mask & I40E_DEBUG_AQ_DESCRIPTOR, + "\tparam (0,1) 0x%08X 0x%08X\n", + LE32_TO_CPU(aq_desc->params.internal.param0), + LE32_TO_CPU(aq_desc->params.internal.param1)); + i40e_debug(hw, mask & I40E_DEBUG_AQ_DESCRIPTOR, + "\taddr (h,l) 0x%08X 0x%08X\n", + LE32_TO_CPU(aq_desc->params.external.addr_high), + LE32_TO_CPU(aq_desc->params.external.addr_low)); + + if (buffer && (buf_len != 0) && (len != 0) && + (effective_mask & I40E_DEBUG_AQ_DESC_BUFFER)) { i40e_debug(hw, mask, "AQ CMD Buffer:\n"); if (buf_len < len) len = buf_len; - /* write the full 16-byte chunks */ - if (hw->debug_mask & mask) { - char prefix[27]; - - snprintf(prefix, sizeof(prefix), - "i40e %02x:%02x.%x: \t0x", - hw->bus.bus_id, - hw->bus.device, - hw->bus.func); - - print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET, - 16, 1, buf, len, false); - } + snprintf(prefix, sizeof(prefix), + "i40e %02x:%02x.%x: \t0x", + hw->bus.bus_id, + hw->bus.device, + hw->bus.func); + + print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET, + 16, 1, buf, len, false); } } @@ -352,9 +338,8 @@ bool i40e_check_asq_alive(struct i40e_hw *hw) { if (hw->aq.asq.len) return !!(rd32(hw, hw->aq.asq.len) & - I40E_PF_ATQLEN_ATQENABLE_MASK); - else - return false; + I40E_PF_ATQLEN_ATQENABLE_MASK); + return false; } /** @@ -377,7 +362,7 @@ i40e_status i40e_aq_queue_shutdown(struct i40e_hw *hw, i40e_aqc_opc_queue_shutdown); if (unloading) - cmd->driver_unloading = cpu_to_le32(I40E_AQ_DRIVER_UNLOADING); + cmd->driver_unloading = CPU_TO_LE32(I40E_AQ_DRIVER_UNLOADING); status = i40e_asq_send_command(hw, &desc, NULL, 0, NULL); return status; @@ -395,9 +380,9 @@ i40e_status i40e_aq_queue_shutdown(struct i40e_hw *hw, * Internal function to get or set RSS look up table **/ static i40e_status i40e_aq_get_set_rss_lut(struct i40e_hw *hw, - u16 vsi_id, bool pf_lut, - u8 *lut, u16 lut_size, - bool set) + u16 vsi_id, bool pf_lut, + u8 *lut, u16 lut_size, + bool set) { i40e_status status; struct i40e_aq_desc desc; @@ -412,22 +397,22 @@ static i40e_status i40e_aq_get_set_rss_lut(struct i40e_hw *hw, i40e_aqc_opc_get_rss_lut); /* Indirect command */ - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_RD); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_RD); cmd_resp->vsi_id = - cpu_to_le16((u16)((vsi_id << + CPU_TO_LE16((u16)((vsi_id << I40E_AQC_SET_RSS_LUT_VSI_ID_SHIFT) & I40E_AQC_SET_RSS_LUT_VSI_ID_MASK)); - cmd_resp->vsi_id |= cpu_to_le16((u16)I40E_AQC_SET_RSS_LUT_VSI_VALID); + cmd_resp->vsi_id |= CPU_TO_LE16((u16)I40E_AQC_SET_RSS_LUT_VSI_VALID); if (pf_lut) - cmd_resp->flags |= cpu_to_le16((u16) + cmd_resp->flags |= CPU_TO_LE16((u16) ((I40E_AQC_SET_RSS_LUT_TABLE_TYPE_PF << I40E_AQC_SET_RSS_LUT_TABLE_TYPE_SHIFT) & I40E_AQC_SET_RSS_LUT_TABLE_TYPE_MASK)); else - cmd_resp->flags |= cpu_to_le16((u16) + cmd_resp->flags |= CPU_TO_LE16((u16) ((I40E_AQC_SET_RSS_LUT_TABLE_TYPE_VSI << I40E_AQC_SET_RSS_LUT_TABLE_TYPE_SHIFT) & I40E_AQC_SET_RSS_LUT_TABLE_TYPE_MASK)); @@ -448,7 +433,7 @@ static i40e_status i40e_aq_get_set_rss_lut(struct i40e_hw *hw, * get the RSS lookup table, PF or VSI type **/ i40e_status i40e_aq_get_rss_lut(struct i40e_hw *hw, u16 vsi_id, - bool pf_lut, u8 *lut, u16 lut_size) + bool pf_lut, u8 *lut, u16 lut_size) { return i40e_aq_get_set_rss_lut(hw, vsi_id, pf_lut, lut, lut_size, false); @@ -465,7 +450,7 @@ i40e_status i40e_aq_get_rss_lut(struct i40e_hw *hw, u16 vsi_id, * set the RSS lookup table, PF or VSI type **/ i40e_status i40e_aq_set_rss_lut(struct i40e_hw *hw, u16 vsi_id, - bool pf_lut, u8 *lut, u16 lut_size) + bool pf_lut, u8 *lut, u16 lut_size) { return i40e_aq_get_set_rss_lut(hw, vsi_id, pf_lut, lut, lut_size, true); } @@ -498,14 +483,14 @@ static i40e_status i40e_aq_get_set_rss_key(struct i40e_hw *hw, i40e_aqc_opc_get_rss_key); /* Indirect command */ - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_RD); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_RD); cmd_resp->vsi_id = - cpu_to_le16((u16)((vsi_id << + CPU_TO_LE16((u16)((vsi_id << I40E_AQC_SET_RSS_KEY_VSI_ID_SHIFT) & I40E_AQC_SET_RSS_KEY_VSI_ID_MASK)); - cmd_resp->vsi_id |= cpu_to_le16((u16)I40E_AQC_SET_RSS_KEY_VSI_VALID); + cmd_resp->vsi_id |= CPU_TO_LE16((u16)I40E_AQC_SET_RSS_KEY_VSI_VALID); status = i40e_asq_send_command(hw, &desc, key, key_size, NULL); @@ -520,8 +505,8 @@ static i40e_status i40e_aq_get_set_rss_key(struct i40e_hw *hw, * **/ i40e_status i40e_aq_get_rss_key(struct i40e_hw *hw, - u16 vsi_id, - struct i40e_aqc_get_set_rss_key_data *key) + u16 vsi_id, + struct i40e_aqc_get_set_rss_key_data *key) { return i40e_aq_get_set_rss_key(hw, vsi_id, key, false); } @@ -535,8 +520,8 @@ i40e_status i40e_aq_get_rss_key(struct i40e_hw *hw, * set the RSS key per VSI **/ i40e_status i40e_aq_set_rss_key(struct i40e_hw *hw, - u16 vsi_id, - struct i40e_aqc_get_set_rss_key_data *key) + u16 vsi_id, + struct i40e_aqc_get_set_rss_key_data *key) { return i40e_aq_get_set_rss_key(hw, vsi_id, key, true); } @@ -920,7 +905,7 @@ struct i40e_rx_ptype_decoded i40e_ptype_lookup[] = { **/ i40e_status i40e_init_shared_code(struct i40e_hw *hw) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; u32 port, ari, func_rid; i40e_set_mac_type(hw); @@ -947,8 +932,16 @@ i40e_status i40e_init_shared_code(struct i40e_hw *hw) else hw->pf_id = (u8)(func_rid & 0x7); - if (hw->mac.type == I40E_MAC_X722) - hw->flags |= I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE; + /* NVMUpdate features structure initialization */ + hw->nvmupd_features.major = I40E_NVMUPD_FEATURES_API_VER_MAJOR; + hw->nvmupd_features.minor = I40E_NVMUPD_FEATURES_API_VER_MINOR; + hw->nvmupd_features.size = sizeof(hw->nvmupd_features); + i40e_memset(hw->nvmupd_features.features, 0x0, + I40E_NVMUPD_FEATURES_API_FEATURES_ARRAY_LEN * + sizeof(*hw->nvmupd_features.features), + I40E_NONDMA_MEM); + + hw->nvmupd_features.features[0] = I40E_NVMUPD_FEATURE_FLAT_NVM_SUPPORT; status = i40e_init_nvm(hw); return status; @@ -972,11 +965,11 @@ static i40e_status i40e_aq_mac_address_read(struct i40e_hw *hw, i40e_status status; i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_mac_address_read); - desc.flags |= cpu_to_le16(I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16(I40E_AQ_FLAG_BUF); status = i40e_asq_send_command(hw, &desc, addrs, sizeof(*addrs), cmd_details); - *flags = le16_to_cpu(cmd_data->command_flags); + *flags = LE16_TO_CPU(cmd_data->command_flags); return status; } @@ -999,9 +992,9 @@ i40e_status i40e_aq_mac_address_write(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_mac_address_write); - cmd_data->command_flags = cpu_to_le16(flags); - cmd_data->mac_sah = cpu_to_le16((u16)mac_addr[0] << 8 | mac_addr[1]); - cmd_data->mac_sal = cpu_to_le32(((u32)mac_addr[2] << 24) | + cmd_data->command_flags = CPU_TO_LE16(flags); + cmd_data->mac_sah = CPU_TO_LE16((u16)mac_addr[0] << 8 | mac_addr[1]); + cmd_data->mac_sal = CPU_TO_LE32(((u32)mac_addr[2] << 24) | ((u32)mac_addr[3] << 16) | ((u32)mac_addr[4] << 8) | mac_addr[5]); @@ -1060,7 +1053,7 @@ i40e_status i40e_get_port_mac_addr(struct i40e_hw *hw, u8 *mac_addr) /** * i40e_pre_tx_queue_cfg - pre tx queue configure * @hw: pointer to the HW structure - * @queue: target PF queue index + * @queue: target pf queue index * @enable: state change request * * Handles hw requirement to indicate intention to enable @@ -1098,28 +1091,28 @@ void i40e_pre_tx_queue_cfg(struct i40e_hw *hw, u32 queue, bool enable) * Reads the part number string from the EEPROM. **/ i40e_status i40e_read_pba_string(struct i40e_hw *hw, u8 *pba_num, - u32 pba_num_size) + u32 pba_num_size) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; u16 pba_word = 0; u16 pba_size = 0; u16 pba_ptr = 0; u16 i = 0; status = i40e_read_nvm_word(hw, I40E_SR_PBA_FLAGS, &pba_word); - if (status || (pba_word != 0xFAFA)) { + if ((status != I40E_SUCCESS) || (pba_word != 0xFAFA)) { hw_dbg(hw, "Failed to read PBA flags or flag is invalid.\n"); return status; } status = i40e_read_nvm_word(hw, I40E_SR_PBA_BLOCK_PTR, &pba_ptr); - if (status) { + if (status != I40E_SUCCESS) { hw_dbg(hw, "Failed to read PBA Block pointer.\n"); return status; } status = i40e_read_nvm_word(hw, pba_ptr, &pba_size); - if (status) { + if (status != I40E_SUCCESS) { hw_dbg(hw, "Failed to read PBA Block size.\n"); return status; } @@ -1135,7 +1128,7 @@ i40e_status i40e_read_pba_string(struct i40e_hw *hw, u8 *pba_num, for (i = 0; i < pba_size; i++) { status = i40e_read_nvm_word(hw, (pba_ptr + 1) + i, &pba_word); - if (status) { + if (status != I40E_SUCCESS) { hw_dbg(hw, "Failed to read PBA Block word %d.\n", i); return status; } @@ -1169,6 +1162,8 @@ static enum i40e_media_type i40e_get_media_type(struct i40e_hw *hw) break; case I40E_PHY_TYPE_100BASE_TX: case I40E_PHY_TYPE_1000BASE_T: + case I40E_PHY_TYPE_2_5GBASE_T: + case I40E_PHY_TYPE_5GBASE_T: case I40E_PHY_TYPE_10GBASE_T: media = I40E_MEDIA_TYPE_BASET; break; @@ -1180,6 +1175,8 @@ static enum i40e_media_type i40e_get_media_type(struct i40e_hw *hw) case I40E_PHY_TYPE_40GBASE_AOC: case I40E_PHY_TYPE_10GBASE_AOC: case I40E_PHY_TYPE_25GBASE_CR: + case I40E_PHY_TYPE_25GBASE_AOC: + case I40E_PHY_TYPE_25GBASE_ACC: media = I40E_MEDIA_TYPE_DA; break; case I40E_PHY_TYPE_1000BASE_KX: @@ -1203,7 +1200,29 @@ static enum i40e_media_type i40e_get_media_type(struct i40e_hw *hw) return media; } -#define I40E_PF_RESET_WAIT_COUNT_A0 200 +/** + * i40e_poll_globr - Poll for Global Reset completion + * @hw: pointer to the hardware structure + * @retry_limit: how many times to retry before failure + **/ +static i40e_status i40e_poll_globr(struct i40e_hw *hw, + u32 retry_limit) +{ + u32 cnt, reg = 0; + + for (cnt = 0; cnt < retry_limit; cnt++) { + reg = rd32(hw, I40E_GLGEN_RSTAT); + if (!(reg & I40E_GLGEN_RSTAT_DEVSTATE_MASK)) + return I40E_SUCCESS; + msleep(100); + } + + hw_dbg(hw, "Global reset failed.\n"); + hw_dbg(hw, "I40E_GLGEN_RSTAT = 0x%x\n", reg); + + return I40E_ERR_RESET_FAILED; +} + #define I40E_PF_RESET_WAIT_COUNT 200 /** * i40e_pf_reset - Reset the PF @@ -1224,13 +1243,10 @@ i40e_status i40e_pf_reset(struct i40e_hw *hw) * couple counts longer to be sure we don't just miss the end. */ grst_del = (rd32(hw, I40E_GLGEN_RSTCTL) & - I40E_GLGEN_RSTCTL_GRSTDEL_MASK) >> - I40E_GLGEN_RSTCTL_GRSTDEL_SHIFT; + I40E_GLGEN_RSTCTL_GRSTDEL_MASK) >> + I40E_GLGEN_RSTCTL_GRSTDEL_SHIFT; - /* It can take upto 15 secs for GRST steady state. - * Bump it to 16 secs max to be safe. - */ - grst_del = grst_del * 20; + grst_del = min(grst_del * 20, 160U); for (cnt = 0; cnt < grst_del; cnt++) { reg = rd32(hw, I40E_GLGEN_RSTAT); @@ -1266,20 +1282,24 @@ i40e_status i40e_pf_reset(struct i40e_hw *hw) * we don't need to do the PF Reset */ if (!cnt) { - if (hw->revision_id == 0) - cnt = I40E_PF_RESET_WAIT_COUNT_A0; - else - cnt = I40E_PF_RESET_WAIT_COUNT; + u32 reg2 = 0; + reg = rd32(hw, I40E_PFGEN_CTRL); wr32(hw, I40E_PFGEN_CTRL, (reg | I40E_PFGEN_CTRL_PFSWR_MASK)); - for (; cnt; cnt--) { + for (cnt = 0; cnt < I40E_PF_RESET_WAIT_COUNT; cnt++) { reg = rd32(hw, I40E_PFGEN_CTRL); if (!(reg & I40E_PFGEN_CTRL_PFSWR_MASK)) break; + reg2 = rd32(hw, I40E_GLGEN_RSTAT); + if (reg2 & I40E_GLGEN_RSTAT_DEVSTATE_MASK) + break; usleep_range(1000, 2000); } - if (reg & I40E_PFGEN_CTRL_PFSWR_MASK) { + if (reg2 & I40E_GLGEN_RSTAT_DEVSTATE_MASK) { + if (i40e_poll_globr(hw, grst_del) != I40E_SUCCESS) + return I40E_ERR_RESET_FAILED; + } else if (reg & I40E_PFGEN_CTRL_PFSWR_MASK) { hw_dbg(hw, "PF reset polling failed to complete.\n"); return I40E_ERR_RESET_FAILED; } @@ -1287,7 +1307,7 @@ i40e_status i40e_pf_reset(struct i40e_hw *hw) i40e_clear_pxe_mode(hw); - return 0; + return I40E_SUCCESS; } /** @@ -1308,18 +1328,18 @@ void i40e_clear_hw(struct i40e_hw *hw) u32 val; u32 eol = 0x7ff; - /* get number of interrupts, queues, and VFs */ + /* get number of interrupts, queues, and vfs */ val = rd32(hw, I40E_GLPCI_CNF2); num_pf_int = (val & I40E_GLPCI_CNF2_MSI_X_PF_N_MASK) >> - I40E_GLPCI_CNF2_MSI_X_PF_N_SHIFT; + I40E_GLPCI_CNF2_MSI_X_PF_N_SHIFT; num_vf_int = (val & I40E_GLPCI_CNF2_MSI_X_VF_N_MASK) >> - I40E_GLPCI_CNF2_MSI_X_VF_N_SHIFT; + I40E_GLPCI_CNF2_MSI_X_VF_N_SHIFT; val = rd32(hw, I40E_PFLAN_QALLOC); base_queue = (val & I40E_PFLAN_QALLOC_FIRSTQ_MASK) >> - I40E_PFLAN_QALLOC_FIRSTQ_SHIFT; + I40E_PFLAN_QALLOC_FIRSTQ_SHIFT; j = (val & I40E_PFLAN_QALLOC_LASTQ_MASK) >> - I40E_PFLAN_QALLOC_LASTQ_SHIFT; + I40E_PFLAN_QALLOC_LASTQ_SHIFT; if (val & I40E_PFLAN_QALLOC_VALID_MASK) num_queues = (j - base_queue) + 1; else @@ -1327,9 +1347,9 @@ void i40e_clear_hw(struct i40e_hw *hw) val = rd32(hw, I40E_PF_VT_PFALLOC); i = (val & I40E_PF_VT_PFALLOC_FIRSTVF_MASK) >> - I40E_PF_VT_PFALLOC_FIRSTVF_SHIFT; + I40E_PF_VT_PFALLOC_FIRSTVF_SHIFT; j = (val & I40E_PF_VT_PFALLOC_LASTVF_MASK) >> - I40E_PF_VT_PFALLOC_LASTVF_SHIFT; + I40E_PF_VT_PFALLOC_LASTVF_SHIFT; if (val & I40E_PF_VT_PFALLOC_VALID_MASK) num_vfs = (j - i) + 1; else @@ -1392,20 +1412,8 @@ void i40e_clear_hw(struct i40e_hw *hw) **/ void i40e_clear_pxe_mode(struct i40e_hw *hw) { - u32 reg; - if (i40e_check_asq_alive(hw)) i40e_aq_clear_pxe_mode(hw, NULL); - - /* Clear single descriptor fetch/write-back mode */ - reg = rd32(hw, I40E_GLLAN_RCTL_0); - - if (hw->revision_id == 0) { - /* As a work around clear PXE_MODE instead of setting it */ - wr32(hw, I40E_GLLAN_RCTL_0, (reg & (~I40E_GLLAN_RCTL_0_PXE_MODE_MASK))); - } else { - wr32(hw, I40E_GLLAN_RCTL_0, (reg | I40E_GLLAN_RCTL_0_PXE_MODE_MASK)); - } } /** @@ -1420,9 +1428,9 @@ static u32 i40e_led_is_mine(struct i40e_hw *hw, int idx) u32 gpio_val = 0; u32 port; - if (!hw->func_caps.led[idx]) + if (!I40E_IS_X710TL_DEVICE(hw->device_id) && + !hw->func_caps.led[idx]) return 0; - gpio_val = rd32(hw, I40E_GLGEN_GPIO_CTL(idx)); port = (gpio_val & I40E_GLGEN_GPIO_CTL_PRT_NUM_MASK) >> I40E_GLGEN_GPIO_CTL_PRT_NUM_SHIFT; @@ -1441,8 +1449,15 @@ static u32 i40e_led_is_mine(struct i40e_hw *hw, int idx) #define I40E_FILTER_ACTIVITY 0xE #define I40E_LINK_ACTIVITY 0xC #define I40E_MAC_ACTIVITY 0xD +#define I40E_FW_LED BIT(4) +#define I40E_LED_MODE_VALID (I40E_GLGEN_GPIO_CTL_LED_MODE_MASK >> \ + I40E_GLGEN_GPIO_CTL_LED_MODE_SHIFT) + #define I40E_LED0 22 +#define I40E_PIN_FUNC_SDP 0x0 +#define I40E_PIN_FUNC_LED 0x1 + /** * i40e_led_get - return current on/off mode * @hw: pointer to the hw struct @@ -1468,7 +1483,7 @@ u32 i40e_led_get(struct i40e_hw *hw) continue; /* ignore gpio LED src mode entries related to the activity - * LEDs + * LEDs */ current_mode = ((gpio_val & I40E_GLGEN_GPIO_CTL_LED_MODE_MASK) >> I40E_GLGEN_GPIO_CTL_LED_MODE_SHIFT); @@ -1476,6 +1491,7 @@ u32 i40e_led_get(struct i40e_hw *hw) case I40E_COMBINED_ACTIVITY: case I40E_FILTER_ACTIVITY: case I40E_MAC_ACTIVITY: + case I40E_LINK_ACTIVITY: continue; default: break; @@ -1503,8 +1519,10 @@ void i40e_led_set(struct i40e_hw *hw, u32 mode, bool blink) u32 current_mode = 0; int i; - if (mode & 0xfffffff0) + if (mode & ~I40E_LED_MODE_VALID) { hw_dbg(hw, "invalid mode passed in %X\n", mode); + return; + } /* as per the documentation GPIO 22-29 are the LED * GPIO pins named LED0..LED7 @@ -1524,19 +1542,30 @@ void i40e_led_set(struct i40e_hw *hw, u32 mode, bool blink) case I40E_COMBINED_ACTIVITY: case I40E_FILTER_ACTIVITY: case I40E_MAC_ACTIVITY: + case I40E_LINK_ACTIVITY: continue; default: break; } + if (I40E_IS_X710TL_DEVICE(hw->device_id)) { + u32 pin_func = 0; + + if (mode & I40E_FW_LED) + pin_func = I40E_PIN_FUNC_SDP; + else + pin_func = I40E_PIN_FUNC_LED; + + gpio_val &= ~I40E_GLGEN_GPIO_CTL_PIN_FUNC_MASK; + gpio_val |= ((pin_func << + I40E_GLGEN_GPIO_CTL_PIN_FUNC_SHIFT) & + I40E_GLGEN_GPIO_CTL_PIN_FUNC_MASK); + } gpio_val &= ~I40E_GLGEN_GPIO_CTL_LED_MODE_MASK; /* this & is a bit of paranoia, but serves as a range check */ gpio_val |= ((mode << I40E_GLGEN_GPIO_CTL_LED_MODE_SHIFT) & I40E_GLGEN_GPIO_CTL_LED_MODE_MASK); - if (mode == I40E_LINK_ACTIVITY) - blink = false; - if (blink) gpio_val |= BIT(I40E_GLGEN_GPIO_CTL_LED_BLINK_SHIFT); else @@ -1566,35 +1595,61 @@ i40e_status i40e_aq_get_phy_capabilities(struct i40e_hw *hw, { struct i40e_aq_desc desc; i40e_status status; + u16 max_delay = I40E_MAX_PHY_TIMEOUT, total_delay = 0; u16 abilities_size = sizeof(struct i40e_aq_get_phy_abilities_resp); if (!abilities) return I40E_ERR_PARAM; - i40e_fill_default_direct_cmd_desc(&desc, - i40e_aqc_opc_get_phy_abilities); + do { + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_get_phy_abilities); - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); - if (abilities_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); + if (abilities_size > I40E_AQ_LARGE_BUF) + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); - if (qualified_modules) - desc.params.external.param0 |= - cpu_to_le32(I40E_AQ_PHY_REPORT_QUALIFIED_MODULES); + if (qualified_modules) + desc.params.external.param0 |= + CPU_TO_LE32(I40E_AQ_PHY_REPORT_QUALIFIED_MODULES); - if (report_init) - desc.params.external.param0 |= - cpu_to_le32(I40E_AQ_PHY_REPORT_INITIAL_VALUES); + if (report_init) + desc.params.external.param0 |= + CPU_TO_LE32(I40E_AQ_PHY_REPORT_INITIAL_VALUES); - status = i40e_asq_send_command(hw, &desc, abilities, abilities_size, - cmd_details); + status = i40e_asq_send_command(hw, &desc, abilities, + abilities_size, cmd_details); - if (hw->aq.asq_last_status == I40E_AQ_RC_EIO) - status = I40E_ERR_UNKNOWN_PHY; + switch (hw->aq.asq_last_status) { + case I40E_AQ_RC_EIO: + status = I40E_ERR_UNKNOWN_PHY; + break; + case I40E_AQ_RC_EAGAIN: + usleep_range(1000, 2000); + total_delay++; + status = I40E_ERR_TIMEOUT; + break; + /* also covers I40E_AQ_RC_OK */ + default: + break; + } + + } while ((hw->aq.asq_last_status == I40E_AQ_RC_EAGAIN) && + (total_delay < max_delay)); + + if (status != I40E_SUCCESS) + return status; if (report_init) { - hw->phy.phy_types = le32_to_cpu(abilities->phy_type); - hw->phy.phy_types |= ((u64)abilities->phy_type_ext << 32); + if (hw->mac.type == I40E_MAC_XL710 && + hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR && + hw->aq.api_min_ver >= I40E_MINOR_VER_GET_LINK_INFO_XL710) { + status = i40e_aq_get_link_info(hw, true, NULL, NULL); + } else { + hw->phy.phy_types = LE32_TO_CPU(abilities->phy_type); + hw->phy.phy_types |= + ((u64)abilities->phy_type_ext << 32); + } } return status; @@ -1612,14 +1667,14 @@ i40e_status i40e_aq_get_phy_capabilities(struct i40e_hw *hw, * of the PHY Config parameters. This status will be indicated by the * command response. **/ -enum i40e_status_code i40e_aq_set_phy_config(struct i40e_hw *hw, +i40e_status i40e_aq_set_phy_config(struct i40e_hw *hw, struct i40e_aq_set_phy_config *config, struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aq_set_phy_config *cmd = - (struct i40e_aq_set_phy_config *)&desc.params.raw; - enum i40e_status_code status; + (struct i40e_aq_set_phy_config *)&desc.params.raw; + i40e_status status; if (!config) return I40E_ERR_PARAM; @@ -1637,16 +1692,18 @@ enum i40e_status_code i40e_aq_set_phy_config(struct i40e_hw *hw, /** * i40e_set_fc * @hw: pointer to the hw struct + * @aq_failures: buffer to return AdminQ failure information + * @atomic_restart: whether to enable atomic link restart * * Set the requested flow control mode using set_phy_config. **/ -enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, +i40e_status i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, bool atomic_restart) { enum i40e_fc_mode fc_mode = hw->fc.requested_mode; struct i40e_aq_get_phy_abilities_resp abilities; struct i40e_aq_set_phy_config config; - enum i40e_status_code status; + i40e_status status; u8 pause_mask = 0x0; *aq_failures = 0x0; @@ -1674,7 +1731,7 @@ enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, return status; } - memset(&config, 0, sizeof(struct i40e_aq_set_phy_config)); + memset(&config, 0, sizeof(config)); /* clear the old pause settings */ config.abilities = abilities.abilities & ~(I40E_AQ_PHY_FLAG_PAUSE_TX) & ~(I40E_AQ_PHY_FLAG_PAUSE_RX); @@ -1723,7 +1780,7 @@ enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, * Tell the firmware that the driver is taking over from PXE **/ i40e_status i40e_aq_clear_pxe_mode(struct i40e_hw *hw, - struct i40e_asq_cmd_details *cmd_details) + struct i40e_asq_cmd_details *cmd_details) { i40e_status status; struct i40e_aq_desc desc; @@ -1751,8 +1808,7 @@ i40e_status i40e_aq_clear_pxe_mode(struct i40e_hw *hw, * Sets up the link and restarts the Auto-Negotiation over the link. **/ i40e_status i40e_aq_set_link_restart_an(struct i40e_hw *hw, - bool enable_link, - struct i40e_asq_cmd_details *cmd_details) + bool enable_link, struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_set_link_restart_an *cmd = @@ -1800,15 +1856,16 @@ i40e_status i40e_aq_get_link_info(struct i40e_hw *hw, command_flags = I40E_AQ_LSE_ENABLE; else command_flags = I40E_AQ_LSE_DISABLE; - resp->command_flags = cpu_to_le16(command_flags); + resp->command_flags = CPU_TO_LE16(command_flags); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); - if (status) + if (status != I40E_SUCCESS) goto aq_get_link_info_exit; /* save off old link status information */ - hw->phy.link_info_old = *hw_link_info; + i40e_memcpy(&hw->phy.link_info_old, hw_link_info, + sizeof(*hw_link_info), I40E_NONDMA_TO_NONDMA); /* update link status */ hw_link_info->phy_type = (enum i40e_aq_phy_type)resp->phy_type; @@ -1819,8 +1876,8 @@ i40e_status i40e_aq_get_link_info(struct i40e_hw *hw, hw_link_info->fec_info = resp->config & (I40E_AQ_CONFIG_FEC_KR_ENA | I40E_AQ_CONFIG_FEC_RS_ENA); hw_link_info->ext_info = resp->ext_info; - hw_link_info->loopback = resp->loopback; - hw_link_info->max_frame_size = le16_to_cpu(resp->max_frame_size); + hw_link_info->loopback = resp->loopback & I40E_AQ_LOOPBACK_MASK; + hw_link_info->max_frame_size = LE16_TO_CPU(resp->max_frame_size); hw_link_info->pacing = resp->config & I40E_AQ_CONFIG_PACING_MASK; /* update fc info */ @@ -1840,7 +1897,7 @@ i40e_status i40e_aq_get_link_info(struct i40e_hw *hw, else hw_link_info->crc_enable = false; - if (resp->command_flags & cpu_to_le16(I40E_AQ_LSE_IS_ENABLED)) + if (resp->command_flags & CPU_TO_LE16(I40E_AQ_LSE_IS_ENABLED)) hw_link_info->lse_enable = true; else hw_link_info->lse_enable = false; @@ -1850,9 +1907,20 @@ i40e_status i40e_aq_get_link_info(struct i40e_hw *hw, hw->aq.fw_min_ver < 40)) && hw_link_info->phy_type == 0xE) hw_link_info->phy_type = I40E_PHY_TYPE_10GBASE_SFPP_CU; + if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE && + hw->mac.type != I40E_MAC_X722) { + __le32 tmp; + + i40e_memcpy(&tmp, resp->link_type, sizeof(tmp), + I40E_NONDMA_TO_NONDMA); + hw->phy.phy_types = LE32_TO_CPU(tmp); + hw->phy.phy_types |= ((u64)resp->link_type_ext << 32); + } + /* save link status information */ if (link) - *link = *hw_link_info; + i40e_memcpy(link, hw_link_info, sizeof(*hw_link_info), + I40E_NONDMA_TO_NONDMA); /* flag cleared so helper functions don't call AQ again */ hw->phy.get_link_info = false; @@ -1870,8 +1938,8 @@ aq_get_link_info_exit: * Set link interrupt mask. **/ i40e_status i40e_aq_set_phy_int_mask(struct i40e_hw *hw, - u16 mask, - struct i40e_asq_cmd_details *cmd_details) + u16 mask, + struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_set_phy_int_mask *cmd = @@ -1881,7 +1949,7 @@ i40e_status i40e_aq_set_phy_int_mask(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_set_phy_int_mask); - cmd->event_mask = cpu_to_le16(mask); + cmd->event_mask = CPU_TO_LE16(mask); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); @@ -1897,7 +1965,7 @@ i40e_status i40e_aq_set_phy_int_mask(struct i40e_hw *hw, * Reset the external PHY. **/ i40e_status i40e_aq_set_phy_debug(struct i40e_hw *hw, u8 cmd_flags, - struct i40e_asq_cmd_details *cmd_details) + struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_set_phy_debug *cmd = @@ -1937,23 +2005,24 @@ i40e_status i40e_aq_add_vsi(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_add_vsi); - cmd->uplink_seid = cpu_to_le16(vsi_ctx->uplink_seid); + cmd->uplink_seid = CPU_TO_LE16(vsi_ctx->uplink_seid); cmd->connection_type = vsi_ctx->connection_type; cmd->vf_id = vsi_ctx->vf_num; - cmd->vsi_flags = cpu_to_le16(vsi_ctx->flags); + cmd->vsi_flags = CPU_TO_LE16(vsi_ctx->flags); - desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); - status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info, - sizeof(vsi_ctx->info), cmd_details); + status = i40e_asq_send_command_atomic(hw, &desc, &vsi_ctx->info, + sizeof(vsi_ctx->info), + cmd_details, true); - if (status) + if (status != I40E_SUCCESS) goto aq_add_vsi_exit; - vsi_ctx->seid = le16_to_cpu(resp->seid); - vsi_ctx->vsi_number = le16_to_cpu(resp->vsi_number); - vsi_ctx->vsis_allocated = le16_to_cpu(resp->vsi_used); - vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free); + vsi_ctx->seid = LE16_TO_CPU(resp->seid); + vsi_ctx->vsi_number = LE16_TO_CPU(resp->vsi_number); + vsi_ctx->vsis_allocated = LE16_TO_CPU(resp->vsi_used); + vsi_ctx->vsis_unallocated = LE16_TO_CPU(resp->vsi_free); aq_add_vsi_exit: return status; @@ -1966,8 +2035,8 @@ aq_add_vsi_exit: * @cmd_details: pointer to command details structure or NULL **/ i40e_status i40e_aq_set_default_vsi(struct i40e_hw *hw, - u16 seid, - struct i40e_asq_cmd_details *cmd_details) + u16 seid, + struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_set_vsi_promiscuous_modes *cmd = @@ -1976,11 +2045,11 @@ i40e_status i40e_aq_set_default_vsi(struct i40e_hw *hw, i40e_status status; i40e_fill_default_direct_cmd_desc(&desc, - i40e_aqc_opc_set_vsi_promiscuous_modes); + i40e_aqc_opc_set_vsi_promiscuous_modes); - cmd->promiscuous_flags = cpu_to_le16(I40E_AQC_SET_VSI_DEFAULT); - cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_DEFAULT); - cmd->seid = cpu_to_le16(seid); + cmd->promiscuous_flags = CPU_TO_LE16(I40E_AQC_SET_VSI_DEFAULT); + cmd->valid_flags = CPU_TO_LE16(I40E_AQC_SET_VSI_DEFAULT); + cmd->seid = CPU_TO_LE16(seid); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); @@ -1994,8 +2063,8 @@ i40e_status i40e_aq_set_default_vsi(struct i40e_hw *hw, * @cmd_details: pointer to command details structure or NULL **/ i40e_status i40e_aq_clear_default_vsi(struct i40e_hw *hw, - u16 seid, - struct i40e_asq_cmd_details *cmd_details) + u16 seid, + struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_set_vsi_promiscuous_modes *cmd = @@ -2004,11 +2073,11 @@ i40e_status i40e_aq_clear_default_vsi(struct i40e_hw *hw, i40e_status status; i40e_fill_default_direct_cmd_desc(&desc, - i40e_aqc_opc_set_vsi_promiscuous_modes); + i40e_aqc_opc_set_vsi_promiscuous_modes); - cmd->promiscuous_flags = cpu_to_le16(0); - cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_DEFAULT); - cmd->seid = cpu_to_le16(seid); + cmd->promiscuous_flags = CPU_TO_LE16(0); + cmd->valid_flags = CPU_TO_LE16(I40E_AQC_SET_VSI_DEFAULT); + cmd->seid = CPU_TO_LE16(seid); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); @@ -2045,15 +2114,16 @@ i40e_status i40e_aq_set_vsi_unicast_promiscuous(struct i40e_hw *hw, flags |= I40E_AQC_SET_VSI_PROMISC_TX; } - cmd->promiscuous_flags = cpu_to_le16(flags); + cmd->promiscuous_flags = CPU_TO_LE16(flags); - cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_UNICAST); + cmd->valid_flags = CPU_TO_LE16(I40E_AQC_SET_VSI_PROMISC_UNICAST); if (((hw->aq.api_maj_ver >= 1) && (hw->aq.api_min_ver >= 5)) || - (hw->aq.api_maj_ver > 1)) - cmd->valid_flags |= cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_TX); + (hw->aq.api_maj_ver > 1)) + cmd->valid_flags |= CPU_TO_LE16(I40E_AQC_SET_VSI_PROMISC_TX); - cmd->seid = cpu_to_le16(seid); - status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + cmd->seid = CPU_TO_LE16(seid); + status = i40e_asq_send_command_atomic(hw, &desc, NULL, 0, + cmd_details, true); return status; } @@ -2080,11 +2150,49 @@ i40e_status i40e_aq_set_vsi_multicast_promiscuous(struct i40e_hw *hw, if (set) flags |= I40E_AQC_SET_VSI_PROMISC_MULTICAST; - cmd->promiscuous_flags = cpu_to_le16(flags); + cmd->promiscuous_flags = CPU_TO_LE16(flags); + + cmd->valid_flags = CPU_TO_LE16(I40E_AQC_SET_VSI_PROMISC_MULTICAST); + + cmd->seid = CPU_TO_LE16(seid); + status = i40e_asq_send_command_atomic(hw, &desc, NULL, 0, + cmd_details, true); + + return status; +} + +/** +* i40e_aq_set_vsi_full_promiscuous +* @hw: pointer to the hw struct +* @seid: VSI number +* @set: set promiscuous enable/disable +* @cmd_details: pointer to command details structure or NULL +**/ +i40e_status i40e_aq_set_vsi_full_promiscuous(struct i40e_hw *hw, + u16 seid, bool set, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_set_vsi_promiscuous_modes *cmd = + (struct i40e_aqc_set_vsi_promiscuous_modes *)&desc.params.raw; + i40e_status status; + u16 flags = 0; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_set_vsi_promiscuous_modes); + + if (set) + flags = I40E_AQC_SET_VSI_PROMISC_UNICAST | + I40E_AQC_SET_VSI_PROMISC_MULTICAST | + I40E_AQC_SET_VSI_PROMISC_BROADCAST; + + cmd->promiscuous_flags = CPU_TO_LE16(flags); - cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_MULTICAST); + cmd->valid_flags = CPU_TO_LE16(I40E_AQC_SET_VSI_PROMISC_UNICAST | + I40E_AQC_SET_VSI_PROMISC_MULTICAST | + I40E_AQC_SET_VSI_PROMISC_BROADCAST); - cmd->seid = cpu_to_le16(seid); + cmd->seid = CPU_TO_LE16(seid); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); return status; @@ -2098,29 +2206,29 @@ i40e_status i40e_aq_set_vsi_multicast_promiscuous(struct i40e_hw *hw, * @vid: The VLAN tag filter - capture any multicast packet with this VLAN tag * @cmd_details: pointer to command details structure or NULL **/ -enum i40e_status_code i40e_aq_set_vsi_mc_promisc_on_vlan(struct i40e_hw *hw, - u16 seid, bool enable, - u16 vid, +i40e_status i40e_aq_set_vsi_mc_promisc_on_vlan(struct i40e_hw *hw, + u16 seid, bool enable, u16 vid, struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_set_vsi_promiscuous_modes *cmd = (struct i40e_aqc_set_vsi_promiscuous_modes *)&desc.params.raw; - enum i40e_status_code status; + i40e_status status; u16 flags = 0; i40e_fill_default_direct_cmd_desc(&desc, - i40e_aqc_opc_set_vsi_promiscuous_modes); + i40e_aqc_opc_set_vsi_promiscuous_modes); if (enable) flags |= I40E_AQC_SET_VSI_PROMISC_MULTICAST; - cmd->promiscuous_flags = cpu_to_le16(flags); - cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_MULTICAST); - cmd->seid = cpu_to_le16(seid); - cmd->vlan_tag = cpu_to_le16(vid | I40E_AQC_SET_VSI_VLAN_VALID); + cmd->promiscuous_flags = CPU_TO_LE16(flags); + cmd->valid_flags = CPU_TO_LE16(I40E_AQC_SET_VSI_PROMISC_MULTICAST); + cmd->seid = CPU_TO_LE16(seid); + cmd->vlan_tag = CPU_TO_LE16(vid | I40E_AQC_SET_VSI_VLAN_VALID); - status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + status = i40e_asq_send_command_atomic(hw, &desc, NULL, 0, + cmd_details, true); return status; } @@ -2133,29 +2241,29 @@ enum i40e_status_code i40e_aq_set_vsi_mc_promisc_on_vlan(struct i40e_hw *hw, * @vid: The VLAN tag filter - capture any unicast packet with this VLAN tag * @cmd_details: pointer to command details structure or NULL **/ -enum i40e_status_code i40e_aq_set_vsi_uc_promisc_on_vlan(struct i40e_hw *hw, - u16 seid, bool enable, - u16 vid, +i40e_status i40e_aq_set_vsi_uc_promisc_on_vlan(struct i40e_hw *hw, + u16 seid, bool enable, u16 vid, struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_set_vsi_promiscuous_modes *cmd = (struct i40e_aqc_set_vsi_promiscuous_modes *)&desc.params.raw; - enum i40e_status_code status; + i40e_status status; u16 flags = 0; i40e_fill_default_direct_cmd_desc(&desc, - i40e_aqc_opc_set_vsi_promiscuous_modes); + i40e_aqc_opc_set_vsi_promiscuous_modes); if (enable) flags |= I40E_AQC_SET_VSI_PROMISC_UNICAST; - cmd->promiscuous_flags = cpu_to_le16(flags); - cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_UNICAST); - cmd->seid = cpu_to_le16(seid); - cmd->vlan_tag = cpu_to_le16(vid | I40E_AQC_SET_VSI_VLAN_VALID); + cmd->promiscuous_flags = CPU_TO_LE16(flags); + cmd->valid_flags = CPU_TO_LE16(I40E_AQC_SET_VSI_PROMISC_UNICAST); + cmd->seid = CPU_TO_LE16(seid); + cmd->vlan_tag = CPU_TO_LE16(vid | I40E_AQC_SET_VSI_VLAN_VALID); - status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + status = i40e_asq_send_command_atomic(hw, &desc, NULL, 0, + cmd_details, true); return status; } @@ -2184,10 +2292,10 @@ i40e_status i40e_aq_set_vsi_bc_promisc_on_vlan(struct i40e_hw *hw, if (enable) flags |= I40E_AQC_SET_VSI_PROMISC_BROADCAST; - cmd->promiscuous_flags = cpu_to_le16(flags); - cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_BROADCAST); - cmd->seid = cpu_to_le16(seid); - cmd->vlan_tag = cpu_to_le16(vid | I40E_AQC_SET_VSI_VLAN_VALID); + cmd->promiscuous_flags = CPU_TO_LE16(flags); + cmd->valid_flags = CPU_TO_LE16(I40E_AQC_SET_VSI_PROMISC_BROADCAST); + cmd->seid = CPU_TO_LE16(seid); + cmd->vlan_tag = CPU_TO_LE16(vid | I40E_AQC_SET_VSI_VLAN_VALID); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); @@ -2217,13 +2325,13 @@ i40e_status i40e_aq_set_vsi_broadcast(struct i40e_hw *hw, if (set_filter) cmd->promiscuous_flags - |= cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_BROADCAST); + |= CPU_TO_LE16(I40E_AQC_SET_VSI_PROMISC_BROADCAST); else cmd->promiscuous_flags - &= cpu_to_le16(~I40E_AQC_SET_VSI_PROMISC_BROADCAST); + &= CPU_TO_LE16(~I40E_AQC_SET_VSI_PROMISC_BROADCAST); - cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_BROADCAST); - cmd->seid = cpu_to_le16(seid); + cmd->valid_flags = CPU_TO_LE16(I40E_AQC_SET_VSI_PROMISC_BROADCAST); + cmd->seid = CPU_TO_LE16(seid); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); return status; @@ -2237,8 +2345,8 @@ i40e_status i40e_aq_set_vsi_broadcast(struct i40e_hw *hw, * @cmd_details: pointer to command details structure or NULL **/ i40e_status i40e_aq_set_vsi_vlan_promisc(struct i40e_hw *hw, - u16 seid, bool enable, - struct i40e_asq_cmd_details *cmd_details) + u16 seid, bool enable, + struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_set_vsi_promiscuous_modes *cmd = @@ -2251,9 +2359,9 @@ i40e_status i40e_aq_set_vsi_vlan_promisc(struct i40e_hw *hw, if (enable) flags |= I40E_AQC_SET_VSI_PROMISC_VLAN; - cmd->promiscuous_flags = cpu_to_le16(flags); - cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_VLAN); - cmd->seid = cpu_to_le16(seid); + cmd->promiscuous_flags = CPU_TO_LE16(flags); + cmd->valid_flags = CPU_TO_LE16(I40E_AQC_SET_VSI_PROMISC_VLAN); + cmd->seid = CPU_TO_LE16(seid); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); @@ -2281,20 +2389,20 @@ i40e_status i40e_aq_get_vsi_params(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_get_vsi_parameters); - cmd->uplink_seid = cpu_to_le16(vsi_ctx->seid); + cmd->uplink_seid = CPU_TO_LE16(vsi_ctx->seid); - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info, sizeof(vsi_ctx->info), NULL); - if (status) + if (status != I40E_SUCCESS) goto aq_get_vsi_params_exit; - vsi_ctx->seid = le16_to_cpu(resp->seid); - vsi_ctx->vsi_number = le16_to_cpu(resp->vsi_number); - vsi_ctx->vsis_allocated = le16_to_cpu(resp->vsi_used); - vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free); + vsi_ctx->seid = LE16_TO_CPU(resp->seid); + vsi_ctx->vsi_number = LE16_TO_CPU(resp->vsi_number); + vsi_ctx->vsis_allocated = LE16_TO_CPU(resp->vsi_used); + vsi_ctx->vsis_unallocated = LE16_TO_CPU(resp->vsi_free); aq_get_vsi_params_exit: return status; @@ -2322,15 +2430,16 @@ i40e_status i40e_aq_update_vsi_params(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_update_vsi_parameters); - cmd->uplink_seid = cpu_to_le16(vsi_ctx->seid); + cmd->uplink_seid = CPU_TO_LE16(vsi_ctx->seid); - desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); - status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info, - sizeof(vsi_ctx->info), cmd_details); + status = i40e_asq_send_command_atomic(hw, &desc, &vsi_ctx->info, + sizeof(vsi_ctx->info), + cmd_details, true); - vsi_ctx->vsis_allocated = le16_to_cpu(resp->vsi_used); - vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free); + vsi_ctx->vsis_allocated = LE16_TO_CPU(resp->vsi_used); + vsi_ctx->vsis_unallocated = LE16_TO_CPU(resp->vsi_free); return status; } @@ -2357,13 +2466,13 @@ i40e_status i40e_aq_get_switch_config(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_get_switch_config); - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); if (buf_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); - scfg->seid = cpu_to_le16(*start_seid); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); + scfg->seid = CPU_TO_LE16(*start_seid); status = i40e_asq_send_command(hw, &desc, buf, buf_size, cmd_details); - *start_seid = le16_to_cpu(scfg->seid); + *start_seid = LE16_TO_CPU(scfg->seid); return status; } @@ -2372,26 +2481,31 @@ i40e_status i40e_aq_get_switch_config(struct i40e_hw *hw, * i40e_aq_set_switch_config * @hw: pointer to the hardware structure * @flags: bit flag values to set + * @mode: cloud filter mode * @valid_flags: which bit flags to set * @cmd_details: pointer to command details structure or NULL * * Set switch configuration bits **/ -enum i40e_status_code i40e_aq_set_switch_config(struct i40e_hw *hw, - u16 flags, - u16 valid_flags, +i40e_status i40e_aq_set_switch_config(struct i40e_hw *hw, + u16 flags, u16 valid_flags, u8 mode, struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_set_switch_config *scfg = (struct i40e_aqc_set_switch_config *)&desc.params.raw; - enum i40e_status_code status; + i40e_status status; i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_set_switch_config); - scfg->flags = cpu_to_le16(flags); - scfg->valid_flags = cpu_to_le16(valid_flags); - + scfg->flags = CPU_TO_LE16(flags); + scfg->valid_flags = CPU_TO_LE16(valid_flags); + scfg->mode = mode; + if (hw->flags & I40E_HW_FLAG_802_1AD_CAPABLE) { + scfg->switch_tag = CPU_TO_LE16(hw->switch_tag); + scfg->first_tag = CPU_TO_LE16(hw->first_tag); + scfg->second_tag = CPU_TO_LE16(hw->second_tag); + } status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); return status; @@ -2424,17 +2538,17 @@ i40e_status i40e_aq_get_firmware_version(struct i40e_hw *hw, status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); - if (!status) { - if (fw_major_version) - *fw_major_version = le16_to_cpu(resp->fw_major); - if (fw_minor_version) - *fw_minor_version = le16_to_cpu(resp->fw_minor); - if (fw_build) - *fw_build = le32_to_cpu(resp->fw_build); - if (api_major_version) - *api_major_version = le16_to_cpu(resp->api_major); - if (api_minor_version) - *api_minor_version = le16_to_cpu(resp->api_minor); + if (status == I40E_SUCCESS) { + if (fw_major_version != NULL) + *fw_major_version = LE16_TO_CPU(resp->fw_major); + if (fw_minor_version != NULL) + *fw_minor_version = LE16_TO_CPU(resp->fw_minor); + if (fw_build != NULL) + *fw_build = LE32_TO_CPU(resp->fw_build); + if (api_major_version != NULL) + *api_major_version = LE16_TO_CPU(resp->api_major); + if (api_minor_version != NULL) + *api_minor_version = LE16_TO_CPU(resp->api_minor); } return status; @@ -2463,7 +2577,7 @@ i40e_status i40e_aq_send_driver_version(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_driver_version); - desc.flags |= cpu_to_le16(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD); + desc.flags |= CPU_TO_LE16(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD); cmd->driver_major_ver = dv->major_version; cmd->driver_minor_ver = dv->minor_version; cmd->driver_build_ver = dv->build_version; @@ -2486,18 +2600,18 @@ i40e_status i40e_aq_send_driver_version(struct i40e_hw *hw, * @link_up: pointer to bool (true/false = linkup/linkdown) * * Variable link_up true if link is up, false if link is down. - * The variable link_up is invalid if returned value of status != 0 + * The variable link_up is invalid if returned value of status != I40E_SUCCESS * * Side effect: LinkStatusEvent reporting becomes enabled **/ i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; if (hw->phy.get_link_info) { status = i40e_update_link_info(hw); - if (status) + if (status != I40E_SUCCESS) i40e_debug(hw, I40E_DEBUG_LINK, "get link failed: status %d\n", status); } @@ -2514,7 +2628,7 @@ i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up) i40e_status i40e_update_link_info(struct i40e_hw *hw) { struct i40e_aq_get_phy_abilities_resp abilities; - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; status = i40e_aq_get_link_info(hw, true, NULL, NULL); if (status) @@ -2529,14 +2643,20 @@ i40e_status i40e_update_link_info(struct i40e_hw *hw) if (status) return status; - hw->phy.link_info.req_fec_info = - abilities.fec_cfg_curr_mod_ext_info & - (I40E_AQ_REQUEST_FEC_KR | I40E_AQ_REQUEST_FEC_RS); + if (abilities.fec_cfg_curr_mod_ext_info & + I40E_AQ_ENABLE_FEC_AUTO) + hw->phy.link_info.req_fec_info = + (I40E_AQ_REQUEST_FEC_KR | + I40E_AQ_REQUEST_FEC_RS); + else + hw->phy.link_info.req_fec_info = + abilities.fec_cfg_curr_mod_ext_info & + (I40E_AQ_REQUEST_FEC_KR | + I40E_AQ_REQUEST_FEC_RS); - memcpy(hw->phy.link_info.module_type, &abilities.module_type, - sizeof(hw->phy.link_info.module_type)); + i40e_memcpy(hw->phy.link_info.module_type, &abilities.module_type, + sizeof(hw->phy.link_info.module_type), I40E_NONDMA_TO_NONDMA); } - return status; } @@ -2574,8 +2694,8 @@ i40e_status i40e_aq_add_veb(struct i40e_hw *hw, u16 uplink_seid, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_add_veb); - cmd->uplink_seid = cpu_to_le16(uplink_seid); - cmd->downlink_seid = cpu_to_le16(downlink_seid); + cmd->uplink_seid = CPU_TO_LE16(uplink_seid); + cmd->downlink_seid = CPU_TO_LE16(downlink_seid); cmd->enable_tcs = enabled_tc; if (!uplink_seid) veb_flags |= I40E_AQC_ADD_VEB_FLOATING; @@ -2588,12 +2708,12 @@ i40e_status i40e_aq_add_veb(struct i40e_hw *hw, u16 uplink_seid, if (!enable_stats) veb_flags |= I40E_AQC_ADD_VEB_ENABLE_DISABLE_STATS; - cmd->veb_flags = cpu_to_le16(veb_flags); + cmd->veb_flags = CPU_TO_LE16(veb_flags); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); if (!status && veb_seid) - *veb_seid = le16_to_cpu(resp->veb_seid); + *veb_seid = LE16_TO_CPU(resp->veb_seid); return status; } @@ -2629,22 +2749,22 @@ i40e_status i40e_aq_get_veb_parameters(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_get_veb_parameters); - cmd_resp->seid = cpu_to_le16(veb_seid); + cmd_resp->seid = CPU_TO_LE16(veb_seid); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); if (status) goto get_veb_exit; if (switch_id) - *switch_id = le16_to_cpu(cmd_resp->switch_id); + *switch_id = LE16_TO_CPU(cmd_resp->switch_id); if (statistic_index) - *statistic_index = le16_to_cpu(cmd_resp->statistic_index); + *statistic_index = LE16_TO_CPU(cmd_resp->statistic_index); if (vebs_used) - *vebs_used = le16_to_cpu(cmd_resp->vebs_used); + *vebs_used = LE16_TO_CPU(cmd_resp->vebs_used); if (vebs_free) - *vebs_free = le16_to_cpu(cmd_resp->vebs_free); + *vebs_free = LE16_TO_CPU(cmd_resp->vebs_free); if (floating) { - u16 flags = le16_to_cpu(cmd_resp->veb_flags); + u16 flags = LE16_TO_CPU(cmd_resp->veb_flags); if (flags & I40E_AQC_ADD_VEB_FLOATING) *floating = true; @@ -2684,22 +2804,22 @@ i40e_status i40e_aq_add_macvlan(struct i40e_hw *hw, u16 seid, /* prep the rest of the request */ i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_add_macvlan); - cmd->num_addresses = cpu_to_le16(count); - cmd->seid[0] = cpu_to_le16(I40E_AQC_MACVLAN_CMD_SEID_VALID | seid); + cmd->num_addresses = CPU_TO_LE16(count); + cmd->seid[0] = CPU_TO_LE16(I40E_AQC_MACVLAN_CMD_SEID_VALID | seid); cmd->seid[1] = 0; cmd->seid[2] = 0; for (i = 0; i < count; i++) if (is_multicast_ether_addr(mv_list[i].mac_addr)) mv_list[i].flags |= - cpu_to_le16(I40E_AQC_MACVLAN_ADD_USE_SHARED_MAC); + CPU_TO_LE16(I40E_AQC_MACVLAN_ADD_USE_SHARED_MAC); - desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); if (buf_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); - status = i40e_asq_send_command(hw, &desc, mv_list, buf_size, - cmd_details); + status = i40e_asq_send_command_atomic(hw, &desc, mv_list, buf_size, + cmd_details, true); return status; } @@ -2731,17 +2851,17 @@ i40e_status i40e_aq_remove_macvlan(struct i40e_hw *hw, u16 seid, /* prep the rest of the request */ i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_remove_macvlan); - cmd->num_addresses = cpu_to_le16(count); - cmd->seid[0] = cpu_to_le16(I40E_AQC_MACVLAN_CMD_SEID_VALID | seid); + cmd->num_addresses = CPU_TO_LE16(count); + cmd->seid[0] = CPU_TO_LE16(I40E_AQC_MACVLAN_CMD_SEID_VALID | seid); cmd->seid[1] = 0; cmd->seid[2] = 0; - desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); if (buf_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); - status = i40e_asq_send_command(hw, &desc, mv_list, buf_size, - cmd_details); + status = i40e_asq_send_command_atomic(hw, &desc, mv_list, buf_size, + cmd_details, true); return status; } @@ -2757,17 +2877,17 @@ i40e_status i40e_aq_remove_macvlan(struct i40e_hw *hw, u16 seid, * @mr_list: list of mirrored VSI SEIDs or VLAN IDs * @cmd_details: pointer to command details structure or NULL * @rule_id: Rule ID returned from FW - * @rule_used: Number of rules used in internal switch - * @rule_free: Number of rules free in internal switch + * @rules_used: Number of rules used in internal switch + * @rules_free: Number of rules free in internal switch * * Add/Delete a mirror rule to a specific switch. Mirror rules are supported for * VEBs/VEPA elements only **/ static i40e_status i40e_mirrorrule_op(struct i40e_hw *hw, - u16 opcode, u16 sw_seid, u16 rule_type, u16 id, - u16 count, __le16 *mr_list, - struct i40e_asq_cmd_details *cmd_details, - u16 *rule_id, u16 *rules_used, u16 *rules_free) + u16 opcode, u16 sw_seid, u16 rule_type, u16 id, + u16 count, __le16 *mr_list, + struct i40e_asq_cmd_details *cmd_details, + u16 *rule_id, u16 *rules_used, u16 *rules_free) { struct i40e_aq_desc desc; struct i40e_aqc_add_delete_mirror_rule *cmd = @@ -2781,29 +2901,29 @@ static i40e_status i40e_mirrorrule_op(struct i40e_hw *hw, /* prep the rest of the request */ i40e_fill_default_direct_cmd_desc(&desc, opcode); - cmd->seid = cpu_to_le16(sw_seid); - cmd->rule_type = cpu_to_le16(rule_type & + cmd->seid = CPU_TO_LE16(sw_seid); + cmd->rule_type = CPU_TO_LE16(rule_type & I40E_AQC_MIRROR_RULE_TYPE_MASK); - cmd->num_entries = cpu_to_le16(count); + cmd->num_entries = CPU_TO_LE16(count); /* Dest VSI for add, rule_id for delete */ - cmd->destination = cpu_to_le16(id); + cmd->destination = CPU_TO_LE16(id); if (mr_list) { - desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); if (buf_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); } status = i40e_asq_send_command(hw, &desc, mr_list, buf_size, cmd_details); - if (!status || + if (status == I40E_SUCCESS || hw->aq.asq_last_status == I40E_AQ_RC_ENOSPC) { if (rule_id) - *rule_id = le16_to_cpu(resp->rule_id); + *rule_id = LE16_TO_CPU(resp->rule_id); if (rules_used) - *rules_used = le16_to_cpu(resp->mirror_rules_used); + *rules_used = LE16_TO_CPU(resp->mirror_rules_used); if (rules_free) - *rules_free = le16_to_cpu(resp->mirror_rules_free); + *rules_free = LE16_TO_CPU(resp->mirror_rules_free); } return status; } @@ -2818,8 +2938,8 @@ static i40e_status i40e_mirrorrule_op(struct i40e_hw *hw, * @mr_list: list of mirrored VSI SEIDs or VLAN IDs * @cmd_details: pointer to command details structure or NULL * @rule_id: Rule ID returned from FW - * @rule_used: Number of rules used in internal switch - * @rule_free: Number of rules free in internal switch + * @rules_used: Number of rules used in internal switch + * @rules_free: Number of rules free in internal switch * * Add mirror rule. Mirror rules are supported for VEBs or VEPA elements only **/ @@ -2849,8 +2969,8 @@ i40e_status i40e_aq_add_mirrorrule(struct i40e_hw *hw, u16 sw_seid, * add_mirrorrule. * @mr_list: list of mirrored VLAN IDs to be removed * @cmd_details: pointer to command details structure or NULL - * @rule_used: Number of rules used in internal switch - * @rule_free: Number of rules free in internal switch + * @rules_used: Number of rules used in internal switch + * @rules_free: Number of rules free in internal switch * * Delete a mirror rule. Mirror rules are supported for VEBs/VEPA elements only **/ @@ -2877,7 +2997,7 @@ i40e_status i40e_aq_delete_mirrorrule(struct i40e_hw *hw, u16 sw_seid, /** * i40e_aq_send_msg_to_vf * @hw: pointer to the hardware structure - * @vfid: VF id to send msg + * @vfid: vf id to send msg * @v_opcode: opcodes for VF-PF communication * @v_retval: return error code * @msg: pointer to the msg buffer @@ -2896,16 +3016,16 @@ i40e_status i40e_aq_send_msg_to_vf(struct i40e_hw *hw, u16 vfid, i40e_status status; i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_send_msg_to_vf); - cmd->id = cpu_to_le32(vfid); - desc.cookie_high = cpu_to_le32(v_opcode); - desc.cookie_low = cpu_to_le32(v_retval); - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_SI); + cmd->id = CPU_TO_LE32(vfid); + desc.cookie_high = CPU_TO_LE32(v_opcode); + desc.cookie_low = CPU_TO_LE32(v_retval); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_SI); if (msglen) { - desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); if (msglen > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); - desc.datalen = cpu_to_le16(msglen); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); + desc.datalen = CPU_TO_LE16(msglen); } status = i40e_asq_send_command(hw, &desc, msg, msglen, cmd_details); @@ -2935,13 +3055,13 @@ i40e_status i40e_aq_debug_read_register(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_debug_read_reg); - cmd_resp->address = cpu_to_le32(reg_addr); + cmd_resp->address = CPU_TO_LE32(reg_addr); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); - if (!status) { - *reg_val = ((u64)le32_to_cpu(cmd_resp->value_high) << 32) | - (u64)le32_to_cpu(cmd_resp->value_low); + if (status == I40E_SUCCESS) { + *reg_val = ((u64)LE32_TO_CPU(cmd_resp->value_high) << 32) | + (u64)LE32_TO_CPU(cmd_resp->value_low); } return status; @@ -2957,8 +3077,8 @@ i40e_status i40e_aq_debug_read_register(struct i40e_hw *hw, * Write to a register using the admin queue commands **/ i40e_status i40e_aq_debug_write_register(struct i40e_hw *hw, - u32 reg_addr, u64 reg_val, - struct i40e_asq_cmd_details *cmd_details) + u32 reg_addr, u64 reg_val, + struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_debug_reg_read_write *cmd = @@ -2967,9 +3087,9 @@ i40e_status i40e_aq_debug_write_register(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_debug_write_reg); - cmd->address = cpu_to_le32(reg_addr); - cmd->value_high = cpu_to_le32((u32)(reg_val >> 32)); - cmd->value_low = cpu_to_le32((u32)(reg_val & 0xFFFFFFFF)); + cmd->address = CPU_TO_LE32(reg_addr); + cmd->value_high = CPU_TO_LE32((u32)(reg_val >> 32)); + cmd->value_low = CPU_TO_LE32((u32)(reg_val & 0xFFFFFFFF)); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); @@ -3000,9 +3120,9 @@ i40e_status i40e_aq_request_resource(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_request_resource); - cmd_resp->resource_id = cpu_to_le16(resource); - cmd_resp->access_type = cpu_to_le16(access); - cmd_resp->resource_number = cpu_to_le32(sdp_number); + cmd_resp->resource_id = CPU_TO_LE16(resource); + cmd_resp->access_type = CPU_TO_LE16(access); + cmd_resp->resource_number = CPU_TO_LE32(sdp_number); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); /* The completion specifies the maximum time in ms that the driver @@ -3011,8 +3131,8 @@ i40e_status i40e_aq_request_resource(struct i40e_hw *hw, * busy return value and the timeout field indicates the maximum time * the current owner of the resource has to free it. */ - if (!status || hw->aq.asq_last_status == I40E_AQ_RC_EBUSY) - *timeout = le32_to_cpu(cmd_resp->timeout); + if (status == I40E_SUCCESS || hw->aq.asq_last_status == I40E_AQ_RC_EBUSY) + *timeout = LE32_TO_CPU(cmd_resp->timeout); return status; } @@ -3038,8 +3158,8 @@ i40e_status i40e_aq_release_resource(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_release_resource); - cmd->resource_id = cpu_to_le16(resource); - cmd->resource_number = cpu_to_le32(sdp_number); + cmd->resource_id = CPU_TO_LE16(resource); + cmd->resource_number = CPU_TO_LE32(sdp_number); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); @@ -3080,12 +3200,12 @@ i40e_status i40e_aq_read_nvm(struct i40e_hw *hw, u8 module_pointer, if (last_command) cmd->command_flags |= I40E_AQ_NVM_LAST_CMD; cmd->module_pointer = module_pointer; - cmd->offset = cpu_to_le32(offset); - cmd->length = cpu_to_le16(length); + cmd->offset = CPU_TO_LE32(offset); + cmd->length = CPU_TO_LE16(length); - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); if (length > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); status = i40e_asq_send_command(hw, &desc, data, length, cmd_details); @@ -3093,6 +3213,100 @@ i40e_aq_read_nvm_exit: return status; } +/** + * i40e_aq_read_nvm_config - read an nvm config block + * @hw: pointer to the hw struct + * @cmd_flags: NVM access admin command bits + * @field_id: field or feature id + * @data: buffer for result + * @buf_size: buffer size + * @element_count: pointer to count of elements read by FW + * @cmd_details: pointer to command details structure or NULL + **/ +i40e_status i40e_aq_read_nvm_config(struct i40e_hw *hw, + u8 cmd_flags, u32 field_id, void *data, + u16 buf_size, u16 *element_count, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_nvm_config_read *cmd = + (struct i40e_aqc_nvm_config_read *)&desc.params.raw; + i40e_status status; + + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_nvm_config_read); + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF)); + if (buf_size > I40E_AQ_LARGE_BUF) + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); + + cmd->cmd_flags = CPU_TO_LE16(cmd_flags); + cmd->element_id = CPU_TO_LE16((u16)(0xffff & field_id)); + if (cmd_flags & I40E_AQ_ANVM_FEATURE_OR_IMMEDIATE_MASK) + cmd->element_id_msw = CPU_TO_LE16((u16)(field_id >> 16)); + else + cmd->element_id_msw = 0; + + status = i40e_asq_send_command(hw, &desc, data, buf_size, cmd_details); + + if (!status && element_count) + *element_count = LE16_TO_CPU(cmd->element_count); + + return status; +} + +/** + * i40e_aq_write_nvm_config - write an nvm config block + * @hw: pointer to the hw struct + * @cmd_flags: NVM access admin command bits + * @data: buffer for result + * @buf_size: buffer size + * @element_count: count of elements to be written + * @cmd_details: pointer to command details structure or NULL + **/ +i40e_status i40e_aq_write_nvm_config(struct i40e_hw *hw, + u8 cmd_flags, void *data, u16 buf_size, + u16 element_count, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_nvm_config_write *cmd = + (struct i40e_aqc_nvm_config_write *)&desc.params.raw; + i40e_status status; + + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_nvm_config_write); + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + if (buf_size > I40E_AQ_LARGE_BUF) + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); + + cmd->element_count = CPU_TO_LE16(element_count); + cmd->cmd_flags = CPU_TO_LE16(cmd_flags); + status = i40e_asq_send_command(hw, &desc, data, buf_size, cmd_details); + + return status; +} + +/** + * i40e_aq_oem_post_update - triggers an OEM specific flow after update + * @hw: pointer to the hw struct + * @buff: buffer for result + * @buff_size: buffer size + * @cmd_details: pointer to command details structure or NULL + **/ +i40e_status i40e_aq_oem_post_update(struct i40e_hw *hw, + void *buff, u16 buff_size, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + i40e_status status; + + + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_oem_post_update); + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + if (status && LE16_TO_CPU(desc.retval) == I40E_AQ_RC_ESRCH) + status = I40E_ERR_NOT_IMPLEMENTED; + + return status; +} + /** * i40e_aq_erase_nvm * @hw: pointer to the hw struct @@ -3105,8 +3319,8 @@ i40e_aq_read_nvm_exit: * Erase the NVM sector using the admin queue commands **/ i40e_status i40e_aq_erase_nvm(struct i40e_hw *hw, u8 module_pointer, - u32 offset, u16 length, bool last_command, - struct i40e_asq_cmd_details *cmd_details) + u32 offset, u16 length, bool last_command, + struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_nvm_update *cmd = @@ -3125,8 +3339,8 @@ i40e_status i40e_aq_erase_nvm(struct i40e_hw *hw, u8 module_pointer, if (last_command) cmd->command_flags |= I40E_AQ_NVM_LAST_CMD; cmd->module_pointer = module_pointer; - cmd->offset = cpu_to_le32(offset); - cmd->length = cpu_to_le16(length); + cmd->offset = CPU_TO_LE32(offset); + cmd->length = CPU_TO_LE16(length); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); @@ -3151,29 +3365,33 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, u32 valid_functions, num_functions; u32 number, logical_id, phys_id; struct i40e_hw_capabilities *p; + i40e_status status; + u16 id, ocp_cfg_word0; u8 major_rev; u32 i = 0; - u16 id; cap = (struct i40e_aqc_list_capabilities_element_resp *) buff; if (list_type_opc == i40e_aqc_opc_list_dev_capabilities) - p = &hw->dev_caps; + p = (struct i40e_hw_capabilities *)&hw->dev_caps; else if (list_type_opc == i40e_aqc_opc_list_func_capabilities) - p = &hw->func_caps; + p = (struct i40e_hw_capabilities *)&hw->func_caps; else return; for (i = 0; i < cap_count; i++, cap++) { - id = le16_to_cpu(cap->id); - number = le32_to_cpu(cap->number); - logical_id = le32_to_cpu(cap->logical_id); - phys_id = le32_to_cpu(cap->phys_id); + id = LE16_TO_CPU(cap->id); + number = LE32_TO_CPU(cap->number); + logical_id = LE32_TO_CPU(cap->logical_id); + phys_id = LE32_TO_CPU(cap->phys_id); major_rev = cap->major_rev; switch (id) { case I40E_AQ_CAP_ID_SWITCH_MODE: p->switch_mode = number; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: Switch mode = %d\n", + p->switch_mode); break; case I40E_AQ_CAP_ID_MNG_MODE: p->management_mode = number; @@ -3185,38 +3403,67 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, } else { p->mng_protocols_over_mctp = 0; } + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: Management Mode = %d\n", + p->management_mode); break; case I40E_AQ_CAP_ID_NPAR_ACTIVE: p->npar_enable = number; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: NPAR enable = %d\n", + p->npar_enable); break; case I40E_AQ_CAP_ID_OS2BMC_CAP: p->os2bmc = number; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: OS2BMC = %d\n", p->os2bmc); break; case I40E_AQ_CAP_ID_FUNCTIONS_VALID: p->valid_functions = number; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: Valid Functions = %d\n", + p->valid_functions); break; case I40E_AQ_CAP_ID_SRIOV: if (number == 1) p->sr_iov_1_1 = true; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: SR-IOV = %d\n", + p->sr_iov_1_1); break; case I40E_AQ_CAP_ID_VF: p->num_vfs = number; p->vf_base_id = logical_id; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: VF count = %d\n", + p->num_vfs); + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: VF base_id = %d\n", + p->vf_base_id); break; case I40E_AQ_CAP_ID_VMDQ: if (number == 1) p->vmdq = true; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: VMDQ = %d\n", p->vmdq); break; case I40E_AQ_CAP_ID_8021QBG: if (number == 1) p->evb_802_1_qbg = true; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: 802.1Qbg = %d\n", number); break; case I40E_AQ_CAP_ID_8021QBR: if (number == 1) p->evb_802_1_qbh = true; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: 802.1Qbh = %d\n", number); break; case I40E_AQ_CAP_ID_VSI: p->num_vsis = number; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: VSI count = %d\n", + p->num_vsis); break; case I40E_AQ_CAP_ID_DCB: if (number == 1) { @@ -3224,27 +3471,56 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, p->enabled_tcmap = logical_id; p->maxtc = phys_id; } + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: DCB = %d\n", p->dcb); + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: TC Mapping = %d\n", + logical_id); + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: TC Max = %d\n", p->maxtc); break; case I40E_AQ_CAP_ID_FCOE: if (number == 1) p->fcoe = true; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: FCOE = %d\n", p->fcoe); break; case I40E_AQ_CAP_ID_ISCSI: if (number == 1) p->iscsi = true; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: iSCSI = %d\n", p->iscsi); break; case I40E_AQ_CAP_ID_RSS: p->rss = true; p->rss_table_size = number; p->rss_table_entry_width = logical_id; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: RSS = %d\n", p->rss); + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: RSS table size = %d\n", + p->rss_table_size); + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: RSS table width = %d\n", + p->rss_table_entry_width); break; case I40E_AQ_CAP_ID_RXQ: p->num_rx_qp = number; p->base_queue = phys_id; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: Rx QP = %d\n", number); + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: base_queue = %d\n", + p->base_queue); break; case I40E_AQ_CAP_ID_TXQ: p->num_tx_qp = number; p->base_queue = phys_id; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: Tx QP = %d\n", number); + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: base_queue = %d\n", + p->base_queue); break; case I40E_AQ_CAP_ID_MSIX: p->num_msix_vectors = number; @@ -3254,6 +3530,9 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, break; case I40E_AQ_CAP_ID_VF_MSIX: p->num_msix_vectors_vf = number; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: MSIX VF vector count = %d\n", + p->num_msix_vectors_vf); break; case I40E_AQ_CAP_ID_FLEX10: if (major_rev == 1) { @@ -3270,41 +3549,72 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, } p->flex10_mode = logical_id; p->flex10_status = phys_id; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: Flex10 mode = %d\n", + p->flex10_mode); + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: Flex10 status = %d\n", + p->flex10_status); break; case I40E_AQ_CAP_ID_CEM: if (number == 1) p->mgmt_cem = true; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: CEM = %d\n", p->mgmt_cem); break; case I40E_AQ_CAP_ID_IWARP: if (number == 1) p->iwarp = true; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: iWARP = %d\n", p->iwarp); break; case I40E_AQ_CAP_ID_LED: if (phys_id < I40E_HW_CAP_MAX_GPIO) p->led[phys_id] = true; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: LED - PIN %d\n", phys_id); break; case I40E_AQ_CAP_ID_SDP: if (phys_id < I40E_HW_CAP_MAX_GPIO) p->sdp[phys_id] = true; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: SDP - PIN %d\n", phys_id); break; case I40E_AQ_CAP_ID_MDIO: if (number == 1) { p->mdio_port_num = phys_id; p->mdio_port_mode = logical_id; } + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: MDIO port number = %d\n", + p->mdio_port_num); + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: MDIO port mode = %d\n", + p->mdio_port_mode); break; case I40E_AQ_CAP_ID_1588: if (number == 1) p->ieee_1588 = true; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: IEEE 1588 = %d\n", + p->ieee_1588); break; case I40E_AQ_CAP_ID_FLOW_DIRECTOR: p->fd = true; p->fd_filters_guaranteed = number; p->fd_filters_best_effort = logical_id; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: Flow Director = 1\n"); + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: Guaranteed FD filters = %d\n", + p->fd_filters_guaranteed); break; case I40E_AQ_CAP_ID_WSR_PROT: p->wr_csr_prot = (u64)number; p->wr_csr_prot |= (u64)logical_id << 32; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: wr_csr_prot = 0x%llX\n\n", + (p->wr_csr_prot & 0xffff)); break; case I40E_AQ_CAP_ID_NVM_MGMT: if (number & I40E_NVM_MGMT_SEC_REV_DISABLED) @@ -3312,6 +3622,19 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, if (number & I40E_NVM_MGMT_UPDATE_DISABLED) p->update_disabled = true; break; + case I40E_AQ_CAP_ID_WOL_AND_PROXY: + hw->num_wol_proxy_filters = (u16)number; + hw->wol_proxy_vsi_seid = (u16)logical_id; + p->apm_wol_support = phys_id & I40E_WOL_SUPPORT_MASK; + if (phys_id & I40E_ACPI_PROGRAMMING_METHOD_MASK) + p->acpi_prog_method = I40E_ACPI_PROGRAMMING_METHOD_AQC_FPK; + else + p->acpi_prog_method = I40E_ACPI_PROGRAMMING_METHOD_HW_FVL; + p->proxy_support = (phys_id & I40E_PROXY_SUPPORT_MASK) ? 1 : 0; + i40e_debug(hw, I40E_DEBUG_INIT, + "HW Capability: WOL proxy filters = %d\n", + hw->num_wol_proxy_filters); + break; default: break; } @@ -3320,11 +3643,8 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, if (p->fcoe) i40e_debug(hw, I40E_DEBUG_ALL, "device is FCoE capable\n"); - /* Software override ensuring FCoE is disabled if npar or mfp - * mode because it is not supported in these modes. - */ - if (p->npar_enable || p->flex10_enable) - p->fcoe = false; + /* Always disable FCoE if compiled without the I40E_FCOE_ENA flag */ + p->fcoe = false; /* count the enabled ports (aka the "not disabled" ports) */ hw->num_ports = 0; @@ -3340,6 +3660,26 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, hw->num_ports++; } + /* OCP cards case: if a mezz is removed the ethernet port is at + * disabled state in PRTGEN_CNF register. Additional NVM read is + * needed in order to check if we are dealing with OCP card. + * Those cards have 4 PFs at minimum, so using PRTGEN_CNF for counting + * physical ports results in wrong partition id calculation and thus + * not supporting WoL. + */ + if (hw->mac.type == I40E_MAC_X722) { + if (i40e_acquire_nvm(hw, I40E_RESOURCE_READ) == I40E_SUCCESS) { + status = i40e_aq_read_nvm(hw, I40E_SR_EMP_MODULE_PTR, + 2 * I40E_SR_OCP_CFG_WORD0, + sizeof(ocp_cfg_word0), + &ocp_cfg_word0, true, NULL); + if (status == I40E_SUCCESS && + (ocp_cfg_word0 & I40E_SR_OCP_ENABLED)) + hw->num_ports = 4; + i40e_release_nvm(hw); + } + } + valid_functions = p->valid_functions; num_functions = 0; while (valid_functions) { @@ -3380,7 +3720,7 @@ i40e_status i40e_aq_discover_capabilities(struct i40e_hw *hw, { struct i40e_aqc_list_capabilites *cmd; struct i40e_aq_desc desc; - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; cmd = (struct i40e_aqc_list_capabilites *)&desc.params.raw; @@ -3392,17 +3732,17 @@ i40e_status i40e_aq_discover_capabilities(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, list_type_opc); - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); if (buff_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details); - *data_size = le16_to_cpu(desc.datalen); + *data_size = LE16_TO_CPU(desc.datalen); if (status) goto exit; - i40e_parse_discover_capabilities(hw, buff, le32_to_cpu(cmd->count), + i40e_parse_discover_capabilities(hw, buff, LE32_TO_CPU(cmd->count), list_type_opc); exit: @@ -3417,14 +3757,15 @@ exit: * @length: length of the section to be written (in bytes from the offset) * @data: command buffer (size [bytes] = length) * @last_command: tells if this is the last command in a series + * @preservation_flags: Preservation mode flags * @cmd_details: pointer to command details structure or NULL * * Update the NVM using the admin queue commands **/ i40e_status i40e_aq_update_nvm(struct i40e_hw *hw, u8 module_pointer, - u32 offset, u16 length, void *data, - bool last_command, - struct i40e_asq_cmd_details *cmd_details) + u32 offset, u16 length, void *data, + bool last_command, u8 preservation_flags, + struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_nvm_update *cmd = @@ -3442,13 +3783,23 @@ i40e_status i40e_aq_update_nvm(struct i40e_hw *hw, u8 module_pointer, /* If this is the last command in a series, set the proper flag. */ if (last_command) cmd->command_flags |= I40E_AQ_NVM_LAST_CMD; + if (hw->mac.type == I40E_MAC_X722) { + if (preservation_flags == I40E_NVM_PRESERVATION_FLAGS_SELECTED) + cmd->command_flags |= + (I40E_AQ_NVM_PRESERVATION_FLAGS_SELECTED << + I40E_AQ_NVM_PRESERVATION_FLAGS_SHIFT); + else if (preservation_flags == I40E_NVM_PRESERVATION_FLAGS_ALL) + cmd->command_flags |= + (I40E_AQ_NVM_PRESERVATION_FLAGS_ALL << + I40E_AQ_NVM_PRESERVATION_FLAGS_SHIFT); + } cmd->module_pointer = module_pointer; - cmd->offset = cpu_to_le32(offset); - cmd->length = cpu_to_le16(length); + cmd->offset = CPU_TO_LE32(offset); + cmd->length = CPU_TO_LE16(length); - desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); if (length > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); status = i40e_asq_send_command(hw, &desc, data, length, cmd_details); @@ -3457,58 +3808,152 @@ i40e_aq_update_nvm_exit: } /** - * i40e_aq_get_lldp_mib + * i40e_aq_rearrange_nvm * @hw: pointer to the hw struct - * @bridge_type: type of bridge requested - * @mib_type: Local, Remote or both Local and Remote MIBs - * @buff: pointer to a user supplied buffer to store the MIB block - * @buff_size: size of the buffer (in bytes) - * @local_len : length of the returned Local LLDP MIB - * @remote_len: length of the returned Remote LLDP MIB + * @rearrange_nvm: defines direction of rearrangement * @cmd_details: pointer to command details structure or NULL * - * Requests the complete LLDP MIB (entire packet). + * Rearrange NVM structure, available only for transition FW **/ -i40e_status i40e_aq_get_lldp_mib(struct i40e_hw *hw, u8 bridge_type, - u8 mib_type, void *buff, u16 buff_size, - u16 *local_len, u16 *remote_len, +i40e_status i40e_aq_rearrange_nvm(struct i40e_hw *hw, + u8 rearrange_nvm, struct i40e_asq_cmd_details *cmd_details) { - struct i40e_aq_desc desc; - struct i40e_aqc_lldp_get_mib *cmd = - (struct i40e_aqc_lldp_get_mib *)&desc.params.raw; - struct i40e_aqc_lldp_get_mib *resp = - (struct i40e_aqc_lldp_get_mib *)&desc.params.raw; + struct i40e_aqc_nvm_update *cmd; i40e_status status; + struct i40e_aq_desc desc; - if (buff_size == 0 || !buff) - return I40E_ERR_PARAM; - - i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_lldp_get_mib); - /* Indirect Command */ - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + cmd = (struct i40e_aqc_nvm_update *)&desc.params.raw; - cmd->type = mib_type & I40E_AQ_LLDP_MIB_TYPE_MASK; - cmd->type |= ((bridge_type << I40E_AQ_LLDP_BRIDGE_TYPE_SHIFT) & - I40E_AQ_LLDP_BRIDGE_TYPE_MASK); + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_nvm_update); - desc.datalen = cpu_to_le16(buff_size); + rearrange_nvm &= (I40E_AQ_NVM_REARRANGE_TO_FLAT | + I40E_AQ_NVM_REARRANGE_TO_STRUCT); - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + if (!rearrange_nvm) { + status = I40E_ERR_PARAM; + goto i40e_aq_rearrange_nvm_exit; + } + + cmd->command_flags |= rearrange_nvm; + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + +i40e_aq_rearrange_nvm_exit: + return status; +} + +/** + * i40e_aq_nvm_progress + * @hw: pointer to the hw struct + * @progress: pointer to progress returned from AQ + * @cmd_details: pointer to command details structure or NULL + * + * Gets progress of flash rearrangement process + **/ +i40e_status i40e_aq_nvm_progress(struct i40e_hw *hw, u8 *progress, + struct i40e_asq_cmd_details *cmd_details) +{ + i40e_status status; + struct i40e_aq_desc desc; + + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_nvm_progress); + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + *progress = desc.params.raw[0]; + return status; +} + +/** + * i40e_aq_get_lldp_mib + * @hw: pointer to the hw struct + * @bridge_type: type of bridge requested + * @mib_type: Local, Remote or both Local and Remote MIBs + * @buff: pointer to a user supplied buffer to store the MIB block + * @buff_size: size of the buffer (in bytes) + * @local_len : length of the returned Local LLDP MIB + * @remote_len: length of the returned Remote LLDP MIB + * @cmd_details: pointer to command details structure or NULL + * + * Requests the complete LLDP MIB (entire packet). + **/ +i40e_status i40e_aq_get_lldp_mib(struct i40e_hw *hw, u8 bridge_type, + u8 mib_type, void *buff, u16 buff_size, + u16 *local_len, u16 *remote_len, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_lldp_get_mib *cmd = + (struct i40e_aqc_lldp_get_mib *)&desc.params.raw; + struct i40e_aqc_lldp_get_mib *resp = + (struct i40e_aqc_lldp_get_mib *)&desc.params.raw; + i40e_status status; + + if (buff_size == 0 || !buff) + return I40E_ERR_PARAM; + + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_lldp_get_mib); + /* Indirect Command */ + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); + + cmd->type = mib_type & I40E_AQ_LLDP_MIB_TYPE_MASK; + cmd->type |= ((bridge_type << I40E_AQ_LLDP_BRIDGE_TYPE_SHIFT) & + I40E_AQ_LLDP_BRIDGE_TYPE_MASK); + + desc.datalen = CPU_TO_LE16(buff_size); + + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); if (buff_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details); if (!status) { if (local_len != NULL) - *local_len = le16_to_cpu(resp->local_len); + *local_len = LE16_TO_CPU(resp->local_len); if (remote_len != NULL) - *remote_len = le16_to_cpu(resp->remote_len); + *remote_len = LE16_TO_CPU(resp->remote_len); } return status; } + /** + * i40e_aq_set_lldp_mib - Set the LLDP MIB + * @hw: pointer to the hw struct + * @mib_type: Local, Remote or both Local and Remote MIBs + * @buff: pointer to a user supplied buffer to store the MIB block + * @buff_size: size of the buffer (in bytes) + * @cmd_details: pointer to command details structure or NULL + * + * Set the LLDP MIB. + **/ +i40e_status i40e_aq_set_lldp_mib(struct i40e_hw *hw, + u8 mib_type, void *buff, u16 buff_size, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_lldp_set_local_mib *cmd = + (struct i40e_aqc_lldp_set_local_mib *)&desc.params.raw; + i40e_status status; + + if (buff_size == 0 || !buff) + return I40E_ERR_PARAM; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_lldp_set_local_mib); + /* Indirect Command */ + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + if (buff_size > I40E_AQ_LARGE_BUF) + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); + desc.datalen = CPU_TO_LE16(buff_size); + + cmd->type = mib_type; + cmd->length = CPU_TO_LE16(buff_size); + cmd->address_high = CPU_TO_LE32(upper_32_bits((u64)buff)); + cmd->address_low = CPU_TO_LE32(lower_32_bits((u64)buff)); + + status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details); + return status; +} + /** * i40e_aq_cfg_lldp_mib_change_event * @hw: pointer to the hw struct @@ -3537,15 +3982,55 @@ i40e_status i40e_aq_cfg_lldp_mib_change_event(struct i40e_hw *hw, return status; } +/** + * i40e_aq_restore_lldp + * @hw: pointer to the hw struct + * @setting: pointer to factory setting variable or NULL + * @restore: True if factory settings should be restored + * @cmd_details: pointer to command details structure or NULL + * + * Restore LLDP Agent factory settings if @restore set to True. In other case + * only returns factory setting in AQ response. + **/ +enum i40e_status_code +i40e_aq_restore_lldp(struct i40e_hw *hw, u8 *setting, bool restore, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_lldp_restore *cmd = + (struct i40e_aqc_lldp_restore *)&desc.params.raw; + i40e_status status; + + if (!(hw->flags & I40E_HW_FLAG_FW_LLDP_PERSISTENT)) { + i40e_debug(hw, I40E_DEBUG_ALL, + "Restore LLDP not supported by current FW version.\n"); + return I40E_ERR_DEVICE_NOT_SUPPORTED; + } + + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_lldp_restore); + + if (restore) + cmd->command |= I40E_AQ_LLDP_AGENT_RESTORE; + + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + + if (setting) + *setting = cmd->command & 1; + + return status; +} + /** * i40e_aq_stop_lldp * @hw: pointer to the hw struct * @shutdown_agent: True if LLDP Agent needs to be Shutdown + * @persist: True if stop of LLDP should be persistent across power cycles * @cmd_details: pointer to command details structure or NULL * * Stop or Shutdown the embedded LLDP Agent **/ i40e_status i40e_aq_stop_lldp(struct i40e_hw *hw, bool shutdown_agent, + bool persist, struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; @@ -3558,6 +4043,14 @@ i40e_status i40e_aq_stop_lldp(struct i40e_hw *hw, bool shutdown_agent, if (shutdown_agent) cmd->command |= I40E_AQ_LLDP_AGENT_SHUTDOWN; + if (persist) { + if (hw->flags & I40E_HW_FLAG_FW_LLDP_PERSISTENT) + cmd->command |= I40E_AQ_LLDP_AGENT_STOP_PERSIST; + else + i40e_debug(hw, I40E_DEBUG_ALL, + "Persistent Stop LLDP not supported by current FW version.\n"); + } + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); return status; @@ -3566,11 +4059,13 @@ i40e_status i40e_aq_stop_lldp(struct i40e_hw *hw, bool shutdown_agent, /** * i40e_aq_start_lldp * @hw: pointer to the hw struct + * @persist: True if start of LLDP should be persistent across power cycles * @cmd_details: pointer to command details structure or NULL * * Start the embedded LLDP Agent on all ports. **/ i40e_status i40e_aq_start_lldp(struct i40e_hw *hw, + bool persist, struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; @@ -3582,6 +4077,45 @@ i40e_status i40e_aq_start_lldp(struct i40e_hw *hw, cmd->command = I40E_AQ_LLDP_AGENT_START; + if (persist) { + if (hw->flags & I40E_HW_FLAG_FW_LLDP_PERSISTENT) + cmd->command |= I40E_AQ_LLDP_AGENT_START_PERSIST; + else + i40e_debug(hw, I40E_DEBUG_ALL, + "Persistent Start LLDP not supported by current FW version.\n"); + } + + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + + return status; +} + +/** + * i40e_aq_set_dcb_parameters + * @hw: pointer to the hw struct + * @cmd_details: pointer to command details structure or NULL + * @dcb_enable: True if DCB configuration needs to be applied + * + **/ +enum i40e_status_code +i40e_aq_set_dcb_parameters(struct i40e_hw *hw, bool dcb_enable, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_set_dcb_parameters *cmd = + (struct i40e_aqc_set_dcb_parameters *)&desc.params.raw; + i40e_status status; + + if (!(hw->flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE)) + return I40E_ERR_DEVICE_NOT_SUPPORTED; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_set_dcb_parameters); + + if (dcb_enable) { + cmd->valid_flags = I40E_DCB_VALID; + cmd->command = I40E_AQ_DCB_SET_AGENT; + } status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); return status; @@ -3597,8 +4131,8 @@ i40e_status i40e_aq_start_lldp(struct i40e_hw *hw, * Get CEE DCBX mode operational configuration from firmware **/ i40e_status i40e_aq_get_cee_dcb_config(struct i40e_hw *hw, - void *buff, u16 buff_size, - struct i40e_asq_cmd_details *cmd_details) + void *buff, u16 buff_size, + struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; i40e_status status; @@ -3608,24 +4142,53 @@ i40e_status i40e_aq_get_cee_dcb_config(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_get_cee_dcb_cfg); - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); status = i40e_asq_send_command(hw, &desc, (void *)buff, buff_size, cmd_details); return status; } +/** + * i40e_aq_start_stop_dcbx - Start/Stop DCBx service in FW + * @hw: pointer to the hw struct + * @start_agent: True if DCBx Agent needs to be Started + * False if DCBx Agent needs to be Stopped + * @cmd_details: pointer to command details structure or NULL + * + * Start/Stop the embedded dcbx Agent + **/ +i40e_status i40e_aq_start_stop_dcbx(struct i40e_hw *hw, + bool start_agent, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_lldp_stop_start_specific_agent *cmd = + (struct i40e_aqc_lldp_stop_start_specific_agent *) + &desc.params.raw; + i40e_status status; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_lldp_stop_start_spec_agent); + + if (start_agent) + cmd->command = I40E_AQC_START_SPECIFIC_AGENT_MASK; + + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + + return status; +} + /** * i40e_aq_add_udp_tunnel * @hw: pointer to the hw struct * @udp_port: the UDP port to add in Host byte order - * @header_len: length of the tunneling header length in DWords * @protocol_index: protocol index type * @filter_index: pointer to filter index * @cmd_details: pointer to command details structure or NULL * * Note: Firmware expects the udp_port value to be in Little Endian format, - * and this function will call cpu_to_le16 to convert from Host byte order to + * and this function will call CPU_TO_LE16 to convert from Host byte order to * Little Endian order. **/ i40e_status i40e_aq_add_udp_tunnel(struct i40e_hw *hw, @@ -3642,7 +4205,7 @@ i40e_status i40e_aq_add_udp_tunnel(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_add_udp_tunnel); - cmd->udp_port = cpu_to_le16(udp_port); + cmd->udp_port = CPU_TO_LE16(udp_port); cmd->protocol_type = protocol_index; status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); @@ -3676,6 +4239,45 @@ i40e_status i40e_aq_del_udp_tunnel(struct i40e_hw *hw, u8 index, return status; } +/** + * i40e_aq_get_switch_resource_alloc (0x0204) + * @hw: pointer to the hw struct + * @num_entries: pointer to u8 to store the number of resource entries returned + * @buf: pointer to a user supplied buffer. This buffer must be large enough + * to store the resource information for all resource types. Each + * resource type is a i40e_aqc_switch_resource_alloc_data structure. + * @count: size, in bytes, of the buffer provided + * @cmd_details: pointer to command details structure or NULL + * + * Query the resources allocated to a function. + **/ +i40e_status i40e_aq_get_switch_resource_alloc(struct i40e_hw *hw, + u8 *num_entries, + struct i40e_aqc_switch_resource_alloc_element_resp *buf, + u16 count, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_get_switch_resource_alloc *cmd_resp = + (struct i40e_aqc_get_switch_resource_alloc *)&desc.params.raw; + i40e_status status; + u16 length = count * sizeof(*buf); + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_get_switch_resource_alloc); + + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); + if (length > I40E_AQ_LARGE_BUF) + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); + + status = i40e_asq_send_command(hw, &desc, buf, length, cmd_details); + + if (!status && num_entries) + *num_entries = cmd_resp->num_entries; + + return status; +} + /** * i40e_aq_delete_element - Delete switch element * @hw: pointer to the hw struct @@ -3697,9 +4299,9 @@ i40e_status i40e_aq_delete_element(struct i40e_hw *hw, u16 seid, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_delete_element); - cmd->seid = cpu_to_le16(seid); - - status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + cmd->seid = CPU_TO_LE16(seid); + status = i40e_asq_send_command_atomic(hw, &desc, NULL, 0, + cmd_details, true); return status; } @@ -3709,6 +4311,14 @@ i40e_status i40e_aq_delete_element(struct i40e_hw *hw, u16 seid, * @hw: pointer to the hw struct * @cmd_details: pointer to command details structure or NULL * + * When LLDP is handled in PF this command is used by the PF + * to notify EMP that a DCB setting is modified. + * When LLDP is handled in EMP this command is used by the PF + * to notify EMP whenever one of the following parameters get + * modified: + * - PFCLinkDelayAllowance in PRTDCB_GENC.PFCLDA + * - PCIRTT in PRTDCB_GENC.PCIRTT + * - Maximum Frame Size for non-FCoE TCs set by PRTDCB_TDPUC.MAX_TXFRAME. * EMP will return when the shared RPB settings have been * recomputed and modified. The retval field in the descriptor * will be set to 0 when RPB is modified. @@ -3772,15 +4382,15 @@ static i40e_status i40e_aq_tx_sched_cmd(struct i40e_hw *hw, u16 seid, i40e_fill_default_direct_cmd_desc(&desc, opcode); /* Indirect command */ - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); if (cmd_param_flag) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_RD); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_RD); if (buff_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); - desc.datalen = cpu_to_le16(buff_size); + desc.datalen = CPU_TO_LE16(buff_size); - cmd->vsi_seid = cpu_to_le16(seid); + cmd->vsi_seid = CPU_TO_LE16(seid); status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details); @@ -3807,8 +4417,8 @@ i40e_status i40e_aq_config_vsi_bw_limit(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_configure_vsi_bw_limit); - cmd->vsi_seid = cpu_to_le16(seid); - cmd->credit = cpu_to_le16(credit); + cmd->vsi_seid = CPU_TO_LE16(seid); + cmd->credit = CPU_TO_LE16(credit); cmd->max_credit = max_credit; status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); @@ -3838,6 +4448,7 @@ i40e_status i40e_aq_config_vsi_tc_bw(struct i40e_hw *hw, * @hw: pointer to the hw struct * @seid: seid of the switching component connected to Physical Port * @ets_data: Buffer holding ETS parameters + * @opcode: Tx scheduler AQ command opcode * @cmd_details: pointer to command details structure or NULL **/ i40e_status i40e_aq_config_switch_comp_ets(struct i40e_hw *hw, @@ -3961,7 +4572,7 @@ i40e_status i40e_aq_query_switch_comp_bw_config(struct i40e_hw *hw, * The function checks for the valid filter/context sizes being * passed for FCoE and PE. * - * Returns 0 if the values passed are valid and within + * Returns I40E_SUCCESS if the values passed are valid and within * range else returns an error. **/ static i40e_status i40e_validate_filter_settings(struct i40e_hw *hw, @@ -3970,6 +4581,7 @@ static i40e_status i40e_validate_filter_settings(struct i40e_hw *hw, u32 fcoe_cntx_size, fcoe_filt_size; u32 pe_cntx_size, pe_filt_size; u32 fcoe_fmax; + u32 val; /* Validate FCoE settings passed */ @@ -4044,7 +4656,7 @@ static i40e_status i40e_validate_filter_settings(struct i40e_hw *hw, if (fcoe_filt_size + fcoe_cntx_size > fcoe_fmax) return I40E_ERR_INVALID_SIZE; - return 0; + return I40E_SUCCESS; } /** @@ -4059,7 +4671,7 @@ static i40e_status i40e_validate_filter_settings(struct i40e_hw *hw, i40e_status i40e_set_filter_control(struct i40e_hw *hw, struct i40e_filter_control_settings *settings) { - i40e_status ret = 0; + i40e_status ret = I40E_SUCCESS; u32 hash_lut_size = 0; u32 val; @@ -4111,7 +4723,7 @@ i40e_status i40e_set_filter_control(struct i40e_hw *hw, i40e_write_rx_ctl(hw, I40E_PFQF_CTL_0, val); - return 0; + return I40E_SUCCESS; } /** @@ -4151,7 +4763,7 @@ i40e_status i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw, if (is_add) { i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_add_control_packet_filter); - cmd->queue = cpu_to_le16(queue); + cmd->queue = CPU_TO_LE16(queue); } else { i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_remove_control_packet_filter); @@ -4160,17 +4772,17 @@ i40e_status i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw, if (mac_addr) ether_addr_copy(cmd->mac, mac_addr); - cmd->etype = cpu_to_le16(ethtype); - cmd->flags = cpu_to_le16(flags); - cmd->seid = cpu_to_le16(vsi_seid); + cmd->etype = CPU_TO_LE16(ethtype); + cmd->flags = CPU_TO_LE16(flags); + cmd->seid = CPU_TO_LE16(vsi_seid); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); if (!status && stats) { - stats->mac_etype_used = le16_to_cpu(resp->mac_etype_used); - stats->etype_used = le16_to_cpu(resp->etype_used); - stats->mac_etype_free = le16_to_cpu(resp->mac_etype_free); - stats->etype_free = le16_to_cpu(resp->etype_free); + stats->mac_etype_used = LE16_TO_CPU(resp->mac_etype_used); + stats->etype_used = LE16_TO_CPU(resp->etype_used); + stats->mac_etype_free = LE16_TO_CPU(resp->mac_etype_free); + stats->etype_free = LE16_TO_CPU(resp->etype_free); } return status; @@ -4181,10 +4793,10 @@ i40e_status i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw, * @hw: pointer to the hw struct * @seid: VSI seid to add ethertype filter from **/ -#define I40E_FLOW_CONTROL_ETHTYPE 0x8808 void i40e_add_filter_to_drop_tx_flow_control_frames(struct i40e_hw *hw, u16 seid) { +#define I40E_FLOW_CONTROL_ETHTYPE 0x8808 u16 flag = I40E_AQC_ADD_CONTROL_PACKET_FLAGS_IGNORE_MAC | I40E_AQC_ADD_CONTROL_PACKET_FLAGS_DROP | I40E_AQC_ADD_CONTROL_PACKET_FLAGS_TX; @@ -4199,89 +4811,361 @@ void i40e_add_filter_to_drop_tx_flow_control_frames(struct i40e_hw *hw, } /** - * i40e_aq_alternate_read + * i40e_fix_up_geneve_vni - adjust Geneve VNI for HW issue + * @filters: list of cloud filters + * @filter_count: length of list + * + * There's an issue in the device where the Geneve VNI layout needs + * to be shifted 1 byte over from the VxLAN VNI + **/ +static void i40e_fix_up_geneve_vni( + struct i40e_aqc_cloud_filters_element_data *filters, + u8 filter_count) +{ + struct i40e_aqc_cloud_filters_element_data *f = filters; + int i; + + for (i = 0; i < filter_count; i++) { + u16 tnl_type; + u32 ti; + + tnl_type = (LE16_TO_CPU(f[i].flags) & + I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >> + I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT; + if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) { + ti = LE32_TO_CPU(f[i].tenant_id); + f[i].tenant_id = CPU_TO_LE32(ti << 8); + } + } +} + +/** + * i40e_aq_add_cloud_filters * @hw: pointer to the hardware structure - * @reg_addr0: address of first dword to be read - * @reg_val0: pointer for data read from 'reg_addr0' - * @reg_addr1: address of second dword to be read - * @reg_val1: pointer for data read from 'reg_addr1' + * @seid: VSI seid to add cloud filters from + * @filters: Buffer which contains the filters to be added + * @filter_count: number of filters contained in the buffer * - * Read one or two dwords from alternate structure. Fields are indicated - * by 'reg_addr0' and 'reg_addr1' register numbers. If 'reg_val1' pointer - * is not passed then only register at 'reg_addr0' is read. + * Set the cloud filters for a given VSI. The contents of the + * i40e_aqc_cloud_filters_element_data are filled + * in by the caller of the function. * **/ -static i40e_status i40e_aq_alternate_read(struct i40e_hw *hw, - u32 reg_addr0, u32 *reg_val0, - u32 reg_addr1, u32 *reg_val1) +i40e_status i40e_aq_add_cloud_filters(struct i40e_hw *hw, + u16 seid, + struct i40e_aqc_cloud_filters_element_data *filters, + u8 filter_count) { struct i40e_aq_desc desc; - struct i40e_aqc_alternate_write *cmd_resp = - (struct i40e_aqc_alternate_write *)&desc.params.raw; + struct i40e_aqc_add_remove_cloud_filters *cmd = + (struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw; i40e_status status; + u16 buff_len; - if (!reg_val0) - return I40E_ERR_PARAM; + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_add_cloud_filters); - i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_alternate_read); - cmd_resp->address0 = cpu_to_le32(reg_addr0); - cmd_resp->address1 = cpu_to_le32(reg_addr1); + buff_len = filter_count * sizeof(*filters); + desc.datalen = CPU_TO_LE16(buff_len); + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + cmd->num_filters = filter_count; + cmd->seid = CPU_TO_LE16(seid); - status = i40e_asq_send_command(hw, &desc, NULL, 0, NULL); + i40e_fix_up_geneve_vni(filters, filter_count); - if (!status) { - *reg_val0 = le32_to_cpu(cmd_resp->data0); + status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL); + + return status; +} + +/** + * i40e_aq_add_cloud_filters_bb + * @hw: pointer to the hardware structure + * @seid: VSI seid to add cloud filters from + * @filters: Buffer which contains the filters in big buffer to be added + * @filter_count: number of filters contained in the buffer + * + * Set the cloud filters for a given VSI. The contents of the + * i40e_aqc_cloud_filters_element_bb are filled in by the caller of the + * the function. + * + **/ +enum i40e_status_code +i40e_aq_add_cloud_filters_bb(struct i40e_hw *hw, u16 seid, + struct i40e_aqc_cloud_filters_element_bb *filters, + u8 filter_count) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_add_remove_cloud_filters *cmd = + (struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw; + i40e_status status; + u16 buff_len; + int i; - if (reg_val1) - *reg_val1 = le32_to_cpu(cmd_resp->data1); + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_add_cloud_filters); + + buff_len = filter_count * sizeof(*filters); + desc.datalen = CPU_TO_LE16(buff_len); + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + cmd->num_filters = filter_count; + cmd->seid = CPU_TO_LE16(seid); + cmd->big_buffer_flag = I40E_AQC_ADD_CLOUD_CMD_BB; + + for (i = 0; i < filter_count; i++) { + u16 tnl_type; + u32 ti; + + tnl_type = (LE16_TO_CPU(filters[i].element.flags) & + I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >> + I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT; + + /* Due to hardware eccentricities, the VNI for Geneve is shifted + * one more byte further than normally used for Tenant ID in + * other tunnel types. + */ + if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) { + ti = LE32_TO_CPU(filters[i].element.tenant_id); + filters[i].element.tenant_id = CPU_TO_LE32(ti << 8); + } } + status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL); + return status; } /** - * i40e_aq_resume_port_tx + * i40e_aq_rem_cloud_filters * @hw: pointer to the hardware structure - * @cmd_details: pointer to command details structure or NULL + * @seid: VSI seid to remove cloud filters from + * @filters: Buffer which contains the filters to be removed + * @filter_count: number of filters contained in the buffer + * + * Remove the cloud filters for a given VSI. The contents of the + * i40e_aqc_cloud_filters_element_data are filled in by the caller + * of the function. * - * Resume port's Tx traffic **/ -i40e_status i40e_aq_resume_port_tx(struct i40e_hw *hw, - struct i40e_asq_cmd_details *cmd_details) +enum i40e_status_code +i40e_aq_rem_cloud_filters(struct i40e_hw *hw, u16 seid, + struct i40e_aqc_cloud_filters_element_data *filters, + u8 filter_count) { struct i40e_aq_desc desc; + struct i40e_aqc_add_remove_cloud_filters *cmd = + (struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw; i40e_status status; + u16 buff_len; - i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_resume_port_tx); + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_remove_cloud_filters); - status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + buff_len = filter_count * sizeof(*filters); + desc.datalen = CPU_TO_LE16(buff_len); + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + cmd->num_filters = filter_count; + cmd->seid = CPU_TO_LE16(seid); + + i40e_fix_up_geneve_vni(filters, filter_count); + + status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL); return status; } /** - * i40e_set_pci_config_data - store PCI bus info - * @hw: pointer to hardware structure - * @link_status: the link status word from PCI config space + * i40e_aq_rem_cloud_filters_bb + * @hw: pointer to the hardware structure + * @seid: VSI seid to remove cloud filters from + * @filters: Buffer which contains the filters in big buffer to be removed + * @filter_count: number of filters contained in the buffer + * + * Remove the big buffer cloud filters for a given VSI. The contents of the + * i40e_aqc_cloud_filters_element_bb are filled in by the caller of the + * function. * - * Stores the PCI bus info (speed, width, type) within the i40e_hw structure **/ -void i40e_set_pci_config_data(struct i40e_hw *hw, u16 link_status) +enum i40e_status_code +i40e_aq_rem_cloud_filters_bb(struct i40e_hw *hw, u16 seid, + struct i40e_aqc_cloud_filters_element_bb *filters, + u8 filter_count) { - hw->bus.type = i40e_bus_type_pci_express; + struct i40e_aq_desc desc; + struct i40e_aqc_add_remove_cloud_filters *cmd = + (struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw; + i40e_status status; + u16 buff_len; + int i; - switch (link_status & PCI_EXP_LNKSTA_NLW) { - case PCI_EXP_LNKSTA_NLW_X1: - hw->bus.width = i40e_bus_width_pcie_x1; - break; - case PCI_EXP_LNKSTA_NLW_X2: - hw->bus.width = i40e_bus_width_pcie_x2; - break; - case PCI_EXP_LNKSTA_NLW_X4: - hw->bus.width = i40e_bus_width_pcie_x4; - break; - case PCI_EXP_LNKSTA_NLW_X8: - hw->bus.width = i40e_bus_width_pcie_x8; + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_remove_cloud_filters); + + buff_len = filter_count * sizeof(*filters); + desc.datalen = CPU_TO_LE16(buff_len); + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + cmd->num_filters = filter_count; + cmd->seid = CPU_TO_LE16(seid); + cmd->big_buffer_flag = I40E_AQC_ADD_CLOUD_CMD_BB; + + for (i = 0; i < filter_count; i++) { + u16 tnl_type; + u32 ti; + + tnl_type = (LE16_TO_CPU(filters[i].element.flags) & + I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >> + I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT; + + /* Due to hardware eccentricities, the VNI for Geneve is shifted + * one more byte further than normally used for Tenant ID in + * other tunnel types. + */ + if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) { + ti = LE32_TO_CPU(filters[i].element.tenant_id); + filters[i].element.tenant_id = CPU_TO_LE32(ti << 8); + } + } + + status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL); + + return status; +} + +/** + * i40e_aq_replace_cloud_filters - Replace cloud filter command + * @hw: pointer to the hw struct + * @filters: pointer to the i40e_aqc_replace_cloud_filter_cmd struct + * @cmd_buf: pointer to the i40e_aqc_replace_cloud_filter_cmd_buf struct + * + **/ +enum +i40e_status_code i40e_aq_replace_cloud_filters(struct i40e_hw *hw, + struct i40e_aqc_replace_cloud_filters_cmd *filters, + struct i40e_aqc_replace_cloud_filters_cmd_buf *cmd_buf) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_replace_cloud_filters_cmd *cmd = + (struct i40e_aqc_replace_cloud_filters_cmd *)&desc.params.raw; + i40e_status status = I40E_SUCCESS; + int i = 0; + + /* X722 doesn't support this command */ + if (hw->mac.type == I40E_MAC_X722) + return I40E_ERR_DEVICE_NOT_SUPPORTED; + + /* need FW version greater than 6.00 */ + if (hw->aq.fw_maj_ver < 6) + return I40E_NOT_SUPPORTED; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_replace_cloud_filters); + + desc.datalen = CPU_TO_LE16(32); + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD)); + cmd->old_filter_type = filters->old_filter_type; + cmd->new_filter_type = filters->new_filter_type; + cmd->valid_flags = filters->valid_flags; + cmd->tr_bit = filters->tr_bit; + cmd->tr_bit2 = filters->tr_bit2; + + status = i40e_asq_send_command(hw, &desc, cmd_buf, + sizeof(struct i40e_aqc_replace_cloud_filters_cmd_buf), NULL); + + /* for get cloud filters command */ + for (i = 0; i < 32; i += 4) { + cmd_buf->filters[i / 4].filter_type = cmd_buf->data[i]; + cmd_buf->filters[i / 4].input[0] = cmd_buf->data[i + 1]; + cmd_buf->filters[i / 4].input[1] = cmd_buf->data[i + 2]; + cmd_buf->filters[i / 4].input[2] = cmd_buf->data[i + 3]; + } + + return status; +} + +/** + * i40e_aq_alternate_read + * @hw: pointer to the hardware structure + * @reg_addr0: address of first dword to be read + * @reg_val0: pointer for data read from 'reg_addr0' + * @reg_addr1: address of second dword to be read + * @reg_val1: pointer for data read from 'reg_addr1' + * + * Read one or two dwords from alternate structure. Fields are indicated + * by 'reg_addr0' and 'reg_addr1' register numbers. If 'reg_val1' pointer + * is not passed then only register at 'reg_addr0' is read. + * + **/ +i40e_status i40e_aq_alternate_read(struct i40e_hw *hw, + u32 reg_addr0, u32 *reg_val0, + u32 reg_addr1, u32 *reg_val1) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_alternate_write *cmd_resp = + (struct i40e_aqc_alternate_write *)&desc.params.raw; + i40e_status status; + + if (reg_val0 == NULL) + return I40E_ERR_PARAM; + + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_alternate_read); + cmd_resp->address0 = CPU_TO_LE32(reg_addr0); + cmd_resp->address1 = CPU_TO_LE32(reg_addr1); + + status = i40e_asq_send_command(hw, &desc, NULL, 0, NULL); + + if (status == I40E_SUCCESS) { + *reg_val0 = LE32_TO_CPU(cmd_resp->data0); + + if (reg_val1 != NULL) + *reg_val1 = LE32_TO_CPU(cmd_resp->data1); + } + + return status; +} + +/** + * i40e_aq_resume_port_tx + * @hw: pointer to the hardware structure + * @cmd_details: pointer to command details structure or NULL + * + * Resume port's Tx traffic + **/ +i40e_status i40e_aq_resume_port_tx(struct i40e_hw *hw, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + i40e_status status; + + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_resume_port_tx); + + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + + return status; +} + +/** + * i40e_set_pci_config_data - store PCI bus info + * @hw: pointer to hardware structure + * @link_status: the link status word from PCI config space + * + * Stores the PCI bus info (speed, width, type) within the i40e_hw structure + **/ +void i40e_set_pci_config_data(struct i40e_hw *hw, u16 link_status) +{ + hw->bus.type = i40e_bus_type_pci_express; + + switch (link_status & PCI_EXP_LNKSTA_NLW) { + case PCI_EXP_LNKSTA_NLW_X1: + hw->bus.width = i40e_bus_width_pcie_x1; + break; + case PCI_EXP_LNKSTA_NLW_X2: + hw->bus.width = i40e_bus_width_pcie_x2; + break; + case PCI_EXP_LNKSTA_NLW_X4: + hw->bus.width = i40e_bus_width_pcie_x4; + break; + case PCI_EXP_LNKSTA_NLW_X8: + hw->bus.width = i40e_bus_width_pcie_x8; break; default: hw->bus.width = i40e_bus_width_unknown; @@ -4315,15 +5199,16 @@ void i40e_set_pci_config_data(struct i40e_hw *hw, u16 link_status) * @ret_buff_size: actual buffer size returned * @ret_next_table: next block to read * @ret_next_index: next index to read + * @cmd_details: pointer to command details structure or NULL * * Dump internal FW/HW data for debug purposes. * **/ i40e_status i40e_aq_debug_dump(struct i40e_hw *hw, u8 cluster_id, - u8 table_id, u32 start_index, u16 buff_size, - void *buff, u16 *ret_buff_size, - u8 *ret_next_table, u32 *ret_next_index, - struct i40e_asq_cmd_details *cmd_details) + u8 table_id, u32 start_index, u16 buff_size, + void *buff, u16 *ret_buff_size, + u8 *ret_next_table, u32 *ret_next_index, + struct i40e_asq_cmd_details *cmd_details) { struct i40e_aq_desc desc; struct i40e_aqc_debug_dump_internals *cmd = @@ -4338,24 +5223,24 @@ i40e_status i40e_aq_debug_dump(struct i40e_hw *hw, u8 cluster_id, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_debug_dump_internals); /* Indirect Command */ - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); if (buff_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); cmd->cluster_id = cluster_id; cmd->table_id = table_id; - cmd->idx = cpu_to_le32(start_index); + cmd->idx = CPU_TO_LE32(start_index); - desc.datalen = cpu_to_le16(buff_size); + desc.datalen = CPU_TO_LE16(buff_size); status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details); if (!status) { - if (ret_buff_size) - *ret_buff_size = le16_to_cpu(desc.datalen); - if (ret_next_table) + if (ret_buff_size != NULL) + *ret_buff_size = LE16_TO_CPU(desc.datalen); + if (ret_next_table != NULL) *ret_next_table = resp->table_id; - if (ret_next_index) - *ret_next_index = le32_to_cpu(resp->idx); + if (ret_next_index != NULL) + *ret_next_index = LE32_TO_CPU(resp->idx); } return status; @@ -4372,8 +5257,8 @@ i40e_status i40e_aq_debug_dump(struct i40e_hw *hw, u8 cluster_id, * Read bw from the alternate ram for the given pf **/ i40e_status i40e_read_bw_from_alt_ram(struct i40e_hw *hw, - u32 *max_bw, u32 *min_bw, - bool *min_valid, bool *max_valid) + u32 *max_bw, u32 *min_bw, + bool *min_valid, bool *max_valid) { i40e_status status; u32 max_bw_addr, min_bw_addr; @@ -4420,19 +5305,15 @@ i40e_status i40e_aq_configure_partition_bw(struct i40e_hw *hw, u16 bwd_size = sizeof(*bw_data); i40e_fill_default_direct_cmd_desc(&desc, - i40e_aqc_opc_configure_partition_bw); + i40e_aqc_opc_configure_partition_bw); /* Indirect command */ - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_RD); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_RD); - if (bwd_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.datalen = CPU_TO_LE16(bwd_size); - desc.datalen = cpu_to_le16(bwd_size); - - status = i40e_asq_send_command(hw, &desc, bw_data, bwd_size, - cmd_details); + status = i40e_asq_send_command(hw, &desc, bw_data, bwd_size, cmd_details); return status; } @@ -4441,13 +5322,13 @@ i40e_status i40e_aq_configure_partition_bw(struct i40e_hw *hw, * i40e_read_phy_register_clause22 * @hw: pointer to the HW structure * @reg: register address in the page - * @phy_adr: PHY address on MDIO interface + * @phy_addr: PHY address on MDIO interface * @value: PHY register value * * Reads specified PHY register value **/ i40e_status i40e_read_phy_register_clause22(struct i40e_hw *hw, - u16 reg, u8 phy_addr, u16 *value) + u16 reg, u8 phy_addr, u16 *value) { i40e_status status = I40E_ERR_TIMEOUT; u8 port_num = (u8)hw->func_caps.mdio_port_num; @@ -4463,7 +5344,7 @@ i40e_status i40e_read_phy_register_clause22(struct i40e_hw *hw, do { command = rd32(hw, I40E_GLGEN_MSCA(port_num)); if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) { - status = 0; + status = I40E_SUCCESS; break; } udelay(10); @@ -4486,13 +5367,13 @@ i40e_status i40e_read_phy_register_clause22(struct i40e_hw *hw, * i40e_write_phy_register_clause22 * @hw: pointer to the HW structure * @reg: register address in the page - * @phy_adr: PHY address on MDIO interface + * @phy_addr: PHY address on MDIO interface * @value: PHY register value * * Writes specified PHY register value **/ i40e_status i40e_write_phy_register_clause22(struct i40e_hw *hw, - u16 reg, u8 phy_addr, u16 value) + u16 reg, u8 phy_addr, u16 value) { i40e_status status = I40E_ERR_TIMEOUT; u8 port_num = (u8)hw->func_caps.mdio_port_num; @@ -4512,7 +5393,7 @@ i40e_status i40e_write_phy_register_clause22(struct i40e_hw *hw, do { command = rd32(hw, I40E_GLGEN_MSCA(port_num)); if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) { - status = 0; + status = I40E_SUCCESS; break; } udelay(10); @@ -4527,7 +5408,7 @@ i40e_status i40e_write_phy_register_clause22(struct i40e_hw *hw, * @hw: pointer to the HW structure * @page: registers page number * @reg: register address in the page - * @phy_adr: PHY address on MDIO interface + * @phy_addr: PHY address on MDIO interface * @value: PHY register value * * Reads specified PHY register value @@ -4536,9 +5417,9 @@ i40e_status i40e_read_phy_register_clause45(struct i40e_hw *hw, u8 page, u16 reg, u8 phy_addr, u16 *value) { i40e_status status = I40E_ERR_TIMEOUT; - u32 command = 0; + u32 command = 0; u16 retry = 1000; - u8 port_num = hw->func_caps.mdio_port_num; + u8 port_num = (u8)hw->func_caps.mdio_port_num; command = (reg << I40E_GLGEN_MSCA_MDIADD_SHIFT) | (page << I40E_GLGEN_MSCA_DEVADD_SHIFT) | @@ -4551,10 +5432,10 @@ i40e_status i40e_read_phy_register_clause45(struct i40e_hw *hw, do { command = rd32(hw, I40E_GLGEN_MSCA(port_num)); if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) { - status = 0; + status = I40E_SUCCESS; break; } - usleep_range(10, 20); + udelay(10); retry--; } while (retry); @@ -4576,10 +5457,10 @@ i40e_status i40e_read_phy_register_clause45(struct i40e_hw *hw, do { command = rd32(hw, I40E_GLGEN_MSCA(port_num)); if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) { - status = 0; + status = I40E_SUCCESS; break; } - usleep_range(10, 20); + udelay(10); retry--; } while (retry); @@ -4601,7 +5482,7 @@ phy_read_end: * @hw: pointer to the HW structure * @page: registers page number * @reg: register address in the page - * @phy_adr: PHY address on MDIO interface + * @phy_addr: PHY address on MDIO interface * @value: PHY register value * * Writes value to specified PHY register @@ -4610,9 +5491,9 @@ i40e_status i40e_write_phy_register_clause45(struct i40e_hw *hw, u8 page, u16 reg, u8 phy_addr, u16 value) { i40e_status status = I40E_ERR_TIMEOUT; - u32 command = 0; + u32 command = 0; u16 retry = 1000; - u8 port_num = hw->func_caps.mdio_port_num; + u8 port_num = (u8)hw->func_caps.mdio_port_num; command = (reg << I40E_GLGEN_MSCA_MDIADD_SHIFT) | (page << I40E_GLGEN_MSCA_DEVADD_SHIFT) | @@ -4625,10 +5506,10 @@ i40e_status i40e_write_phy_register_clause45(struct i40e_hw *hw, do { command = rd32(hw, I40E_GLGEN_MSCA(port_num)); if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) { - status = 0; + status = I40E_SUCCESS; break; } - usleep_range(10, 20); + udelay(10); retry--; } while (retry); if (status) { @@ -4652,10 +5533,10 @@ i40e_status i40e_write_phy_register_clause45(struct i40e_hw *hw, do { command = rd32(hw, I40E_GLGEN_MSCA(port_num)); if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) { - status = 0; + status = I40E_SUCCESS; break; } - usleep_range(10, 20); + udelay(10); retry--; } while (retry); @@ -4668,28 +5549,30 @@ phy_write_end: * @hw: pointer to the HW structure * @page: registers page number * @reg: register address in the page - * @phy_adr: PHY address on MDIO interface + * @phy_addr: PHY address on MDIO interface * @value: PHY register value * * Writes value to specified PHY register **/ i40e_status i40e_write_phy_register(struct i40e_hw *hw, - u8 page, u16 reg, u8 phy_addr, u16 value) + u8 page, u16 reg, u8 phy_addr, u16 value) { i40e_status status; switch (hw->device_id) { case I40E_DEV_ID_1G_BASE_T_X722: - status = i40e_write_phy_register_clause22(hw, reg, phy_addr, - value); + status = i40e_write_phy_register_clause22(hw, + reg, phy_addr, value); break; case I40E_DEV_ID_10G_BASE_T: case I40E_DEV_ID_10G_BASE_T4: + case I40E_DEV_ID_10G_BASE_T_BC: + case I40E_DEV_ID_5G_BASE_T_BC: case I40E_DEV_ID_10G_BASE_T_X722: case I40E_DEV_ID_25G_B: case I40E_DEV_ID_25G_SFP28: - status = i40e_write_phy_register_clause45(hw, page, reg, - phy_addr, value); + status = i40e_write_phy_register_clause45(hw, + page, reg, phy_addr, value); break; default: status = I40E_ERR_UNKNOWN_PHY; @@ -4704,13 +5587,13 @@ i40e_status i40e_write_phy_register(struct i40e_hw *hw, * @hw: pointer to the HW structure * @page: registers page number * @reg: register address in the page - * @phy_adr: PHY address on MDIO interface + * @phy_addr: PHY address on MDIO interface * @value: PHY register value * * Reads specified PHY register value **/ i40e_status i40e_read_phy_register(struct i40e_hw *hw, - u8 page, u16 reg, u8 phy_addr, u16 *value) + u8 page, u16 reg, u8 phy_addr, u16 *value) { i40e_status status; @@ -4721,6 +5604,8 @@ i40e_status i40e_read_phy_register(struct i40e_hw *hw, break; case I40E_DEV_ID_10G_BASE_T: case I40E_DEV_ID_10G_BASE_T4: + case I40E_DEV_ID_10G_BASE_T_BC: + case I40E_DEV_ID_5G_BASE_T_BC: case I40E_DEV_ID_10G_BASE_T_X722: case I40E_DEV_ID_25G_B: case I40E_DEV_ID_25G_SFP28: @@ -4739,13 +5624,12 @@ i40e_status i40e_read_phy_register(struct i40e_hw *hw, * i40e_get_phy_address * @hw: pointer to the HW structure * @dev_num: PHY port num that address we want - * @phy_addr: Returned PHY address * * Gets PHY address for current port **/ u8 i40e_get_phy_address(struct i40e_hw *hw, u8 dev_num) { - u8 port_num = hw->func_caps.mdio_port_num; + u8 port_num = (u8)hw->func_caps.mdio_port_num; u32 reg_val = rd32(hw, I40E_GLGEN_MDIO_I2C_SEL(port_num)); return (u8)(reg_val >> ((dev_num + 1) * 5)) & 0x1f; @@ -4760,11 +5644,11 @@ u8 i40e_get_phy_address(struct i40e_hw *hw, u8 dev_num) * Blinks PHY link LED **/ i40e_status i40e_blink_phy_link_led(struct i40e_hw *hw, - u32 time, u32 interval) + u32 time, u32 interval) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; u32 i; - u16 led_ctl; + u16 led_ctl = 0; u16 gpio_led_port; u16 led_reg; u16 led_addr = I40E_PHY_LED_PROV_REG_1; @@ -4825,6 +5709,64 @@ phy_blinking_end: return status; } +/** + * i40e_led_get_reg - read LED register + * @hw: pointer to the HW structure + * @led_addr: LED register address + * @reg_val: read register value + **/ +static i40e_status i40e_led_get_reg(struct i40e_hw *hw, u16 led_addr, + u32 *reg_val) +{ + i40e_status status; + u8 phy_addr = 0; + + *reg_val = 0; + if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE) { + status = i40e_aq_get_phy_register(hw, + I40E_AQ_PHY_REG_ACCESS_EXTERNAL, + I40E_PHY_COM_REG_PAGE, true, + I40E_PHY_LED_PROV_REG_1, + reg_val, NULL); + } else { + phy_addr = i40e_get_phy_address(hw, hw->port); + status = i40e_read_phy_register_clause45(hw, + I40E_PHY_COM_REG_PAGE, + led_addr, phy_addr, + (u16 *)reg_val); + } + return status; +} + +/** + * i40e_led_set_reg - write LED register + * @hw: pointer to the HW structure + * @led_addr: LED register address + * @reg_val: register value to write + **/ +static i40e_status i40e_led_set_reg(struct i40e_hw *hw, u16 led_addr, + u32 reg_val) +{ + i40e_status status; + u8 phy_addr = 0; + + if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE) { + status = i40e_aq_set_phy_register(hw, + I40E_AQ_PHY_REG_ACCESS_EXTERNAL, + I40E_PHY_COM_REG_PAGE, true, + I40E_PHY_LED_PROV_REG_1, + reg_val, NULL); + } else { + phy_addr = i40e_get_phy_address(hw, hw->port); + status = i40e_write_phy_register_clause45(hw, + I40E_PHY_COM_REG_PAGE, + led_addr, phy_addr, + (u16)reg_val); + } + + return status; +} + /** * i40e_led_get_phy - return current on/off mode * @hw: pointer to the hw struct @@ -4833,21 +5775,27 @@ phy_blinking_end: * **/ i40e_status i40e_led_get_phy(struct i40e_hw *hw, u16 *led_addr, - u16 *val) + u16 *val) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; u16 gpio_led_port; + u32 reg_val_aq; + u16 temp_addr; u8 phy_addr = 0; u16 reg_val; - u16 temp_addr; - u8 port_num; - u32 i; + if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE) { + status = i40e_aq_get_phy_register(hw, + I40E_AQ_PHY_REG_ACCESS_EXTERNAL, + I40E_PHY_COM_REG_PAGE, true, + I40E_PHY_LED_PROV_REG_1, + ®_val_aq, NULL); + if (status == I40E_SUCCESS) + *val = (u16)reg_val_aq; + return status; + } temp_addr = I40E_PHY_LED_PROV_REG_1; - i = rd32(hw, I40E_PFGEN_PORTNUM); - port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK); - phy_addr = i40e_get_phy_address(hw, port_num); - + phy_addr = i40e_get_phy_address(hw, hw->port); for (gpio_led_port = 0; gpio_led_port < 3; gpio_led_port++, temp_addr++) { status = i40e_read_phy_register_clause45(hw, @@ -4869,62 +5817,240 @@ i40e_status i40e_led_get_phy(struct i40e_hw *hw, u16 *led_addr, * i40e_led_set_phy * @hw: pointer to the HW structure * @on: true or false + * @led_addr: address of led register to use * @mode: original val plus bit for set or ignore + * * Set led's on or off when controlled by the PHY * **/ i40e_status i40e_led_set_phy(struct i40e_hw *hw, bool on, - u16 led_addr, u32 mode) + u16 led_addr, u32 mode) { - i40e_status status = 0; - u16 led_ctl = 0; - u16 led_reg = 0; - u8 phy_addr = 0; - u8 port_num; - u32 i; + i40e_status status = I40E_SUCCESS; + u32 led_ctl = 0; + u32 led_reg = 0; - i = rd32(hw, I40E_PFGEN_PORTNUM); - port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK); - phy_addr = i40e_get_phy_address(hw, port_num); - status = i40e_read_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE, - led_addr, phy_addr, &led_reg); + status = i40e_led_get_reg(hw, led_addr, &led_reg); if (status) return status; led_ctl = led_reg; if (led_reg & I40E_PHY_LED_LINK_MODE_MASK) { led_reg = 0; - status = i40e_write_phy_register_clause45(hw, - I40E_PHY_COM_REG_PAGE, - led_addr, phy_addr, - led_reg); + status = i40e_led_set_reg(hw, led_addr, led_reg); if (status) return status; } - status = i40e_read_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE, - led_addr, phy_addr, &led_reg); + status = i40e_led_get_reg(hw, led_addr, &led_reg); if (status) goto restore_config; if (on) led_reg = I40E_PHY_LED_MANUAL_ON; else led_reg = 0; - status = i40e_write_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE, - led_addr, phy_addr, led_reg); + status = i40e_led_set_reg(hw, led_addr, led_reg); if (status) goto restore_config; if (mode & I40E_PHY_LED_MODE_ORIG) { led_ctl = (mode & I40E_PHY_LED_MODE_MASK); - status = i40e_write_phy_register_clause45(hw, - I40E_PHY_COM_REG_PAGE, - led_addr, phy_addr, led_ctl); + status = i40e_led_set_reg(hw, led_addr, led_ctl); } return status; + restore_config: - status = i40e_write_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE, - led_addr, phy_addr, led_ctl); + status = i40e_led_set_reg(hw, led_addr, led_ctl); return status; } +/** + * i40e_get_phy_lpi_status - read LPI status from PHY or MAC register + * @hw: pointer to the hw struct + * @stat: pointer to structure with status of rx and tx lpi + * + * Read LPI state directly from external PHY register or from MAC + * register, depending on device ID and current link speed. + */ +i40e_status i40e_get_phy_lpi_status(struct i40e_hw *hw, + struct i40e_hw_port_stats *stat) +{ + i40e_status ret = I40E_SUCCESS; + u32 val; + + stat->rx_lpi_status = 0; + stat->tx_lpi_status = 0; + + if ((hw->device_id == I40E_DEV_ID_10G_BASE_T_BC || + hw->device_id == I40E_DEV_ID_5G_BASE_T_BC) && + (hw->phy.link_info.link_speed == I40E_LINK_SPEED_2_5GB || + hw->phy.link_info.link_speed == I40E_LINK_SPEED_5GB)) { + ret = i40e_aq_get_phy_register(hw, + I40E_AQ_PHY_REG_ACCESS_EXTERNAL, + I40E_BCM_PHY_PCS_STATUS1_PAGE, + true, + I40E_BCM_PHY_PCS_STATUS1_REG, + &val, NULL); + + if (ret != I40E_SUCCESS) + return ret; + + stat->rx_lpi_status = !!(val & I40E_BCM_PHY_PCS_STATUS1_RX_LPI); + stat->tx_lpi_status = !!(val & I40E_BCM_PHY_PCS_STATUS1_TX_LPI); + + return ret; + } + + val = rd32(hw, I40E_PRTPM_EEE_STAT); + stat->rx_lpi_status = (val & I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_MASK) >> + I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_SHIFT; + stat->tx_lpi_status = (val & I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_MASK) >> + I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_SHIFT; + + return ret; +} + +/** + * i40e_get_lpi_counters - read LPI counters from EEE statistics + * @hw: pointer to the hw struct + * @tx_counter: pointer to memory for TX LPI counter + * @rx_counter: pointer to memory for RX LPI counter + * @is_clear: returns true if counters are clear after read + * + * Read Low Power Idle (LPI) mode counters from Energy Efficient + * Ethernet (EEE) statistics. + **/ +i40e_status i40e_get_lpi_counters(struct i40e_hw *hw, + u32 *tx_counter, u32 *rx_counter, + bool *is_clear) +{ + /* only X710-T*L requires special handling of counters + * for other devices we just read the MAC registers + */ + if ((hw->device_id == I40E_DEV_ID_10G_BASE_T_BC || + hw->device_id == I40E_DEV_ID_5G_BASE_T_BC) && + hw->phy.link_info.link_speed != I40E_LINK_SPEED_1GB) { + i40e_status retval; + u32 cmd_status; + + *is_clear = false; + retval = i40e_aq_run_phy_activity(hw, + I40E_AQ_RUN_PHY_ACT_ID_USR_DFND, + I40E_AQ_RUN_PHY_ACT_DNL_OPCODE_GET_EEE_STAT, + &cmd_status, tx_counter, rx_counter, NULL); + + if (cmd_status != I40E_AQ_RUN_PHY_ACT_CMD_STAT_SUCC) + retval = I40E_ERR_ADMIN_QUEUE_ERROR; + + return retval; + } + + *is_clear = true; + *tx_counter = rd32(hw, I40E_PRTPM_TLPIC); + *rx_counter = rd32(hw, I40E_PRTPM_RLPIC); + + return I40E_SUCCESS; +} + +/** + * i40e_get_lpi_duration - read LPI time duration from EEE statistics + * @hw: pointer to the hw struct + * @stat: pointer to structure with status of rx and tx lpi + * @tx_duration: pointer to memory for TX LPI time duration + * @rx_duration: pointer to memory for RX LPI time duration + * + * Read Low Power Idle (LPI) mode time duration from Energy Efficient + * Ethernet (EEE) statistics. + */ +i40e_status i40e_get_lpi_duration(struct i40e_hw *hw, + struct i40e_hw_port_stats *stat, + u64 *tx_duration, u64 *rx_duration) +{ + u32 tx_time_dur, rx_time_dur; + i40e_status retval; + u32 cmd_status; + + if (hw->device_id != I40E_DEV_ID_10G_BASE_T_BC && + hw->device_id != I40E_DEV_ID_5G_BASE_T_BC) + return I40E_ERR_NOT_IMPLEMENTED; + + retval = i40e_aq_run_phy_activity + (hw, I40E_AQ_RUN_PHY_ACT_ID_USR_DFND, + I40E_AQ_RUN_PHY_ACT_DNL_OPCODE_GET_EEE_DUR, + &cmd_status, &tx_time_dur, &rx_time_dur, NULL); + + if (retval) + return retval; + if ((cmd_status & I40E_AQ_RUN_PHY_ACT_CMD_STAT_MASK) != + I40E_AQ_RUN_PHY_ACT_CMD_STAT_SUCC) + return I40E_ERR_ADMIN_QUEUE_ERROR; + + if (hw->phy.link_info.link_speed == I40E_LINK_SPEED_1GB && + !tx_time_dur && !rx_time_dur && + stat->tx_lpi_status && stat->rx_lpi_status) { + retval = i40e_aq_run_phy_activity + (hw, I40E_AQ_RUN_PHY_ACT_ID_USR_DFND, + I40E_AQ_RUN_PHY_ACT_DNL_OPCODE_GET_EEE_STAT_DUR, + &cmd_status, + &tx_time_dur, &rx_time_dur, NULL); + + if (retval) + return retval; + if ((cmd_status & I40E_AQ_RUN_PHY_ACT_CMD_STAT_MASK) != + I40E_AQ_RUN_PHY_ACT_CMD_STAT_SUCC) + return I40E_ERR_ADMIN_QUEUE_ERROR; + tx_time_dur = 0; + rx_time_dur = 0; + } + + *tx_duration = tx_time_dur; + *rx_duration = rx_time_dur; + + return retval; +} + +/** + * i40e_lpi_stat_update - update LPI counters with values relative to offset + * @hw: pointer to the hw struct + * @offset_loaded: flag indicating need of writing current value to offset + * @tx_offset: pointer to offset of TX LPI counter + * @tx_stat: pointer to value of TX LPI counter + * @rx_offset: pointer to offset of RX LPI counter + * @rx_stat: pointer to value of RX LPI counter + * + * Update Low Power Idle (LPI) mode counters while having regard to passed + * offsets. + **/ +i40e_status i40e_lpi_stat_update(struct i40e_hw *hw, + bool offset_loaded, u64 *tx_offset, + u64 *tx_stat, u64 *rx_offset, + u64 *rx_stat) +{ + i40e_status retval; + u32 tx_counter, rx_counter; + bool is_clear; + + retval = i40e_get_lpi_counters(hw, &tx_counter, &rx_counter, &is_clear); + if (retval) + goto err; + + if (is_clear) { + *tx_stat += tx_counter; + *rx_stat += rx_counter; + } else { + if (!offset_loaded) { + *tx_offset = tx_counter; + *rx_offset = rx_counter; + } + + *tx_stat = (tx_counter >= *tx_offset) ? + (u32)(tx_counter - *tx_offset) : + (u32)((tx_counter + BIT_ULL(32)) - *tx_offset); + *rx_stat = (rx_counter >= *rx_offset) ? + (u32)(rx_counter - *rx_offset) : + (u32)((rx_counter + BIT_ULL(32)) - *rx_offset); + } +err: + return retval; +} + /** * i40e_aq_rx_ctl_read_register - use FW to read from an Rx control register * @hw: pointer to the hw struct @@ -4944,17 +6070,17 @@ i40e_status i40e_aq_rx_ctl_read_register(struct i40e_hw *hw, (struct i40e_aqc_rx_ctl_reg_read_write *)&desc.params.raw; i40e_status status; - if (!reg_val) + if (reg_val == NULL) return I40E_ERR_PARAM; i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_rx_ctl_reg_read); - cmd_resp->address = cpu_to_le32(reg_addr); + cmd_resp->address = CPU_TO_LE32(reg_addr); status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); - if (status == 0) - *reg_val = le32_to_cpu(cmd_resp->value); + if (status == I40E_SUCCESS) + *reg_val = LE32_TO_CPU(cmd_resp->value); return status; } @@ -4966,7 +6092,7 @@ i40e_status i40e_aq_rx_ctl_read_register(struct i40e_hw *hw, **/ u32 i40e_read_rx_ctl(struct i40e_hw *hw, u32 reg_addr) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; bool use_register; int retry = 5; u32 val = 0; @@ -5012,10 +6138,11 @@ i40e_status i40e_aq_rx_ctl_write_register(struct i40e_hw *hw, i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_rx_ctl_reg_write); - cmd->address = cpu_to_le32(reg_addr); - cmd->value = cpu_to_le32(reg_val); + cmd->address = CPU_TO_LE32(reg_addr); + cmd->value = CPU_TO_LE32(reg_val); - status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + status = i40e_asq_send_command_atomic(hw, &desc, NULL, 0, + cmd_details, true); return status; } @@ -5028,7 +6155,7 @@ i40e_status i40e_aq_rx_ctl_write_register(struct i40e_hw *hw, **/ void i40e_write_rx_ctl(struct i40e_hw *hw, u32 reg_addr, u32 reg_val) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; bool use_register; int retry = 5; @@ -5052,7 +6179,358 @@ do_retry: } /** - * i40e_aq_write_ppp - Write pipeline personalization profile (ppp) + * i40e_mdio_if_number_selection - MDIO I/F number selection + * @hw: pointer to the hw struct + * @set_mdio: use MDIO I/F number specified by mdio_num + * @mdio_num: MDIO I/F number + * @cmd: pointer to PHY Register command structure + **/ +static void +i40e_mdio_if_number_selection(struct i40e_hw *hw, bool set_mdio, u8 mdio_num, + struct i40e_aqc_phy_register_access *cmd) +{ + if (set_mdio && cmd->phy_interface == I40E_AQ_PHY_REG_ACCESS_EXTERNAL) { + if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_EXTENDED) + cmd->cmd_flags |= + I40E_AQ_PHY_REG_ACCESS_SET_MDIO_IF_NUMBER | + ((mdio_num << + I40E_AQ_PHY_REG_ACCESS_MDIO_IF_NUMBER_SHIFT) & + I40E_AQ_PHY_REG_ACCESS_MDIO_IF_NUMBER_MASK); + else + i40e_debug(hw, I40E_DEBUG_PHY, + "MDIO I/F number selection not supported by current FW version.\n"); + } +} + +/** + * i40e_aq_set_phy_register_ext + * @hw: pointer to the hw struct + * @phy_select: select which phy should be accessed + * @dev_addr: PHY device address + * @page_change: enable auto page change + * @set_mdio: use MDIO I/F number specified by mdio_num + * @mdio_num: MDIO I/F number + * @reg_addr: PHY register address + * @reg_val: new register value + * @cmd_details: pointer to command details structure or NULL + * + * Write the external PHY register. + * NOTE: In common cases MDIO I/F number should not be changed, thats why you + * may use simple wrapper i40e_aq_set_phy_register. + **/ +enum i40e_status_code +i40e_aq_set_phy_register_ext(struct i40e_hw *hw, + u8 phy_select, u8 dev_addr, bool page_change, + bool set_mdio, u8 mdio_num, + u32 reg_addr, u32 reg_val, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_phy_register_access *cmd = + (struct i40e_aqc_phy_register_access *)&desc.params.raw; + i40e_status status; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_set_phy_register); + + cmd->phy_interface = phy_select; + cmd->dev_addres = dev_addr; + cmd->reg_address = CPU_TO_LE32(reg_addr); + cmd->reg_value = CPU_TO_LE32(reg_val); + + if (!page_change) + cmd->cmd_flags = I40E_AQ_PHY_REG_ACCESS_DONT_CHANGE_QSFP_PAGE; + + i40e_mdio_if_number_selection(hw, set_mdio, mdio_num, cmd); + + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + + return status; +} + +/** + * i40e_aq_get_phy_register_ext + * @hw: pointer to the hw struct + * @phy_select: select which phy should be accessed + * @dev_addr: PHY device address + * @page_change: enable auto page change + * @set_mdio: use MDIO I/F number specified by mdio_num + * @mdio_num: MDIO I/F number + * @reg_addr: PHY register address + * @reg_val: read register value + * @cmd_details: pointer to command details structure or NULL + * + * Read the external PHY register. + * NOTE: In common cases MDIO I/F number should not be changed, thats why you + * may use simple wrapper i40e_aq_get_phy_register. + **/ +enum i40e_status_code +i40e_aq_get_phy_register_ext(struct i40e_hw *hw, + u8 phy_select, u8 dev_addr, bool page_change, + bool set_mdio, u8 mdio_num, + u32 reg_addr, u32 *reg_val, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_phy_register_access *cmd = + (struct i40e_aqc_phy_register_access *)&desc.params.raw; + i40e_status status; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_get_phy_register); + + cmd->phy_interface = phy_select; + cmd->dev_addres = dev_addr; + cmd->reg_address = CPU_TO_LE32(reg_addr); + + if (!page_change) + cmd->cmd_flags = I40E_AQ_PHY_REG_ACCESS_DONT_CHANGE_QSFP_PAGE; + + i40e_mdio_if_number_selection(hw, set_mdio, mdio_num, cmd); + + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + if (!status) + *reg_val = LE32_TO_CPU(cmd->reg_value); + + return status; +} + +/** + * i40e_aq_run_phy_activity + * @hw: pointer to the hw struct + * @activity_id: ID of DNL activity to run + * @dnl_opcode: opcode passed to DNL script + * @cmd_status: pointer to memory to write return value of DNL script + * @data0: pointer to memory for first 4 bytes of data returned by DNL script + * @data1: pointer to memory for last 4 bytes of data returned by DNL script + * @cmd_details: pointer to command details structure or NULL + * + * Run DNL admin command. + **/ +enum i40e_status_code +i40e_aq_run_phy_activity(struct i40e_hw *hw, u16 activity_id, u32 dnl_opcode, + u32 *cmd_status, u32 *data0, u32 *data1, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aqc_run_phy_activity *cmd; + i40e_status retval; + struct i40e_aq_desc desc; + + cmd = (struct i40e_aqc_run_phy_activity *)&desc.params.raw; + + if (!cmd_status || !data0 || !data1) { + retval = I40E_ERR_PARAM; + goto err; + } + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_run_phy_activity); + + cmd->activity_id = CPU_TO_LE16(activity_id); + cmd->params.cmd.dnl_opcode = CPU_TO_LE32(dnl_opcode); + + retval = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + if (retval) + goto err; + + *cmd_status = LE32_TO_CPU(cmd->params.resp.cmd_status); + *data0 = LE32_TO_CPU(cmd->params.resp.data0); + *data1 = LE32_TO_CPU(cmd->params.resp.data1); +err: + return retval; +} + +/** + * i40e_aq_set_arp_proxy_config + * @hw: pointer to the HW structure + * @proxy_config: pointer to proxy config command table struct + * @cmd_details: pointer to command details + * + * Set ARP offload parameters from pre-populated + * i40e_aqc_arp_proxy_data struct + **/ +i40e_status i40e_aq_set_arp_proxy_config(struct i40e_hw *hw, + struct i40e_aqc_arp_proxy_data *proxy_config, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + i40e_status status; + + if (!proxy_config) + return I40E_ERR_PARAM; + + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_set_proxy_config); + + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_RD); + desc.params.external.addr_high = + CPU_TO_LE32(upper_32_bits((u64)proxy_config)); + desc.params.external.addr_low = + CPU_TO_LE32(lower_32_bits((u64)proxy_config)); + desc.datalen = CPU_TO_LE16(sizeof(struct i40e_aqc_arp_proxy_data)); + + status = i40e_asq_send_command(hw, &desc, proxy_config, + sizeof(struct i40e_aqc_arp_proxy_data), + cmd_details); + + return status; +} + +/** + * i40e_aq_opc_set_ns_proxy_table_entry + * @hw: pointer to the HW structure + * @ns_proxy_table_entry: pointer to NS table entry command struct + * @cmd_details: pointer to command details + * + * Set IPv6 Neighbor Solicitation (NS) protocol offload parameters + * from pre-populated i40e_aqc_ns_proxy_data struct + **/ +i40e_status i40e_aq_set_ns_proxy_table_entry(struct i40e_hw *hw, + struct i40e_aqc_ns_proxy_data *ns_proxy_table_entry, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + i40e_status status; + + if (!ns_proxy_table_entry) + return I40E_ERR_PARAM; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_set_ns_proxy_table_entry); + + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_RD); + desc.params.external.addr_high = + CPU_TO_LE32(upper_32_bits((u64)ns_proxy_table_entry)); + desc.params.external.addr_low = + CPU_TO_LE32(lower_32_bits((u64)ns_proxy_table_entry)); + desc.datalen = CPU_TO_LE16(sizeof(struct i40e_aqc_ns_proxy_data)); + + status = i40e_asq_send_command(hw, &desc, ns_proxy_table_entry, + sizeof(struct i40e_aqc_ns_proxy_data), + cmd_details); + + return status; +} + +/** + * i40e_aq_set_clear_wol_filter + * @hw: pointer to the hw struct + * @filter_index: index of filter to modify (0-7) + * @filter: buffer containing filter to be set + * @set_filter: true to set filter, false to clear filter + * @no_wol_tco: if true, pass through packets cannot cause wake-up + * if false, pass through packets may cause wake-up + * @filter_valid: true if filter action is valid + * @no_wol_tco_valid: true if no WoL in TCO traffic action valid + * @cmd_details: pointer to command details structure or NULL + * + * Set or clear WoL filter for port attached to the PF + **/ +i40e_status i40e_aq_set_clear_wol_filter(struct i40e_hw *hw, + u8 filter_index, + struct i40e_aqc_set_wol_filter_data *filter, + bool set_filter, bool no_wol_tco, + bool filter_valid, bool no_wol_tco_valid, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_set_wol_filter *cmd = + (struct i40e_aqc_set_wol_filter *)&desc.params.raw; + i40e_status status; + u16 cmd_flags = 0; + u16 valid_flags = 0; + u16 buff_len = 0; + + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_set_wol_filter); + + if (filter_index >= I40E_AQC_MAX_NUM_WOL_FILTERS) + return I40E_ERR_PARAM; + cmd->filter_index = CPU_TO_LE16(filter_index); + + if (set_filter) { + if (!filter) + return I40E_ERR_PARAM; + + cmd_flags |= I40E_AQC_SET_WOL_FILTER; + cmd_flags |= I40E_AQC_SET_WOL_FILTER_WOL_PRESERVE_ON_PFR; + } + + if (no_wol_tco) + cmd_flags |= I40E_AQC_SET_WOL_FILTER_NO_TCO_WOL; + cmd->cmd_flags = CPU_TO_LE16(cmd_flags); + + if (filter_valid) + valid_flags |= I40E_AQC_SET_WOL_FILTER_ACTION_VALID; + if (no_wol_tco_valid) + valid_flags |= I40E_AQC_SET_WOL_FILTER_NO_TCO_ACTION_VALID; + cmd->valid_flags = CPU_TO_LE16(valid_flags); + + buff_len = sizeof(*filter); + desc.datalen = CPU_TO_LE16(buff_len); + + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_RD); + + cmd->address_high = CPU_TO_LE32(upper_32_bits((u64)filter)); + cmd->address_low = CPU_TO_LE32(lower_32_bits((u64)filter)); + + status = i40e_asq_send_command(hw, &desc, filter, + buff_len, cmd_details); + + return status; +} + +/** + * i40e_aq_get_wake_event_reason + * @hw: pointer to the hw struct + * @wake_reason: return value, index of matching filter + * @cmd_details: pointer to command details structure or NULL + * + * Get information for the reason of a Wake Up event + **/ +i40e_status i40e_aq_get_wake_event_reason(struct i40e_hw *hw, + u16 *wake_reason, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_get_wake_reason_completion *resp = + (struct i40e_aqc_get_wake_reason_completion *)&desc.params.raw; + i40e_status status; + + i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_get_wake_reason); + + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + + if (status == I40E_SUCCESS) + *wake_reason = LE16_TO_CPU(resp->wake_reason); + + return status; +} + +/** +* i40e_aq_clear_all_wol_filters +* @hw: pointer to the hw struct +* @cmd_details: pointer to command details structure or NULL +* +* Get information for the reason of a Wake Up event +**/ +i40e_status i40e_aq_clear_all_wol_filters(struct i40e_hw *hw, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + i40e_status status; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_clear_all_wol_filters); + + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + + return status; +} + +/** + * i40e_aq_write_ddp - Write dynamic device personalization (ddp) * @hw: pointer to the hw struct * @buff: command buffer (size in bytes = buff_size) * @buff_size: buffer size in bytes @@ -5062,7 +6540,7 @@ do_retry: * @cmd_details: pointer to command details structure or NULL **/ enum -i40e_status_code i40e_aq_write_ppp(struct i40e_hw *hw, void *buff, +i40e_status_code i40e_aq_write_ddp(struct i40e_hw *hw, void *buff, u16 buff_size, u32 track_id, u32 *error_offset, u32 *error_info, struct i40e_asq_cmd_details *cmd_details) @@ -5071,41 +6549,42 @@ i40e_status_code i40e_aq_write_ppp(struct i40e_hw *hw, void *buff, struct i40e_aqc_write_personalization_profile *cmd = (struct i40e_aqc_write_personalization_profile *) &desc.params.raw; - struct i40e_aqc_write_ppp_resp *resp; + struct i40e_aqc_write_ddp_resp *resp; i40e_status status; i40e_fill_default_direct_cmd_desc(&desc, - i40e_aqc_opc_write_personalization_profile); + i40e_aqc_opc_write_personalization_profile); - desc.flags |= cpu_to_le16(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD); + desc.flags |= CPU_TO_LE16(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD); if (buff_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); - desc.datalen = cpu_to_le16(buff_size); + desc.datalen = CPU_TO_LE16(buff_size); - cmd->profile_track_id = cpu_to_le32(track_id); + cmd->profile_track_id = CPU_TO_LE32(track_id); status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details); if (!status) { - resp = (struct i40e_aqc_write_ppp_resp *)&desc.params.raw; + resp = (struct i40e_aqc_write_ddp_resp *)&desc.params.raw; if (error_offset) - *error_offset = le32_to_cpu(resp->error_offset); + *error_offset = LE32_TO_CPU(resp->error_offset); if (error_info) - *error_info = le32_to_cpu(resp->error_info); + *error_info = LE32_TO_CPU(resp->error_info); } return status; } /** - * i40e_aq_get_ppp_list - Read pipeline personalization profile (ppp) + * i40e_aq_get_ddp_list - Read dynamic device personalization (ddp) * @hw: pointer to the hw struct * @buff: command buffer (size in bytes = buff_size) * @buff_size: buffer size in bytes + * @flags: AdminQ command flags * @cmd_details: pointer to command details structure or NULL **/ enum -i40e_status_code i40e_aq_get_ppp_list(struct i40e_hw *hw, void *buff, +i40e_status_code i40e_aq_get_ddp_list(struct i40e_hw *hw, void *buff, u16 buff_size, u8 flags, struct i40e_asq_cmd_details *cmd_details) { @@ -5115,12 +6594,12 @@ i40e_status_code i40e_aq_get_ppp_list(struct i40e_hw *hw, void *buff, i40e_status status; i40e_fill_default_direct_cmd_desc(&desc, - i40e_aqc_opc_get_personalization_profile_list); + i40e_aqc_opc_get_personalization_profile_list); - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); if (buff_size > I40E_AQ_LARGE_BUF) - desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); - desc.datalen = cpu_to_le16(buff_size); + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); + desc.datalen = CPU_TO_LE16(buff_size); cmd->flags = flags; @@ -5158,6 +6637,165 @@ i40e_find_segment_in_package(u32 segment_type, return NULL; } +/* Get section table in profile */ +#define I40E_SECTION_TABLE(profile, sec_tbl) \ + do { \ + struct i40e_profile_segment *p = (profile); \ + u32 count; \ + u32 *nvm; \ + count = p->device_table_count; \ + nvm = (u32 *)&p->device_table[count]; \ + sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; \ + } while (0) + +/* Get section header in profile */ +#define I40E_SECTION_HEADER(profile, offset) \ + (struct i40e_profile_section_header *)((u8 *)(profile) + (offset)) + +/** + * i40e_find_section_in_profile + * @section_type: the section type to search for (i.e., SECTION_TYPE_NOTE) + * @profile: pointer to the i40e segment header to be searched + * + * This function searches i40e segment for a particular section type. On + * success it returns a pointer to the section header, otherwise it will + * return NULL. + **/ +struct i40e_profile_section_header * +i40e_find_section_in_profile(u32 section_type, + struct i40e_profile_segment *profile) +{ + struct i40e_profile_section_header *sec; + struct i40e_section_table *sec_tbl; + u32 sec_off; + u32 i; + + if (profile->header.type != SEGMENT_TYPE_I40E) + return NULL; + + I40E_SECTION_TABLE(profile, sec_tbl); + + for (i = 0; i < sec_tbl->section_count; i++) { + sec_off = sec_tbl->section_offset[i]; + sec = I40E_SECTION_HEADER(profile, sec_off); + if (sec->section.type == section_type) + return sec; + } + + return NULL; +} + +/** + * i40e_ddp_exec_aq_section - Execute generic AQ for DDP + * @hw: pointer to the hw struct + * @aq: command buffer containing all data to execute AQ + **/ +static enum +i40e_status_code i40e_ddp_exec_aq_section(struct i40e_hw *hw, + struct i40e_profile_aq_section *aq) +{ + i40e_status status; + struct i40e_aq_desc desc; + u8 *msg = NULL; + u16 msglen; + + i40e_fill_default_direct_cmd_desc(&desc, aq->opcode); + desc.flags |= CPU_TO_LE16(aq->flags); + i40e_memcpy(desc.params.raw, aq->param, sizeof(desc.params.raw), + I40E_NONDMA_TO_NONDMA); + + msglen = aq->datalen; + if (msglen) { + desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF | + I40E_AQ_FLAG_RD)); + if (msglen > I40E_AQ_LARGE_BUF) + desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB); + desc.datalen = CPU_TO_LE16(msglen); + msg = &aq->data[0]; + } + + status = i40e_asq_send_command(hw, &desc, msg, msglen, NULL); + + if (status != I40E_SUCCESS) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, + "unable to exec DDP AQ opcode %u, error %d\n", + aq->opcode, status); + return status; + } + + /* copy returned desc to aq_buf */ + i40e_memcpy(aq->param, desc.params.raw, sizeof(desc.params.raw), + I40E_NONDMA_TO_NONDMA); + + return I40E_SUCCESS; +} + +/** + * i40e_validate_profile + * @hw: pointer to the hardware structure + * @profile: pointer to the profile segment of the package to be validated + * @track_id: package tracking id + * @rollback: flag if the profile is for rollback. + * + * Validates supported devices and profile's sections. + */ +static enum i40e_status_code +i40e_validate_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile, + u32 track_id, bool rollback) +{ + struct i40e_profile_section_header *sec = NULL; + i40e_status status = I40E_SUCCESS; + struct i40e_section_table *sec_tbl; + u32 vendor_dev_id; + u32 dev_cnt; + u32 sec_off; + u32 i; + + if (track_id == I40E_DDP_TRACKID_INVALID) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, "Invalid track_id\n"); + return I40E_NOT_SUPPORTED; + } + + dev_cnt = profile->device_table_count; + for (i = 0; i < dev_cnt; i++) { + vendor_dev_id = profile->device_table[i].vendor_dev_id; + if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL && + hw->device_id == (vendor_dev_id & 0xFFFF)) + break; + } + if (dev_cnt && (i == dev_cnt)) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, + "Device doesn't support DDP\n"); + return I40E_ERR_DEVICE_NOT_SUPPORTED; + } + + I40E_SECTION_TABLE(profile, sec_tbl); + + /* Validate sections types */ + for (i = 0; i < sec_tbl->section_count; i++) { + sec_off = sec_tbl->section_offset[i]; + sec = I40E_SECTION_HEADER(profile, sec_off); + if (rollback) { + if (sec->section.type == SECTION_TYPE_MMIO || + sec->section.type == SECTION_TYPE_AQ || + sec->section.type == SECTION_TYPE_RB_AQ) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, + "Not a roll-back package\n"); + return I40E_NOT_SUPPORTED; + } + } else { + if (sec->section.type == SECTION_TYPE_RB_AQ || + sec->section.type == SECTION_TYPE_RB_MMIO) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, + "Not an original package\n"); + return I40E_NOT_SUPPORTED; + } + } + } + + return status; +} + /** * i40e_write_profile * @hw: pointer to the hardware structure @@ -5170,55 +6808,102 @@ enum i40e_status_code i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile, u32 track_id) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; struct i40e_section_table *sec_tbl; struct i40e_profile_section_header *sec = NULL; - u32 dev_cnt; - u32 vendor_dev_id; - u32 *nvm; + struct i40e_profile_aq_section *ddp_aq; u32 section_size = 0; u32 offset = 0, info = 0; + u32 sec_off; u32 i; - if (!track_id) { - i40e_debug(hw, I40E_DEBUG_PACKAGE, "Track_id can't be 0."); - return I40E_NOT_SUPPORTED; - } + status = i40e_validate_profile(hw, profile, track_id, false); + if (status) + return status; - dev_cnt = profile->device_table_count; + I40E_SECTION_TABLE(profile, sec_tbl); - for (i = 0; i < dev_cnt; i++) { - vendor_dev_id = profile->device_table[i].vendor_dev_id; - if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL) - if (hw->device_id == (vendor_dev_id & 0xFFFF)) + for (i = 0; i < sec_tbl->section_count; i++) { + sec_off = sec_tbl->section_offset[i]; + sec = I40E_SECTION_HEADER(profile, sec_off); + /* Process generic admin command */ + if (sec->section.type == SECTION_TYPE_AQ) { + ddp_aq = (struct i40e_profile_aq_section *)&sec[1]; + status = i40e_ddp_exec_aq_section(hw, ddp_aq); + if (status) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, + "Failed to execute aq: section %d, opcode %u\n", + i, ddp_aq->opcode); break; + } + sec->section.type = SECTION_TYPE_RB_AQ; + } + + /* Skip any non-mmio sections */ + if (sec->section.type != SECTION_TYPE_MMIO) + continue; + + section_size = sec->section.size + + sizeof(struct i40e_profile_section_header); + + /* Write MMIO section */ + status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size, + track_id, &offset, &info, NULL); + if (status) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, + "Failed to write profile: section %d, offset %d, info %d\n", + i, offset, info); + break; + } } - if (i == dev_cnt) { - i40e_debug(hw, I40E_DEBUG_PACKAGE, "Device doesn't support PPP"); - return I40E_ERR_DEVICE_NOT_SUPPORTED; - } + return status; +} - nvm = (u32 *)&profile->device_table[dev_cnt]; - sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; +/** + * i40e_rollback_profile + * @hw: pointer to the hardware structure + * @profile: pointer to the profile segment of the package to be removed + * @track_id: package tracking id + * + * Rolls back previously loaded package. + */ +enum i40e_status_code +i40e_rollback_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile, + u32 track_id) +{ + struct i40e_profile_section_header *sec = NULL; + i40e_status status = I40E_SUCCESS; + struct i40e_section_table *sec_tbl; + u32 offset = 0, info = 0; + u32 section_size = 0; + u32 sec_off; + int i; - for (i = 0; i < sec_tbl->section_count; i++) { - sec = (struct i40e_profile_section_header *)((u8 *)profile + - sec_tbl->section_offset[i]); + status = i40e_validate_profile(hw, profile, track_id, true); + if (status) + return status; - /* Skip 'AQ', 'note' and 'name' sections */ - if (sec->section.type != SECTION_TYPE_MMIO) + I40E_SECTION_TABLE(profile, sec_tbl); + + /* For rollback write sections in reverse */ + for (i = sec_tbl->section_count - 1; i >= 0; i--) { + sec_off = sec_tbl->section_offset[i]; + sec = I40E_SECTION_HEADER(profile, sec_off); + + /* Skip any non-rollback sections */ + if (sec->section.type != SECTION_TYPE_RB_MMIO) continue; section_size = sec->section.size + sizeof(struct i40e_profile_section_header); - /* Write profile */ - status = i40e_aq_write_ppp(hw, (void *)sec, (u16)section_size, + /* Write roll-back MMIO section */ + status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size, track_id, &offset, &info, NULL); if (status) { i40e_debug(hw, I40E_DEBUG_PACKAGE, - "Failed to write profile: offset %d, info %d", - offset, info); + "Failed to write profile: section %d, offset %d, info %d\n", + i, offset, info); break; } } @@ -5239,7 +6924,7 @@ i40e_add_pinfo_to_list(struct i40e_hw *hw, struct i40e_profile_segment *profile, u8 *profile_info_sec, u32 track_id) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; struct i40e_profile_section_header *sec = NULL; struct i40e_profile_info *pinfo; u32 offset = 0, info = 0; @@ -5255,10 +6940,11 @@ i40e_add_pinfo_to_list(struct i40e_hw *hw, sec->section.offset); pinfo->track_id = track_id; pinfo->version = profile->version; - pinfo->op = I40E_PPP_ADD_TRACKID; - memcpy(pinfo->name, profile->name, I40E_PPP_NAME_SIZE); + pinfo->op = I40E_DDP_ADD_TRACKID; + i40e_memcpy(pinfo->name, profile->name, I40E_DDP_NAME_SIZE, + I40E_NONDMA_TO_NONDMA); - status = i40e_aq_write_ppp(hw, (void *)sec, sec->data_end, + status = i40e_aq_write_ddp(hw, (void *)sec, sec->data_end, track_id, &offset, &info, NULL); return status; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c index 55079fe3e..0580a51fe 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2017 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #include "i40e_adminq.h" #include "i40e_prototype.h" @@ -46,7 +23,7 @@ i40e_status i40e_get_dcbx_status(struct i40e_hw *hw, u16 *status) *status = (u16)((reg & I40E_PRTDCB_GENS_DCBX_STATUS_MASK) >> I40E_PRTDCB_GENS_DCBX_STATUS_SHIFT); - return 0; + return I40E_SUCCESS; } /** @@ -387,7 +364,6 @@ static void i40e_parse_cee_app_tlv(struct i40e_cee_feat_tlv *tlv, I40E_LLDP_TLV_LEN_SHIFT); dcbcfg->numapps = length / sizeof(*app); - if (!dcbcfg->numapps) return; if (dcbcfg->numapps > I40E_DCBX_MAX_APPS) @@ -523,7 +499,7 @@ static void i40e_parse_org_tlv(struct i40e_lldp_org_tlv *tlv, i40e_status i40e_lldp_to_dcb_config(u8 *lldpmib, struct i40e_dcbx_config *dcbcfg) { - i40e_status ret = 0; + i40e_status ret = I40E_SUCCESS; struct i40e_lldp_org_tlv *tlv; u16 type; u16 length; @@ -578,7 +554,7 @@ i40e_status i40e_aq_get_dcb_config(struct i40e_hw *hw, u8 mib_type, u8 bridgetype, struct i40e_dcbx_config *dcbcfg) { - i40e_status ret = 0; + i40e_status ret = I40E_SUCCESS; struct i40e_virt_mem mem; u8 *lldpmib; @@ -613,8 +589,8 @@ static void i40e_cee_to_dcb_v1_config( struct i40e_aqc_get_cee_dcb_cfg_v1_resp *cee_cfg, struct i40e_dcbx_config *dcbcfg) { - u16 status, tlv_status = le16_to_cpu(cee_cfg->tlv_status); - u16 app_prio = le16_to_cpu(cee_cfg->oper_app_prio); + u16 status, tlv_status = LE16_TO_CPU(cee_cfg->tlv_status); + u16 app_prio = LE16_TO_CPU(cee_cfg->oper_app_prio); u8 i, tc, err; /* CEE PG data to ETS config */ @@ -627,7 +603,7 @@ static void i40e_cee_to_dcb_v1_config( tc = (u8)((cee_cfg->oper_prio_tc[i] & I40E_CEE_PGID_PRIO_0_MASK) >> I40E_CEE_PGID_PRIO_0_SHIFT); - dcbcfg->etscfg.prioritytable[i * 2] = tc; + dcbcfg->etscfg.prioritytable[i*2] = tc; tc = (u8)((cee_cfg->oper_prio_tc[i] & I40E_CEE_PGID_PRIO_1_MASK) >> I40E_CEE_PGID_PRIO_1_SHIFT); @@ -694,8 +670,8 @@ static void i40e_cee_to_dcb_config( struct i40e_aqc_get_cee_dcb_cfg_resp *cee_cfg, struct i40e_dcbx_config *dcbcfg) { - u32 status, tlv_status = le32_to_cpu(cee_cfg->tlv_status); - u16 app_prio = le16_to_cpu(cee_cfg->oper_app_prio); + u32 status, tlv_status = LE32_TO_CPU(cee_cfg->tlv_status); + u16 app_prio = LE16_TO_CPU(cee_cfg->oper_app_prio); u8 i, tc, err, sync, oper; /* CEE PG data to ETS config */ @@ -708,11 +684,11 @@ static void i40e_cee_to_dcb_config( tc = (u8)((cee_cfg->oper_prio_tc[i] & I40E_CEE_PGID_PRIO_0_MASK) >> I40E_CEE_PGID_PRIO_0_SHIFT); - dcbcfg->etscfg.prioritytable[i * 2] = tc; + dcbcfg->etscfg.prioritytable[i*2] = tc; tc = (u8)((cee_cfg->oper_prio_tc[i] & I40E_CEE_PGID_PRIO_1_MASK) >> I40E_CEE_PGID_PRIO_1_SHIFT); - dcbcfg->etscfg.prioritytable[i * 2 + 1] = tc; + dcbcfg->etscfg.prioritytable[i*2 + 1] = tc; } for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) @@ -792,7 +768,7 @@ static void i40e_cee_to_dcb_config( **/ static i40e_status i40e_get_ieee_dcb_config(struct i40e_hw *hw) { - i40e_status ret = 0; + i40e_status ret = I40E_SUCCESS; /* IEEE mode */ hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_IEEE; @@ -808,7 +784,7 @@ static i40e_status i40e_get_ieee_dcb_config(struct i40e_hw *hw) &hw->remote_dcbx_config); /* Don't treat ENOENT as an error for Remote MIBs */ if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT) - ret = 0; + ret = I40E_SUCCESS; out: return ret; @@ -822,7 +798,7 @@ out: **/ i40e_status i40e_get_dcb_config(struct i40e_hw *hw) { - i40e_status ret = 0; + i40e_status ret = I40E_SUCCESS; struct i40e_aqc_get_cee_dcb_cfg_resp cee_cfg; struct i40e_aqc_get_cee_dcb_cfg_v1_resp cee_v1_cfg; @@ -837,22 +813,22 @@ i40e_status i40e_get_dcb_config(struct i40e_hw *hw) ((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver == 33))) { ret = i40e_aq_get_cee_dcb_config(hw, &cee_v1_cfg, sizeof(cee_v1_cfg), NULL); - if (!ret) { + if (ret == I40E_SUCCESS) { /* CEE mode */ hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_CEE; hw->local_dcbx_config.tlv_status = - le16_to_cpu(cee_v1_cfg.tlv_status); + LE16_TO_CPU(cee_v1_cfg.tlv_status); i40e_cee_to_dcb_v1_config(&cee_v1_cfg, &hw->local_dcbx_config); } } else { ret = i40e_aq_get_cee_dcb_config(hw, &cee_cfg, sizeof(cee_cfg), NULL); - if (!ret) { + if (ret == I40E_SUCCESS) { /* CEE mode */ hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_CEE; hw->local_dcbx_config.tlv_status = - le32_to_cpu(cee_cfg.tlv_status); + LE32_TO_CPU(cee_cfg.tlv_status); i40e_cee_to_dcb_config(&cee_cfg, &hw->local_dcbx_config); } @@ -862,7 +838,7 @@ i40e_status i40e_get_dcb_config(struct i40e_hw *hw) if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT) return i40e_get_ieee_dcb_config(hw); - if (ret) + if (ret != I40E_SUCCESS) goto out; /* Get CEE DCB Desired Config */ @@ -873,11 +849,11 @@ i40e_status i40e_get_dcb_config(struct i40e_hw *hw) /* Get Remote DCB Config */ ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE, - I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE, - &hw->remote_dcbx_config); + I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE, + &hw->remote_dcbx_config); /* Don't treat ENOENT as an error for Remote MIBs */ if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT) - ret = 0; + ret = I40E_SUCCESS; out: return ret; @@ -886,22 +862,41 @@ out: /** * i40e_init_dcb * @hw: pointer to the hw struct + * @enable_mib_change: enable mib change event * * Update DCB configuration from the Firmware **/ -i40e_status i40e_init_dcb(struct i40e_hw *hw) +i40e_status i40e_init_dcb(struct i40e_hw *hw, bool enable_mib_change) { - i40e_status ret = 0; + i40e_status ret = I40E_SUCCESS; struct i40e_lldp_variables lldp_cfg; u8 adminstatus = 0; if (!hw->func_caps.dcb) - return ret; + return I40E_NOT_SUPPORTED; /* Read LLDP NVM area */ - ret = i40e_read_lldp_cfg(hw, &lldp_cfg); + if (hw->flags & I40E_HW_FLAG_FW_LLDP_PERSISTENT) { + u8 offset = 0; + + if (hw->mac.type == I40E_MAC_XL710) + offset = I40E_LLDP_CURRENT_STATUS_XL710_OFFSET; + else if (hw->mac.type == I40E_MAC_X722) + offset = I40E_LLDP_CURRENT_STATUS_X722_OFFSET; + else + return I40E_NOT_SUPPORTED; + + ret = i40e_read_nvm_module_data(hw, + I40E_SR_EMP_SR_SETTINGS_PTR, + offset, + I40E_LLDP_CURRENT_STATUS_OFFSET, + I40E_LLDP_CURRENT_STATUS_SIZE, + &lldp_cfg.adminstatus); + } else { + ret = i40e_read_lldp_cfg(hw, &lldp_cfg); + } if (ret) - return ret; + return I40E_ERR_NOT_READY; /* Get the LLDP AdminStatus for the current port */ adminstatus = lldp_cfg.adminstatus >> (hw->port * 4); @@ -910,7 +905,7 @@ i40e_status i40e_init_dcb(struct i40e_hw *hw) /* LLDP agent disabled */ if (!adminstatus) { hw->dcbx_status = I40E_DCBX_STATUS_DISABLED; - return ret; + return I40E_ERR_NOT_READY; } /* Get DCBX status */ @@ -919,27 +914,454 @@ i40e_status i40e_init_dcb(struct i40e_hw *hw) return ret; /* Check the DCBX Status */ - switch (hw->dcbx_status) { - case I40E_DCBX_STATUS_DONE: - case I40E_DCBX_STATUS_IN_PROGRESS: + if (hw->dcbx_status == I40E_DCBX_STATUS_DONE || + hw->dcbx_status == I40E_DCBX_STATUS_IN_PROGRESS) { /* Get current DCBX configuration */ ret = i40e_get_dcb_config(hw); if (ret) return ret; - break; - case I40E_DCBX_STATUS_DISABLED: + } else if (hw->dcbx_status == I40E_DCBX_STATUS_DISABLED) { + return I40E_ERR_NOT_READY; + } + + /* Configure the LLDP MIB change event */ + if (enable_mib_change) + ret = i40e_aq_cfg_lldp_mib_change_event(hw, true, NULL); + + return ret; +} + +/** + * i40e_get_fw_lldp_status + * @hw: pointer to the hw struct + * @lldp_status: pointer to the status enum + * + * Get status of FW Link Layer Discovery Protocol (LLDP) Agent. + * Status of agent is reported via @lldp_status parameter. + **/ +enum i40e_status_code +i40e_get_fw_lldp_status(struct i40e_hw *hw, + enum i40e_get_fw_lldp_status_resp *lldp_status) +{ + i40e_status ret; + struct i40e_virt_mem mem; + u8 *lldpmib; + + if (!lldp_status) + return I40E_ERR_PARAM; + + /* Allocate buffer for the LLDPDU */ + ret = i40e_allocate_virt_mem(hw, &mem, I40E_LLDPDU_SIZE); + if (ret) return ret; - case I40E_DCBX_STATUS_NOT_STARTED: - case I40E_DCBX_STATUS_MULTIPLE_PEERS: + + lldpmib = (u8 *)mem.va; + ret = i40e_aq_get_lldp_mib(hw, 0, 0, (void *)lldpmib, + I40E_LLDPDU_SIZE, NULL, NULL, NULL); + + if (ret == I40E_SUCCESS) { + *lldp_status = I40E_GET_FW_LLDP_STATUS_ENABLED; + } else if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT) { + /* MIB is not available yet but the agent is running */ + *lldp_status = I40E_GET_FW_LLDP_STATUS_ENABLED; + ret = I40E_SUCCESS; + } else if (hw->aq.asq_last_status == I40E_AQ_RC_EPERM) { + *lldp_status = I40E_GET_FW_LLDP_STATUS_DISABLED; + ret = I40E_SUCCESS; + } + + i40e_free_virt_mem(hw, &mem); + return ret; +} + +/** + * i40e_add_ieee_ets_tlv - Prepare ETS TLV in IEEE format + * @tlv: Fill the ETS config data in IEEE format + * @dcbcfg: Local store which holds the DCB Config + * + * Prepare IEEE 802.1Qaz ETS CFG TLV + **/ +static void i40e_add_ieee_ets_tlv(struct i40e_lldp_org_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + u8 priority0, priority1, maxtcwilling = 0; + struct i40e_dcb_ets_config *etscfg; + u16 offset = 0, typelength, i; + u8 *buf = tlv->tlvinfo; + u32 ouisubtype; + + typelength = (u16)((I40E_TLV_TYPE_ORG << I40E_LLDP_TLV_TYPE_SHIFT) | + I40E_IEEE_ETS_TLV_LENGTH); + tlv->typelength = htons(typelength); + + ouisubtype = (u32)((I40E_IEEE_8021QAZ_OUI << I40E_LLDP_TLV_OUI_SHIFT) | + I40E_IEEE_SUBTYPE_ETS_CFG); + tlv->ouisubtype = htonl(ouisubtype); + + /* First Octet post subtype + * -------------------------- + * |will-|CBS | Re- | Max | + * |ing | |served| TCs | + * -------------------------- + * |1bit | 1bit|3 bits|3bits| + */ + etscfg = &dcbcfg->etscfg; + if (etscfg->willing) + maxtcwilling = BIT(I40E_IEEE_ETS_WILLING_SHIFT); + maxtcwilling |= etscfg->maxtcs & I40E_IEEE_ETS_MAXTC_MASK; + buf[offset] = maxtcwilling; + + /* Move offset to Priority Assignment Table */ + offset++; + + /* Priority Assignment Table (4 octets) + * Octets:| 1 | 2 | 3 | 4 | + * ----------------------------------------- + * |pri0|pri1|pri2|pri3|pri4|pri5|pri6|pri7| + * ----------------------------------------- + * Bits:|7 4|3 0|7 4|3 0|7 4|3 0|7 4|3 0| + * ----------------------------------------- + */ + for (i = 0; i < 4; i++) { + priority0 = etscfg->prioritytable[i * 2] & 0xF; + priority1 = etscfg->prioritytable[i * 2 + 1] & 0xF; + buf[offset] = (priority0 << I40E_IEEE_ETS_PRIO_1_SHIFT) | + priority1; + offset++; + } + + /* TC Bandwidth Table (8 octets) + * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | + * --------------------------------- + * |tc0|tc1|tc2|tc3|tc4|tc5|tc6|tc7| + * --------------------------------- + */ + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) + buf[offset++] = etscfg->tcbwtable[i]; + + /* TSA Assignment Table (8 octets) + * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | + * --------------------------------- + * |tc0|tc1|tc2|tc3|tc4|tc5|tc6|tc7| + * --------------------------------- + */ + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) + buf[offset++] = etscfg->tsatable[i]; +} + +/** + * i40e_add_ieee_etsrec_tlv - Prepare ETS Recommended TLV in IEEE format + * @tlv: Fill ETS Recommended TLV in IEEE format + * @dcbcfg: Local store which holds the DCB Config + * + * Prepare IEEE 802.1Qaz ETS REC TLV + **/ +static void i40e_add_ieee_etsrec_tlv(struct i40e_lldp_org_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + struct i40e_dcb_ets_config *etsrec; + u16 offset = 0, typelength, i; + u8 priority0, priority1; + u8 *buf = tlv->tlvinfo; + u32 ouisubtype; + + typelength = (u16)((I40E_TLV_TYPE_ORG << I40E_LLDP_TLV_TYPE_SHIFT) | + I40E_IEEE_ETS_TLV_LENGTH); + tlv->typelength = htons(typelength); + + ouisubtype = (u32)((I40E_IEEE_8021QAZ_OUI << I40E_LLDP_TLV_OUI_SHIFT) | + I40E_IEEE_SUBTYPE_ETS_REC); + tlv->ouisubtype = htonl(ouisubtype); + + etsrec = &dcbcfg->etsrec; + /* First Octet is reserved */ + /* Move offset to Priority Assignment Table */ + offset++; + + /* Priority Assignment Table (4 octets) + * Octets:| 1 | 2 | 3 | 4 | + * ----------------------------------------- + * |pri0|pri1|pri2|pri3|pri4|pri5|pri6|pri7| + * ----------------------------------------- + * Bits:|7 4|3 0|7 4|3 0|7 4|3 0|7 4|3 0| + * ----------------------------------------- + */ + for (i = 0; i < 4; i++) { + priority0 = etsrec->prioritytable[i * 2] & 0xF; + priority1 = etsrec->prioritytable[i * 2 + 1] & 0xF; + buf[offset] = (priority0 << I40E_IEEE_ETS_PRIO_1_SHIFT) | + priority1; + offset++; + } + + /* TC Bandwidth Table (8 octets) + * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | + * --------------------------------- + * |tc0|tc1|tc2|tc3|tc4|tc5|tc6|tc7| + * --------------------------------- + */ + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) + buf[offset++] = etsrec->tcbwtable[i]; + + /* TSA Assignment Table (8 octets) + * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | + * --------------------------------- + * |tc0|tc1|tc2|tc3|tc4|tc5|tc6|tc7| + * --------------------------------- + */ + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) + buf[offset++] = etsrec->tsatable[i]; +} + + /** + * i40e_add_ieee_pfc_tlv - Prepare PFC TLV in IEEE format + * @tlv: Fill PFC TLV in IEEE format + * @dcbcfg: Local store to get PFC CFG data + * + * Prepare IEEE 802.1Qaz PFC CFG TLV + **/ +static void i40e_add_ieee_pfc_tlv(struct i40e_lldp_org_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + u8 *buf = tlv->tlvinfo; + u32 ouisubtype; + u16 typelength; + + typelength = (u16)((I40E_TLV_TYPE_ORG << I40E_LLDP_TLV_TYPE_SHIFT) | + I40E_IEEE_PFC_TLV_LENGTH); + tlv->typelength = htons(typelength); + + ouisubtype = (u32)((I40E_IEEE_8021QAZ_OUI << I40E_LLDP_TLV_OUI_SHIFT) | + I40E_IEEE_SUBTYPE_PFC_CFG); + tlv->ouisubtype = htonl(ouisubtype); + + /* ---------------------------------------- + * |will-|MBC | Re- | PFC | PFC Enable | + * |ing | |served| cap | | + * ----------------------------------------- + * |1bit | 1bit|2 bits|4bits| 1 octet | + */ + if (dcbcfg->pfc.willing) + buf[0] = BIT(I40E_IEEE_PFC_WILLING_SHIFT); + + if (dcbcfg->pfc.mbc) + buf[0] |= BIT(I40E_IEEE_PFC_MBC_SHIFT); + + buf[0] |= dcbcfg->pfc.pfccap & 0xF; + buf[1] = dcbcfg->pfc.pfcenable; +} + +/** + * i40e_add_ieee_app_pri_tlv - Prepare APP TLV in IEEE format + * @tlv: Fill APP TLV in IEEE format + * @dcbcfg: Local store to get APP CFG data + * + * Prepare IEEE 802.1Qaz APP CFG TLV + **/ +static void i40e_add_ieee_app_pri_tlv(struct i40e_lldp_org_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + u16 typelength, length, offset = 0; + u8 priority, selector, i = 0; + u8 *buf = tlv->tlvinfo; + u32 ouisubtype; + + /* No APP TLVs then just return */ + if (dcbcfg->numapps == 0) + return; + ouisubtype = (u32)((I40E_IEEE_8021QAZ_OUI << I40E_LLDP_TLV_OUI_SHIFT) | + I40E_IEEE_SUBTYPE_APP_PRI); + tlv->ouisubtype = htonl(ouisubtype); + + /* Move offset to App Priority Table */ + offset++; + /* Application Priority Table (3 octets) + * Octets:| 1 | 2 | 3 | + * ----------------------------------------- + * |Priority|Rsrvd| Sel | Protocol ID | + * ----------------------------------------- + * Bits:|23 21|20 19|18 16|15 0| + * ----------------------------------------- + */ + while (i < dcbcfg->numapps) { + priority = dcbcfg->app[i].priority & 0x7; + selector = dcbcfg->app[i].selector & 0x7; + buf[offset] = (priority << I40E_IEEE_APP_PRIO_SHIFT) | selector; + buf[offset + 1] = (dcbcfg->app[i].protocolid >> 0x8) & 0xFF; + buf[offset + 2] = dcbcfg->app[i].protocolid & 0xFF; + /* Move to next app */ + offset += 3; + i++; + if (i >= I40E_DCBX_MAX_APPS) + break; + } + /* length includes size of ouisubtype + 1 reserved + 3*numapps */ + length = sizeof(tlv->ouisubtype) + 1 + (i*3); + typelength = (u16)((I40E_TLV_TYPE_ORG << I40E_LLDP_TLV_TYPE_SHIFT) | + (length & 0x1FF)); + tlv->typelength = htons(typelength); +} + + /** + * i40e_add_dcb_tlv - Add all IEEE TLVs + * @tlv: pointer to org tlv + * + * add tlv information + **/ +static void i40e_add_dcb_tlv(struct i40e_lldp_org_tlv *tlv, + struct i40e_dcbx_config *dcbcfg, + u16 tlvid) +{ + switch (tlvid) { + case I40E_IEEE_TLV_ID_ETS_CFG: + i40e_add_ieee_ets_tlv(tlv, dcbcfg); + break; + case I40E_IEEE_TLV_ID_ETS_REC: + i40e_add_ieee_etsrec_tlv(tlv, dcbcfg); + break; + case I40E_IEEE_TLV_ID_PFC_CFG: + i40e_add_ieee_pfc_tlv(tlv, dcbcfg); + break; + case I40E_IEEE_TLV_ID_APP_PRI: + i40e_add_ieee_app_pri_tlv(tlv, dcbcfg); + break; default: break; } +} - /* Configure the LLDP MIB change event */ - ret = i40e_aq_cfg_lldp_mib_change_event(hw, true, NULL); + /** + * i40e_set_dcb_config - Set the local LLDP MIB to FW + * @hw: pointer to the hw struct + * + * Set DCB configuration to the Firmware + **/ +i40e_status i40e_set_dcb_config(struct i40e_hw *hw) +{ + i40e_status ret = I40E_SUCCESS; + struct i40e_dcbx_config *dcbcfg; + struct i40e_virt_mem mem; + u8 mib_type, *lldpmib; + u16 miblen; + + /* update the hw local config */ + dcbcfg = &hw->local_dcbx_config; + /* Allocate the LLDPDU */ + ret = i40e_allocate_virt_mem(hw, &mem, I40E_LLDPDU_SIZE); if (ret) return ret; + mib_type = SET_LOCAL_MIB_AC_TYPE_LOCAL_MIB; + if (dcbcfg->app_mode == I40E_DCBX_APPS_NON_WILLING) { + mib_type |= SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS << + SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_SHIFT; + } + lldpmib = (u8 *)mem.va; + ret = i40e_dcb_config_to_lldp(lldpmib, &miblen, dcbcfg); + ret = i40e_aq_set_lldp_mib(hw, mib_type, (void *)lldpmib, miblen, NULL); + + i40e_free_virt_mem(hw, &mem); + return ret; +} + +/** + * i40e_dcb_config_to_lldp - Convert Dcbconfig to MIB format + * @lldpmib: pointer to mib to be output + * @miblen: pointer to u16 for length of lldpmib + * @dcbcfg: store for LLDPDU data + * + * send DCB configuration to FW + **/ +i40e_status i40e_dcb_config_to_lldp(u8 *lldpmib, u16 *miblen, + struct i40e_dcbx_config *dcbcfg) +{ + u16 length, offset = 0, tlvid = I40E_TLV_ID_START; + i40e_status ret = I40E_SUCCESS; + struct i40e_lldp_org_tlv *tlv; + u16 typelength; + + tlv = (struct i40e_lldp_org_tlv *)lldpmib; + while (1) { + i40e_add_dcb_tlv(tlv, dcbcfg, tlvid++); + typelength = ntohs(tlv->typelength); + length = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >> + I40E_LLDP_TLV_LEN_SHIFT); + if (length) + offset += length + 2; + /* END TLV or beyond LLDPDU size */ + if ((tlvid >= I40E_TLV_ID_END_OF_LLDPPDU) || + (offset > I40E_LLDPDU_SIZE)) + break; + /* Move to next TLV */ + if (length) + tlv = (struct i40e_lldp_org_tlv *)((char *)tlv + + sizeof(tlv->typelength) + length); + } + *miblen = offset; + return ret; +} + +/** + * _i40e_read_lldp_cfg - generic read of LLDP Configuration data from NVM + * @hw: pointer to the HW structure + * @lldp_cfg: pointer to hold lldp configuration variables + * @module: address of the module pointer + * @word_offset: offset of LLDP configuration + * + * Reads the LLDP configuration data from NVM using passed addresses + **/ +static i40e_status _i40e_read_lldp_cfg(struct i40e_hw *hw, + struct i40e_lldp_variables *lldp_cfg, + u8 module, u32 word_offset) +{ + u32 address, offset = (2 * word_offset); + i40e_status ret; + __le16 raw_mem; + u16 mem; + + ret = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); + if (ret != I40E_SUCCESS) + return ret; + + ret = i40e_aq_read_nvm(hw, 0x0, module * 2, sizeof(raw_mem), &raw_mem, + true, NULL); + i40e_release_nvm(hw); + if (ret != I40E_SUCCESS) + return ret; + + mem = LE16_TO_CPU(raw_mem); + /* Check if this pointer needs to be read in word size or 4K sector + * units. + */ + if (mem & I40E_PTR_TYPE) + address = (0x7FFF & mem) * 4096; + else + address = (0x7FFF & mem) * 2; + + ret = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); + if (ret != I40E_SUCCESS) + goto err_lldp_cfg; + + ret = i40e_aq_read_nvm(hw, module, offset, sizeof(raw_mem), &raw_mem, + true, NULL); + i40e_release_nvm(hw); + if (ret != I40E_SUCCESS) + return ret; + + mem = LE16_TO_CPU(raw_mem); + offset = mem + word_offset; + offset *= 2; + + ret = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); + if (ret != I40E_SUCCESS) + goto err_lldp_cfg; + + ret = i40e_aq_read_nvm(hw, 0, address + offset, + sizeof(struct i40e_lldp_variables), lldp_cfg, + true, NULL); + i40e_release_nvm(hw); + +err_lldp_cfg: return ret; } @@ -951,24 +1373,37 @@ i40e_status i40e_init_dcb(struct i40e_hw *hw) * Reads the LLDP configuration data from NVM **/ i40e_status i40e_read_lldp_cfg(struct i40e_hw *hw, - struct i40e_lldp_variables *lldp_cfg) + struct i40e_lldp_variables *lldp_cfg) { - i40e_status ret = 0; - u32 offset = (2 * I40E_NVM_LLDP_CFG_PTR); + i40e_status ret = I40E_SUCCESS; + u32 mem; if (!lldp_cfg) return I40E_ERR_PARAM; ret = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); - if (ret) - goto err_lldp_cfg; + if (ret != I40E_SUCCESS) + return ret; - ret = i40e_aq_read_nvm(hw, I40E_SR_EMP_MODULE_PTR, offset, - sizeof(struct i40e_lldp_variables), - (u8 *)lldp_cfg, - true, NULL); + ret = i40e_aq_read_nvm(hw, I40E_SR_NVM_CONTROL_WORD, 0, sizeof(mem), + &mem, true, NULL); i40e_release_nvm(hw); + if (ret != I40E_SUCCESS) + return ret; + + /* Read a bit that holds information whether we are running flat or + * structured NVM image. Flat image has LLDP configuration in shadow + * ram, so there is a need to pass different addresses for both cases. + */ + if (mem & I40E_SR_NVM_MAP_STRUCTURE_TYPE) { + /* Flat NVM case */ + ret = _i40e_read_lldp_cfg(hw, lldp_cfg, I40E_SR_EMP_MODULE_PTR, + I40E_SR_LLDP_CFG_PTR); + } else { + /* Good old structured NVM image */ + ret = _i40e_read_lldp_cfg(hw, lldp_cfg, I40E_EMP_MODULE_PTR, + I40E_NVM_LLDP_CFG_PTR); + } -err_lldp_cfg: return ret; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.h b/drivers/net/ethernet/intel/i40e/i40e_dcb.h index 92d01042c..3cdaeeb15 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.h +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.h @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_DCB_H_ #define _I40E_DCB_H_ @@ -53,6 +30,11 @@ #define I40E_CEE_SUBTYPE_APP_PRI 4 #define I40E_CEE_MAX_FEAT_TYPE 3 +#define I40E_LLDP_CURRENT_STATUS_XL710_OFFSET 0x2B +#define I40E_LLDP_CURRENT_STATUS_X722_OFFSET 0x31 +#define I40E_LLDP_CURRENT_STATUS_OFFSET 1 +#define I40E_LLDP_CURRENT_STATUS_SIZE 1 + /* Defines for LLDP TLV header */ #define I40E_LLDP_TLV_LEN_SHIFT 0 #define I40E_LLDP_TLV_LEN_MASK (0x01FF << I40E_LLDP_TLV_LEN_SHIFT) @@ -98,6 +80,20 @@ #define I40E_IEEE_APP_PRIO_SHIFT 5 #define I40E_IEEE_APP_PRIO_MASK (0x7 << I40E_IEEE_APP_PRIO_SHIFT) +/* TLV definitions for preparing MIB */ +#define I40E_TLV_ID_CHASSIS_ID 0 +#define I40E_TLV_ID_PORT_ID 1 +#define I40E_TLV_ID_TIME_TO_LIVE 2 +#define I40E_IEEE_TLV_ID_ETS_CFG 3 +#define I40E_IEEE_TLV_ID_ETS_REC 4 +#define I40E_IEEE_TLV_ID_PFC_CFG 5 +#define I40E_IEEE_TLV_ID_APP_PRI 6 +#define I40E_TLV_ID_END_OF_LLDPPDU 7 +#define I40E_TLV_ID_START I40E_IEEE_TLV_ID_ETS_CFG + +#define I40E_IEEE_ETS_TLV_LENGTH 25 +#define I40E_IEEE_PFC_TLV_LENGTH 6 +#define I40E_IEEE_APP_TLV_LENGTH 11 #pragma pack(1) @@ -139,6 +135,11 @@ struct i40e_cee_app_prio { }; #pragma pack() +enum i40e_get_fw_lldp_status_resp { + I40E_GET_FW_LLDP_STATUS_DISABLED = 0, + I40E_GET_FW_LLDP_STATUS_ENABLED = 1 +}; + i40e_status i40e_get_dcbx_status(struct i40e_hw *hw, u16 *status); i40e_status i40e_lldp_to_dcb_config(u8 *lldpmib, @@ -147,5 +148,12 @@ i40e_status i40e_aq_get_dcb_config(struct i40e_hw *hw, u8 mib_type, u8 bridgetype, struct i40e_dcbx_config *dcbcfg); i40e_status i40e_get_dcb_config(struct i40e_hw *hw); -i40e_status i40e_init_dcb(struct i40e_hw *hw); +i40e_status i40e_init_dcb(struct i40e_hw *hw, + bool enable_mib_change); +enum i40e_status_code +i40e_get_fw_lldp_status(struct i40e_hw *hw, + enum i40e_get_fw_lldp_status_resp *lldp_status); +i40e_status i40e_set_dcb_config(struct i40e_hw *hw); +i40e_status i40e_dcb_config_to_lldp(u8 *lldpmib, u16 *miblen, + struct i40e_dcbx_config *dcbcfg); #endif /* _I40E_DCB_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c index 886e667f2..1610197b4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c @@ -1,33 +1,11 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ -#ifdef CONFIG_I40E_DCB +#ifdef CONFIG_DCB #include "i40e.h" #include +#ifdef HAVE_DCBNL_IEEE /** * i40e_get_pfc_delay - retrieve PFC Link Delay * @hw: pointer to hardware struct @@ -46,7 +24,7 @@ static void i40e_get_pfc_delay(struct i40e_hw *hw, u16 *delay) /** * i40e_dcbnl_ieee_getets - retrieve local IEEE ETS configuration - * @netdev: the corresponding netdev + * @dev: the corresponding netdev * @ets: structure to hold the ETS information * * Returns local IEEE ETS configuration @@ -85,8 +63,8 @@ static int i40e_dcbnl_ieee_getets(struct net_device *dev, /** * i40e_dcbnl_ieee_getpfc - retrieve local IEEE PFC configuration - * @netdev: the corresponding netdev - * @ets: structure to hold the PFC information + * @dev: the corresponding netdev + * @pfc: structure to hold the PFC information * * Returns local IEEE PFC configuration **/ @@ -118,7 +96,7 @@ static int i40e_dcbnl_ieee_getpfc(struct net_device *dev, /** * i40e_dcbnl_getdcbx - retrieve current DCBx capability - * @netdev: the corresponding netdev + * @dev: the corresponding netdev * * Returns DCBx capability features **/ @@ -131,7 +109,8 @@ static u8 i40e_dcbnl_getdcbx(struct net_device *dev) /** * i40e_dcbnl_get_perm_hw_addr - MAC address used by DCBx - * @netdev: the corresponding netdev + * @dev: the corresponding netdev + * @perm_addr: buffer to store the MAC address * * Returns the SAN MAC address used for LLDP exchange **/ @@ -198,8 +177,10 @@ void i40e_dcbnl_set_all(struct i40e_vsi *vsi) } } +#ifdef HAVE_DCBNL_IEEE_DELAPP /* Notify user-space of the changes */ dcbnl_ieee_notify(dev, RTM_SETDCB, DCB_CMD_IEEE_SET, 0, 0); +#endif } /** @@ -317,4 +298,5 @@ void i40e_dcbnl_setup(struct i40e_vsi *vsi) /* Set initial IEEE DCB settings */ i40e_dcbnl_set_all(vsi); } -#endif /* CONFIG_I40E_DCB */ +#endif /* HAVE_DCBNL_IEEE */ +#endif /* CONFIG_DCB */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_ddp.c b/drivers/net/ethernet/intel/i40e/i40e_ddp.c new file mode 100644 index 000000000..bda29f1a8 --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/i40e_ddp.c @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ + +#include "i40e.h" + +#include + +/** + * i40e_ddp_profiles_eq - checks if DDP profiles are the equivalent + * @a: new profile info + * @b: old profile info + * + * checks if DDP profiles are the equivalent. + * Returns true if profiles are the same. + **/ +static bool i40e_ddp_profiles_eq(struct i40e_profile_info *a, + struct i40e_profile_info *b) +{ + return a->track_id == b->track_id && + !memcmp(&a->version, &b->version, sizeof(a->version)) && + !memcmp(&a->name, &b->name, I40E_DDP_NAME_SIZE); +} + +/** + * i40e_ddp_does_profile_exist - checks if DDP profile loaded already + * @hw: HW data structure + * @pinfo: DDP profile information structure + * + * checks if DDP profile loaded already. + * Returns >0 if the profile exists. + * Returns 0 if the profile is absent. + * Returns <0 if error. + **/ +static int i40e_ddp_does_profile_exist(struct i40e_hw *hw, + struct i40e_profile_info *pinfo) +{ + struct i40e_ddp_profile_list *profile_list; + u8 buff[I40E_PROFILE_LIST_SIZE]; + i40e_status status; + int i; + + status = i40e_aq_get_ddp_list(hw, buff, I40E_PROFILE_LIST_SIZE, 0, + NULL); + if (status != I40E_SUCCESS) + return -1; + + profile_list = (struct i40e_ddp_profile_list *)buff; + for (i = 0; i < profile_list->p_count; i++) { + if (i40e_ddp_profiles_eq(pinfo, &profile_list->p_info[i])) + return 1; + } + return 0; +} + +/** + * i40e_ddp_profiles_overlap - checks if DDP profiles overlap. + * @new: new profile info + * @old: old profile info + * + * checks if DDP profiles overlap. + * Returns true if profiles are overlap. + **/ +static bool i40e_ddp_profiles_overlap(struct i40e_profile_info *new, + struct i40e_profile_info *old) +{ + unsigned int group_id_old = (u8)((old->track_id & 0x00FF0000) >> 16); + unsigned int group_id_new = (u8)((new->track_id & 0x00FF0000) >> 16); + + /* 0x00 group must be only the first */ + if (group_id_new == 0) + return true; + /* 0xFF group is compatible with anything else */ + if (group_id_new == 0xFF || group_id_old == 0xFF) + return false; + /* otherwise only profiles from the same group are compatible*/ + return group_id_old != group_id_new; +} + +/** + * i40e_ddp_does_profiles_ - checks if DDP overlaps with existing one. + * @hw: HW data structure + * @pinfo: DDP profile information structure + * + * checks if DDP profile overlaps with existing one. + * Returns >0 if the profile overlaps. + * Returns 0 if the profile is ok. + * Returns <0 if error. + **/ +static int i40e_ddp_does_profile_overlap(struct i40e_hw *hw, + struct i40e_profile_info *pinfo) +{ + struct i40e_ddp_profile_list *profile_list; + u8 buff[I40E_PROFILE_LIST_SIZE]; + i40e_status status; + int i; + + status = i40e_aq_get_ddp_list(hw, buff, I40E_PROFILE_LIST_SIZE, 0, + NULL); + if (status != I40E_SUCCESS) + return -EIO; + + profile_list = (struct i40e_ddp_profile_list *)buff; + for (i = 0; i < profile_list->p_count; i++) { + if (i40e_ddp_profiles_overlap(pinfo, + &profile_list->p_info[i])) + return 1; + } + return 0; +} + + +/** + * i40e_add_pinfo + * @hw: pointer to the hardware structure + * @profile: pointer to the profile segment of the package + * @profile_info_sec: buffer for information section + * @track_id: package tracking id + * + * Register a profile to the list of loaded profiles. + */ +static enum i40e_status_code +i40e_add_pinfo(struct i40e_hw *hw, struct i40e_profile_segment *profile, + u8 *profile_info_sec, u32 track_id) +{ + struct i40e_profile_section_header *sec; + struct i40e_profile_info *pinfo; + i40e_status status; + u32 offset = 0, info = 0; + + sec = (struct i40e_profile_section_header *)profile_info_sec; + sec->tbl_size = 1; + sec->data_end = sizeof(struct i40e_profile_section_header) + + sizeof(struct i40e_profile_info); + sec->section.type = SECTION_TYPE_INFO; + sec->section.offset = sizeof(struct i40e_profile_section_header); + sec->section.size = sizeof(struct i40e_profile_info); + pinfo = (struct i40e_profile_info *)(profile_info_sec + + sec->section.offset); + pinfo->track_id = track_id; + pinfo->version = profile->version; + pinfo->op = I40E_DDP_ADD_TRACKID; + + /* Clear reserved field */ + memset(pinfo->reserved, 0, sizeof(pinfo->reserved)); + i40e_memcpy(pinfo->name, profile->name, I40E_DDP_NAME_SIZE, + I40E_NONDMA_TO_NONDMA); + + status = i40e_aq_write_ddp(hw, (void *)sec, sec->data_end, + track_id, &offset, &info, NULL); + return status; +} + +/** + * i40e_del_pinfo - delete DDP profile info from NIC + * @hw: HW data structure + * @profile: DDP profile segment to be deleted + * @profile_info_sec: DDP profile section header + * @track_id: track ID of the profile for deletion + * + * Removes DDP profile from the NIC. + **/ +static enum i40e_status_code +i40e_del_pinfo(struct i40e_hw *hw, struct i40e_profile_segment *profile, + u8 *profile_info_sec, u32 track_id) +{ + struct i40e_profile_section_header *sec; + struct i40e_profile_info *pinfo; + i40e_status status; + u32 offset = 0, info = 0; + + sec = (struct i40e_profile_section_header *)profile_info_sec; + sec->tbl_size = 1; + sec->data_end = sizeof(struct i40e_profile_section_header) + + sizeof(struct i40e_profile_info); + sec->section.type = SECTION_TYPE_INFO; + sec->section.offset = sizeof(struct i40e_profile_section_header); + sec->section.size = sizeof(struct i40e_profile_info); + pinfo = (struct i40e_profile_info *)(profile_info_sec + + sec->section.offset); + pinfo->track_id = track_id; + pinfo->version = profile->version; + pinfo->op = I40E_DDP_REMOVE_TRACKID; + + /* Clear reserved field */ + memset(pinfo->reserved, 0, sizeof(pinfo->reserved)); + memcpy(pinfo->name, profile->name, I40E_DDP_NAME_SIZE); + + status = i40e_aq_write_ddp(hw, (void *)sec, sec->data_end, + track_id, &offset, &info, NULL); + return status; +} + +/** + * i40e_ddp_is_pkg_hdr_valid - performs basic pkg header integrity checks + * @netdev: net device structure (for logging purposes) + * @pkg_hdr: pointer to package header + * @size_huge: size of the whole DDP profile package in size_t + * + * Checks correctness of pkg header: Version, size too big/small, and + * all segment offsets alignment and boundaries. This function lets + * reject non DDP profile file to be loaded by administrator mistake. + **/ +static bool i40e_ddp_is_pkg_hdr_valid(struct net_device *netdev, + struct i40e_package_header *pkg_hdr, + size_t size_huge) +{ + u32 size = 0xFFFFFFFFU & size_huge; + u32 pkg_hdr_size; + u32 segment; + + if (!pkg_hdr) + return false; + + if (pkg_hdr->version.major > 0) { + struct i40e_ddp_version ver = pkg_hdr->version; + netdev_err(netdev, "Unsupported DDP profile version %u.%u.%u.%u", + ver.major, ver.minor, ver.update, ver.draft); + return false; + } + if (size_huge > size) { + netdev_err(netdev, "Invalid DDP profile - size is bigger than 4G"); + return false; + } + if (size < (sizeof(struct i40e_package_header) + + sizeof(struct i40e_metadata_segment) + sizeof(u32) * 2)) { + netdev_err(netdev, "Invalid DDP profile - size is too small."); + return false; + } + + pkg_hdr_size = sizeof(u32) * (pkg_hdr->segment_count + 2U); + if (size < pkg_hdr_size) { + netdev_err(netdev, "Invalid DDP profile - too many segments"); + return false; + } + for (segment = 0; segment < pkg_hdr->segment_count; ++segment) { + u32 offset = pkg_hdr->segment_offset[segment]; + + if (0xFU & offset) { + netdev_err(netdev, "Invalid DDP profile %u segment alignment", segment); + return false; + } + if (pkg_hdr_size > offset || offset >= size) { + netdev_err(netdev, "Invalid DDP profile %u segment offset", segment); + return false; + } + } + + return true; +} + +/** + * i40e_ddp_load - performs DDP loading + * @netdev: net device structure + * @data: buffer containing recipe file + * @size: size of the buffer + * @is_add: true when loading profile, false when rolling back the previous one + * + * Checks correctness and loads DDP profile to the NIC. The function is + * also used for rolling back previously loaded profile. + **/ +int i40e_ddp_load(struct net_device *netdev, const u8 *data, size_t size, + bool is_add) +{ + u8 profile_info_sec[sizeof(struct i40e_profile_section_header) + + sizeof(struct i40e_profile_info)]; + struct i40e_metadata_segment *metadata_hdr; + struct i40e_profile_segment *profile_hdr; + struct i40e_profile_info pinfo; + struct i40e_package_header *pkg_hdr; + i40e_status status; + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + u32 track_id; + int istatus; + + pkg_hdr = (struct i40e_package_header *)data; + if (!i40e_ddp_is_pkg_hdr_valid(netdev, pkg_hdr, size)) + return -EINVAL; + + if (size < (sizeof(struct i40e_package_header) + + sizeof(struct i40e_metadata_segment) + sizeof(u32) * 2)) { + netdev_err(netdev, "Invalid DDP recipe size."); + return -EINVAL; + } + + /* Find beginning of segment data in buffer */ + metadata_hdr = (struct i40e_metadata_segment *) + i40e_find_segment_in_package(SEGMENT_TYPE_METADATA, pkg_hdr); + if (!metadata_hdr) { + netdev_err(netdev, "Failed to find metadata segment in DDP recipe."); + return -EINVAL; + } + + track_id = metadata_hdr->track_id; + profile_hdr = (struct i40e_profile_segment *) + i40e_find_segment_in_package(SEGMENT_TYPE_I40E, pkg_hdr); + if (!profile_hdr) { + netdev_err(netdev, "Failed to find profile segment in DDP recipe."); + return -EINVAL; + } + + pinfo.track_id = track_id; + pinfo.version = profile_hdr->version; + if (is_add) + pinfo.op = I40E_DDP_ADD_TRACKID; + else + pinfo.op = I40E_DDP_REMOVE_TRACKID; + + memcpy(pinfo.name, profile_hdr->name, I40E_DDP_NAME_SIZE); + + /* Check if profile data already exists*/ + istatus = i40e_ddp_does_profile_exist(&pf->hw, &pinfo); + if (istatus < 0) { + netdev_err(netdev, "Failed to fetch loaded profiles."); + return istatus; + } + if (is_add) { + if (istatus > 0) { + netdev_err(netdev, "DDP profile already loaded."); + return -EINVAL; + } + istatus = i40e_ddp_does_profile_overlap(&pf->hw, &pinfo); + if (istatus < 0) { + netdev_err(netdev, "Failed to fetch loaded profiles."); + return istatus; + } + if (istatus > 0) { + netdev_err(netdev, "DDP profile overlaps with existing one."); + return -EINVAL; + } + } else { + if (istatus == 0) { + netdev_err(netdev, + "DDP profile for deletion does not exist."); + return -EINVAL; + } + } + + /* Load profile data */ + if (is_add) { + status = i40e_write_profile(&pf->hw, profile_hdr, track_id); + if (status) { + if (status == I40E_ERR_DEVICE_NOT_SUPPORTED) { + netdev_err(netdev, "Profile is not supported by the device."); + return -EPERM; + } + netdev_err(netdev, "Failed to write DDP profile."); + return -EIO; + } + } else { + status = i40e_rollback_profile(&pf->hw, profile_hdr, track_id); + if (status) { + netdev_err(netdev, "Failed to remove DDP profile."); + return -EIO; + } + } + + /* Add/remove profile to/from profile list in FW */ + if (is_add) { + status = i40e_add_pinfo(&pf->hw, profile_hdr, profile_info_sec, + track_id); + if (status) { + netdev_err(netdev, "Failed to add DDP profile info."); + return -EIO; + } + } else { + status = i40e_del_pinfo(&pf->hw, profile_hdr, profile_info_sec, + track_id); + if (status) { + netdev_err(netdev, "Failed to restore DDP profile info."); + return -EIO; + } + } + + return 0; +} + +/** + * i40e_ddp_restore - restore previously loaded profile and remove from list + * @pf: PF data struct + * + * Restores previously loaded profile stored on the list in driver memory. + * After rolling back removes entry from the list. + **/ +static int i40e_ddp_restore(struct i40e_pf *pf) +{ + struct i40e_ddp_old_profile_list *entry; + struct net_device *netdev = pf->vsi[pf->lan_vsi]->netdev; + int status = 0; + + if (!list_empty(&pf->ddp_old_prof)) { + entry = list_first_entry(&pf->ddp_old_prof, + struct i40e_ddp_old_profile_list, + list); + status = i40e_ddp_load(netdev, entry->old_ddp_buf, + entry->old_ddp_size, false); + list_del(&entry->list); + kfree(entry); + } + return status; +} + +#define I40E_DDP_PROFILE_NAME_MAX \ + (sizeof(I40E_DDP_PROFILE_PATH) + ETHTOOL_FLASH_MAX_FILENAME) + +/** + * i40e_ddp_flash - callback function for ethtool flash feature + * @netdev: net device structure + * @flash: kernel flash structure + * + * Ethtool callback function used for loading and unloading DDP profiles. + **/ +int i40e_ddp_flash(struct net_device *netdev, struct ethtool_flash *flash) +{ + const struct firmware *ddp_config; + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + int status = 0; + + /* Check for valid region first */ + if (flash->region != I40_DDP_FLASH_REGION) { + netdev_err(netdev, "Requested firmware region is not recognized by this driver."); + return -EINVAL; + } + if (pf->hw.bus.func != 0) { + netdev_err(netdev, "Any DDP operation is allowed only on Phy0 NIC interface"); + return -EINVAL; + } + + /* If the user supplied "-" instead of file name rollback previously + * stored profile. + */ + if (strncmp(flash->data, "-", 2) != 0) { + struct i40e_ddp_old_profile_list *list_entry; + char profile_name[I40E_DDP_PROFILE_NAME_MAX]; + + profile_name[sizeof(profile_name)-1]=0; + strncpy(profile_name, I40E_DDP_PROFILE_PATH, sizeof(profile_name)-1); + strncat(profile_name, flash->data, ETHTOOL_FLASH_MAX_FILENAME); + /* Load DDP recipe. */ + status = request_firmware(&ddp_config, profile_name, + &netdev->dev); + if (status) { + netdev_err(netdev, "DDP recipe file request failed."); + return status; + } + + status = i40e_ddp_load(netdev, ddp_config->data, + ddp_config->size, true); + + if (!status) { + list_entry = kzalloc( + sizeof(struct i40e_ddp_old_profile_list) + + ddp_config->size, GFP_KERNEL); + if (!list_entry) { + netdev_info(netdev, "Failed to allocate memory for previous DDP profile data."); + netdev_info(netdev, "New profile loaded but roll-back will be impossible."); + } else { + memcpy(list_entry->old_ddp_buf, + ddp_config->data, ddp_config->size); + list_entry->old_ddp_size = ddp_config->size; + list_add(&list_entry->list, &pf->ddp_old_prof); + } + } + + release_firmware(ddp_config); + } else { + if (!list_empty(&pf->ddp_old_prof)) { + status = i40e_ddp_restore(pf); + } else { + netdev_warn(netdev, "There is no DDP profile to restore."); + status = -ENOENT; + } + } + return status; +} diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 8f326f87a..928ef5b27 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2016 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifdef CONFIG_DEBUG_FS @@ -35,8 +12,8 @@ static struct dentry *i40e_dbg_root; /** * i40e_dbg_find_vsi - searches for the vsi with the given seid - * @pf - the PF structure to search for the vsi - * @seid - seid of the vsi it is searching for + * @pf: the PF structure to search for the vsi + * @seid: seid of the vsi it is searching for **/ static struct i40e_vsi *i40e_dbg_find_vsi(struct i40e_pf *pf, int seid) { @@ -54,8 +31,8 @@ static struct i40e_vsi *i40e_dbg_find_vsi(struct i40e_pf *pf, int seid) /** * i40e_dbg_find_veb - searches for the veb with the given seid - * @pf - the PF structure to search for the veb - * @seid - seid of the veb it is searching for + * @pf: the PF structure to search for the veb + * @seid: seid of the veb it is searching for **/ static struct i40e_veb *i40e_dbg_find_veb(struct i40e_pf *pf, int seid) { @@ -88,7 +65,7 @@ static ssize_t i40e_dbg_command_read(struct file *filp, char __user *buffer, { struct i40e_pf *pf = filp->private_data; int bytes_not_copied; - int buf_size = 256; + size_t buf_size = 256; char *buf; int len; @@ -124,6 +101,44 @@ static char *i40e_filter_state_string[] = { "REMOVE", }; +/** + * i40e_dbg_dump_vsi_filters - handles dump of mac/vlan filters for a VSI + * @pf: the i40e_pf created in command write + * @vsi: the vsi to dump + */ +static void i40e_dbg_dump_vsi_filters(struct i40e_pf *pf, struct i40e_vsi *vsi) +{ + struct i40e_mac_filter *f; + int bkt; + + hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { + dev_info(&pf->pdev->dev, + " mac_filter_hash: %pM vid=%d, state %s\n", + f->macaddr, f->vlan, + i40e_filter_state_string[f->state]); + } + dev_info(&pf->pdev->dev, " active_filters %u, promisc_threshold %u, overflow promisc %s\n", + vsi->active_filters, vsi->promisc_threshold, + (test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state) ? + "ON" : "OFF")); +} + +/** + * i40e_dbg_dump_all_vsi_filters - dump mac/vlan filters for all VSI on a PF + * @pf: the i40e_pf created in command write + */ +static void i40e_dbg_dump_all_vsi_filters(struct i40e_pf *pf) +{ + int i; + + for (i = 0; i < pf->num_alloc_vsi; i++) + if (pf->vsi[i]) { + dev_info(&pf->pdev->dev, "vsi seid %d\n", + pf->vsi[i]->seid); + i40e_dbg_dump_vsi_filters(pf, pf->vsi[i]); + } +} + /** * i40e_dbg_dump_vsi_seid - handles dump vsi seid write into command datum * @pf: the i40e_pf created in command write @@ -131,10 +146,13 @@ static char *i40e_filter_state_string[] = { **/ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) { +#ifdef HAVE_NDO_GET_STATS64 struct rtnl_link_stats64 *nstat; - struct i40e_mac_filter *f; +#else + struct net_device_stats *nstat; +#endif struct i40e_vsi *vsi; - int i, bkt; + int i; vsi = i40e_dbg_find_vsi(pf, seid); if (!vsi) { @@ -145,20 +163,38 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) dev_info(&pf->pdev->dev, "vsi seid %d\n", seid); if (vsi->netdev) { struct net_device *nd = vsi->netdev; +#ifdef HAVE_RHEL6_NET_DEVICE_OPS_EXT + u32 hw_features; +#endif dev_info(&pf->pdev->dev, " netdev: name = %s, state = %lu, flags = 0x%08x\n", nd->name, nd->state, nd->flags); dev_info(&pf->pdev->dev, " features = 0x%08lx\n", (unsigned long int)nd->features); +#ifdef HAVE_NDO_SET_FEATURES +#ifdef HAVE_RHEL6_NET_DEVICE_OPS_EXT + hw_features = get_netdev_hw_features(vsi->netdev); + dev_info(&pf->pdev->dev, " hw_features = 0x%08x\n", + hw_features); +#else dev_info(&pf->pdev->dev, " hw_features = 0x%08lx\n", (unsigned long int)nd->hw_features); +#endif +#endif +#ifdef HAVE_NETDEV_VLAN_FEATURES dev_info(&pf->pdev->dev, " vlan_features = 0x%08lx\n", (unsigned long int)nd->vlan_features); +#endif } +#ifdef HAVE_VLAN_RX_REGISTER + dev_info(&pf->pdev->dev, " vlgrp is %s\n", + vsi->vlgrp ? "" : ""); +#else + dev_info(&pf->pdev->dev, " active_vlans is %s\n", + vsi->active_vlans ? "" : ""); +#endif /* HAVE_VLAN_RX_REGISTER */ dev_info(&pf->pdev->dev, - " vlgrp: & = %p\n", vsi->active_vlans); - dev_info(&pf->pdev->dev, - " flags = 0x%08lx, netdev_registered = %i, current_netdev_flags = 0x%04x\n", + " flags = 0x%016llx, netdev_registered = %i, current_netdev_flags = 0x%04x\n", vsi->flags, vsi->netdev_registered, vsi->current_netdev_flags); for (i = 0; i < BITS_TO_LONGS(__I40E_VSI_STATE_SIZE__); i++) dev_info(&pf->pdev->dev, @@ -169,16 +205,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) pf->hw.mac.addr, pf->hw.mac.san_addr, pf->hw.mac.port_addr); - hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { - dev_info(&pf->pdev->dev, - " mac_filter_hash: %pM vid=%d, state %s\n", - f->macaddr, f->vlan, - i40e_filter_state_string[f->state]); - } - dev_info(&pf->pdev->dev, " active_filters %u, promisc_threshold %u, overflow promisc %s\n", - vsi->active_filters, vsi->promisc_threshold, - (test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state) ? - "ON" : "OFF")); + i40e_dbg_dump_vsi_filters(pf, vsi); nstat = i40e_get_vsi_stats_struct(vsi); dev_info(&pf->pdev->dev, " net_stats: rx_packets = %lu, rx_bytes = %lu, rx_errors = %lu, rx_dropped = %lu\n", @@ -264,22 +291,14 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) vsi->rx_buf_failed, vsi->rx_page_failed); rcu_read_lock(); for (i = 0; i < vsi->num_queue_pairs; i++) { - struct i40e_ring *rx_ring = ACCESS_ONCE(vsi->rx_rings[i]); + struct i40e_ring *rx_ring = READ_ONCE(vsi->rx_rings[i]); if (!rx_ring) continue; dev_info(&pf->pdev->dev, - " rx_rings[%i]: desc = %p\n", - i, rx_ring->desc); - dev_info(&pf->pdev->dev, - " rx_rings[%i]: dev = %p, netdev = %p, rx_bi = %p\n", - i, rx_ring->dev, - rx_ring->netdev, - rx_ring->rx_bi); - dev_info(&pf->pdev->dev, - " rx_rings[%i]: state = %li, queue_index = %d, reg_idx = %d\n", - i, rx_ring->state, + " rx_rings[%i]: state = %lu, queue_index = %d, reg_idx = %d\n", + i, *rx_ring->state, rx_ring->queue_index, rx_ring->reg_idx); dev_info(&pf->pdev->dev, @@ -307,35 +326,23 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) rx_ring->rx_stats.realloc_count, rx_ring->rx_stats.page_reuse_count); dev_info(&pf->pdev->dev, - " rx_rings[%i]: size = %i, dma = 0x%08lx\n", - i, rx_ring->size, - (unsigned long int)rx_ring->dma); - dev_info(&pf->pdev->dev, - " rx_rings[%i]: vsi = %p, q_vector = %p\n", - i, rx_ring->vsi, - rx_ring->q_vector); + " rx_rings[%i]: size = %i\n", + i, rx_ring->size); dev_info(&pf->pdev->dev, " rx_rings[%i]: rx_itr_setting = %d (%s)\n", - i, rx_ring->rx_itr_setting, - ITR_IS_DYNAMIC(rx_ring->rx_itr_setting) ? "dynamic" : "fixed"); + i, rx_ring->itr_setting, + ITR_IS_DYNAMIC(rx_ring->itr_setting) ? + "dynamic" : "fixed"); } for (i = 0; i < vsi->num_queue_pairs; i++) { - struct i40e_ring *tx_ring = ACCESS_ONCE(vsi->tx_rings[i]); + struct i40e_ring *tx_ring = READ_ONCE(vsi->tx_rings[i]); if (!tx_ring) continue; dev_info(&pf->pdev->dev, - " tx_rings[%i]: desc = %p\n", - i, tx_ring->desc); - dev_info(&pf->pdev->dev, - " tx_rings[%i]: dev = %p, netdev = %p, tx_bi = %p\n", - i, tx_ring->dev, - tx_ring->netdev, - tx_ring->tx_bi); - dev_info(&pf->pdev->dev, - " tx_rings[%i]: state = %li, queue_index = %d, reg_idx = %d\n", - i, tx_ring->state, + " tx_rings[%i]: state = %lu, queue_index = %d, reg_idx = %d\n", + i, *tx_ring->state, tx_ring->queue_index, tx_ring->reg_idx); dev_info(&pf->pdev->dev, @@ -355,20 +362,16 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) tx_ring->tx_stats.tx_busy, tx_ring->tx_stats.tx_done_old); dev_info(&pf->pdev->dev, - " tx_rings[%i]: size = %i, dma = 0x%08lx\n", - i, tx_ring->size, - (unsigned long int)tx_ring->dma); - dev_info(&pf->pdev->dev, - " tx_rings[%i]: vsi = %p, q_vector = %p\n", - i, tx_ring->vsi, - tx_ring->q_vector); + " tx_rings[%i]: size = %i\n", + i, tx_ring->size); dev_info(&pf->pdev->dev, " tx_rings[%i]: DCB tc = %d\n", i, tx_ring->dcb_tc); dev_info(&pf->pdev->dev, " tx_rings[%i]: tx_itr_setting = %d (%s)\n", - i, tx_ring->tx_itr_setting, - ITR_IS_DYNAMIC(tx_ring->tx_itr_setting) ? "dynamic" : "fixed"); + i, tx_ring->itr_setting, + ITR_IS_DYNAMIC(tx_ring->itr_setting) ? + "dynamic" : "fixed"); } rcu_read_unlock(); dev_info(&pf->pdev->dev, @@ -384,8 +387,9 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) " seid = %d, id = %d, uplink_seid = %d\n", vsi->seid, vsi->id, vsi->uplink_seid); dev_info(&pf->pdev->dev, - " base_queue = %d, num_queue_pairs = %d, num_desc = %d\n", - vsi->base_queue, vsi->num_queue_pairs, vsi->num_desc); + " base_queue = %d, num_queue_pairs = %d, num_tx_desc = %d, num_rx_desc = %d\n", + vsi->base_queue, vsi->num_queue_pairs, vsi->num_tx_desc, + vsi->num_rx_desc); dev_info(&pf->pdev->dev, " type = %i\n", vsi->type); if (vsi->type == I40E_VSI_SRIOV) dev_info(&pf->pdev->dev, " VF ID = %i\n", vsi->vf_id); @@ -466,8 +470,6 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) vsi->info.resp_reserved[6], vsi->info.resp_reserved[7], vsi->info.resp_reserved[8], vsi->info.resp_reserved[9], vsi->info.resp_reserved[10], vsi->info.resp_reserved[11]); - if (vsi->back) - dev_info(&pf->pdev->dev, " PF = %p\n", vsi->back); dev_info(&pf->pdev->dev, " idx = %d\n", vsi->idx); dev_info(&pf->pdev->dev, " tc_config: numtc = %d, enabled_tc = 0x%x\n", @@ -534,6 +536,15 @@ static void i40e_dbg_dump_aq_desc(struct i40e_pf *pf) } } +/* Helper macros for printing upper half of the 32byte descriptor. */ +#ifdef I40E_32BYTE_RX +#define RXD_RSVD1(_rxd) ((_rxd)->read.rsvd1) +#define RXD_RSVD2(_rxd) ((_rxd)->read.rsvd2) +#else +#define RXD_RSVD1(_rxd) 0ULL +#define RXD_RSVD2(_rxd) 0ULL +#endif + /** * i40e_dbg_dump_desc - handles dump desc write into command datum * @cnt: number of arguments that the user supplied @@ -548,7 +559,7 @@ static void i40e_dbg_dump_desc(int cnt, int vsi_seid, int ring_id, int desc_n, { struct i40e_tx_desc *txd; union i40e_rx_desc *rxd; - struct i40e_ring *ring; + struct i40e_ring ring; struct i40e_vsi *vsi; int i; @@ -567,58 +578,59 @@ static void i40e_dbg_dump_desc(int cnt, int vsi_seid, int ring_id, int desc_n, vsi_seid); return; } - - ring = kmemdup(is_rx_ring - ? vsi->rx_rings[ring_id] : vsi->tx_rings[ring_id], - sizeof(*ring), GFP_KERNEL); - if (!ring) - return; - + if (is_rx_ring) + ring = *vsi->rx_rings[ring_id]; + else + ring = *vsi->tx_rings[ring_id]; if (cnt == 2) { + void *head = (struct i40e_tx_desc *)ring.desc + ring.count; + u32 tx_head = le32_to_cpu(*(volatile __le32 *)head); + dev_info(&pf->pdev->dev, "vsi = %02i %s ring = %02i\n", vsi_seid, is_rx_ring ? "rx" : "tx", ring_id); - for (i = 0; i < ring->count; i++) { + dev_info(&pf->pdev->dev, "head = %04x tail = %04x\n", + is_rx_ring ? 0 : tx_head, readl(ring.tail)); + dev_info(&pf->pdev->dev, "ntc = %04x ntu = %04x\n", + ring.next_to_clean, ring.next_to_use); + for (i = 0; i < ring.count; i++) { if (!is_rx_ring) { - txd = I40E_TX_DESC(ring, i); + txd = I40E_TX_DESC(&ring, i); dev_info(&pf->pdev->dev, " d[%03x] = 0x%016llx 0x%016llx\n", i, txd->buffer_addr, txd->cmd_type_offset_bsz); } else { - rxd = I40E_RX_DESC(ring, i); + rxd = I40E_RX_DESC(&ring, i); dev_info(&pf->pdev->dev, " d[%03x] = 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n", i, rxd->read.pkt_addr, rxd->read.hdr_addr, - rxd->read.rsvd1, rxd->read.rsvd2); + RXD_RSVD1(rxd), RXD_RSVD2(rxd)); } } } else if (cnt == 3) { - if (desc_n >= ring->count || desc_n < 0) { + if (desc_n >= ring.count || desc_n < 0) { dev_info(&pf->pdev->dev, "descriptor %d not found\n", desc_n); - goto out; + return; } if (!is_rx_ring) { - txd = I40E_TX_DESC(ring, desc_n); + txd = I40E_TX_DESC(&ring, desc_n); dev_info(&pf->pdev->dev, "vsi = %02i tx ring = %02i d[%03x] = 0x%016llx 0x%016llx\n", vsi_seid, ring_id, desc_n, txd->buffer_addr, txd->cmd_type_offset_bsz); } else { - rxd = I40E_RX_DESC(ring, desc_n); + rxd = I40E_RX_DESC(&ring, desc_n); dev_info(&pf->pdev->dev, "vsi = %02i rx ring = %02i d[%03x] = 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n", vsi_seid, ring_id, desc_n, rxd->read.pkt_addr, rxd->read.hdr_addr, - rxd->read.rsvd1, rxd->read.rsvd2); + RXD_RSVD1(rxd), RXD_RSVD2(rxd)); } } else { dev_info(&pf->pdev->dev, "dump desc rx/tx []\n"); } - -out: - kfree(ring); } /** @@ -636,7 +648,161 @@ static void i40e_dbg_dump_vsi_no_seid(struct i40e_pf *pf) } /** - * i40e_dbg_dump_stats - handles dump stats write into command datum + * i40e_dbg_dump_resources - handles dump resources request + * @pf: the i40e_pf created in command write + **/ +static void i40e_dbg_dump_resources(struct i40e_pf *pf) +{ + struct i40e_aqc_switch_resource_alloc_element_resp *buf; + int buf_len; + u16 count = 32; + u8 num_entries; + int ret, i; + + buf_len = count * sizeof(*buf); + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) { + dev_err(&pf->pdev->dev, "Can't get memory\n"); + return; + } + + ret = i40e_aq_get_switch_resource_alloc(&pf->hw, &num_entries, + buf, count, NULL); + if (ret) { + dev_err(&pf->pdev->dev, + "fail to get resources, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + kfree(buf); + return; + } + + dev_info(&pf->pdev->dev, " resources:\n"); + dev_info(&pf->pdev->dev, " guar total used unalloc name\n"); + for (i = 0; i < num_entries; i++) { + char *p; + + switch (buf[i].resource_type) { + case I40E_AQ_RESOURCE_TYPE_VEB: + p = "vebs"; + break; + case I40E_AQ_RESOURCE_TYPE_VSI: + p = "vsis"; + break; + case I40E_AQ_RESOURCE_TYPE_MACADDR: + p = "macaddrs"; + break; + case I40E_AQ_RESOURCE_TYPE_STAG: + p = "stags"; + break; + case I40E_AQ_RESOURCE_TYPE_ETAG: + p = "etags"; + break; + case I40E_AQ_RESOURCE_TYPE_MULTICAST_HASH: + p = "multicast hash"; + break; + case I40E_AQ_RESOURCE_TYPE_UNICAST_HASH: + p = "unicast hash"; + break; + case I40E_AQ_RESOURCE_TYPE_VLAN: + p = "vlans"; + break; + case I40E_AQ_RESOURCE_TYPE_VSI_LIST_ENTRY: + p = "vsi list entries"; + break; + case I40E_AQ_RESOURCE_TYPE_ETAG_LIST_ENTRY: + p = "etag list entries"; + break; + case I40E_AQ_RESOURCE_TYPE_VLAN_STAT_POOL: + p = "vlan stat pools"; + break; + case I40E_AQ_RESOURCE_TYPE_MIRROR_RULE: + p = "mirror rules"; + break; + case I40E_AQ_RESOURCE_TYPE_QUEUE_SETS: + p = "queue sets"; + break; + case I40E_AQ_RESOURCE_TYPE_VLAN_FILTERS: + p = "vlan filters"; + break; + case I40E_AQ_RESOURCE_TYPE_INNER_MAC_FILTERS: + p = "inner mac filters"; + break; + case I40E_AQ_RESOURCE_TYPE_IP_FILTERS: + p = "ip filters"; + break; + case I40E_AQ_RESOURCE_TYPE_GRE_VN_KEYS: + p = "gre vn keys"; + break; + case I40E_AQ_RESOURCE_TYPE_VN2_KEYS: + p = "vn2 keys"; + break; + case I40E_AQ_RESOURCE_TYPE_TUNNEL_PORTS: + p = "tunnel ports"; + break; + default: + p = "unknown"; + break; + } + + dev_info(&pf->pdev->dev, " %4d %4d %4d %4d %s\n", + buf[i].guaranteed, buf[i].total, buf[i].used, + buf[i].total_unalloced, p); + } + + kfree(buf); +} + +/** + * i40e_dbg_dump_capabilities - handles dump capabilities request + * @pf: the i40e_pf created in command write + **/ +static void i40e_dbg_dump_capabilities(struct i40e_pf *pf) +{ + struct i40e_hw_capabilities *p; + + p = (struct i40e_hw_capabilities *)&pf->hw.func_caps; + dev_info(&pf->pdev->dev, " capabilities:\n"); + dev_info(&pf->pdev->dev, + " switch_mode = %d\tmgmt_mode = %d\tnpar = %d\tos2bmc = %d\n", + p->switch_mode, p->management_mode, p->npar_enable, p->os2bmc); + dev_info(&pf->pdev->dev, + " valid_functions = 0x%04x\tsr_iov_1_1 = %d\tnum_vfs = %d\tvf_base_id = %d\n", + p->valid_functions, p->sr_iov_1_1, p->num_vfs, p->vf_base_id); + dev_info(&pf->pdev->dev, " nvm_image_type = %d\n", p->nvm_image_type); + dev_info(&pf->pdev->dev, + " num_vsis = %d\tvmdq = %d\tflex10_enable = %d\tflex10_capable = %d\n", + p->num_vsis, p->vmdq, p->flex10_enable, p->flex10_capable); + dev_info(&pf->pdev->dev, + " evb_802_1_qbg = %d\tevb_802_1_qbh = %d\tmgmt_cem = %d\tieee_1588 = %d\n", + p->evb_802_1_qbg, p->evb_802_1_qbh, p->mgmt_cem, p->ieee_1588); + dev_info(&pf->pdev->dev, + " fcoe = %d\tiwarp = %d\tmdio_port_num = %d\tmdio_port_mode = %d\n", + p->fcoe, p->iwarp, p->mdio_port_num, p->mdio_port_mode); + dev_info(&pf->pdev->dev, + " dcb = %d\tenabled_tcmap = %d\tmaxtc = %d\tiscsi = %d\n", + p->dcb, p->enabled_tcmap, p->maxtc, p->iscsi); + dev_info(&pf->pdev->dev, + " fd = %d\tfd_filters_guaranteed = %d\tfd_filters_best_effort = %d\tnum_flow_director_filters = %d\n", + p->fd, p->fd_filters_guaranteed, p->fd_filters_best_effort, + p->num_flow_director_filters); + dev_info(&pf->pdev->dev, + " rss = %d\trss_table_size = %d\trss_table_entry_width = %d\n", + p->rss, p->rss_table_size, p->rss_table_entry_width); + dev_info(&pf->pdev->dev, + " led[0] = %d\tsdp[0] = %d\tled_pin_num = %d\tsdp_pin_num = %d\n", + p->led[0], p->sdp[0], p->led_pin_num, p->sdp_pin_num); + dev_info(&pf->pdev->dev, + " num_rx_qp = %d\tnum_tx_qp = %d\tbase_queue = %d\n", + p->num_rx_qp, p->num_tx_qp, p->base_queue); + dev_info(&pf->pdev->dev, + " num_msix_vectors = %d\tnum_msix_vectors_vf = %d\trx_buf_chain_len = %d\n", + p->num_msix_vectors, p->num_msix_vectors_vf, + p->rx_buf_chain_len); +} + +/** + * i40e_dbg_dump_eth_stats - handles dump stats write into command datum * @pf: the i40e_pf created in command write * @estats: the eth stats structure to be dumped **/ @@ -669,17 +835,35 @@ static void i40e_dbg_dump_eth_stats(struct i40e_pf *pf, static void i40e_dbg_dump_veb_seid(struct i40e_pf *pf, int seid) { struct i40e_veb *veb; + int i; veb = i40e_dbg_find_veb(pf, seid); if (!veb) { dev_info(&pf->pdev->dev, "can't find veb %d\n", seid); return; } +#ifdef HAVE_BRIDGE_ATTRIBS dev_info(&pf->pdev->dev, "veb idx=%d,%d stats_ic=%d seid=%d uplink=%d mode=%s\n", veb->idx, veb->veb_idx, veb->stats_idx, veb->seid, veb->uplink_seid, veb->bridge_mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); +#else + dev_info(&pf->pdev->dev, + "veb idx=%d,%d stats_ic=%d seid=%d uplink=%d mode=%s\n", + veb->idx, veb->veb_idx, veb->stats_idx, veb->seid, + veb->uplink_seid, + "VEB"); +#endif + dev_info(&pf->pdev->dev, + "veb bw: enabled_tc=0x%x bw_limit=%d bw_max_quanta=%d is_abs_credits=%d\n", + veb->enabled_tc, veb->bw_limit, veb->bw_max_quanta, + veb->is_abs_credits); + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { + dev_info(&pf->pdev->dev, "veb bw: tc=%d bw_share=%d bw_limit=%d max_quanta=%d\n", + i, veb->bw_tc_share_credits[i], + veb->bw_tc_limit_credits[i], veb->bw_tc_max_quanta[i]); + } i40e_dbg_dump_eth_stats(pf, &veb->stats); } @@ -740,6 +924,100 @@ static void i40e_dbg_dump_vf_all(struct i40e_pf *pf) i40e_dbg_dump_vf(pf, i); } +/** + * i40e_dbg_dump_dcb_cfg - Dump DCB config data struct + * @pf: the corresponding PF + * @cfg: DCB Config data structure + * @prefix: Prefix string + **/ +static void i40e_dbg_dump_dcb_cfg(struct i40e_pf *pf, + struct i40e_dcbx_config *cfg, + char *prefix) +{ + int i; + + dev_info(&pf->pdev->dev, + "%s ets_cfg: willing=%d cbs=%d, maxtcs=%d\n", + prefix, cfg->etscfg.willing, cfg->etscfg.cbs, + cfg->etscfg.maxtcs); + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { + dev_info(&pf->pdev->dev, "%s ets_cfg: up=%d tc=%d\n", + prefix, i, cfg->etscfg.prioritytable[i]); + } + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { + dev_info(&pf->pdev->dev, "%s ets_cfg: tc=%d tcbw=%d tctsa=%d\n", + prefix, i, cfg->etscfg.tcbwtable[i], + cfg->etscfg.tsatable[i]); + } + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { + dev_info(&pf->pdev->dev, "%s ets_rec: up=%d tc=%d\n", + prefix, i, cfg->etsrec.prioritytable[i]); + } + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { + dev_info(&pf->pdev->dev, "%s ets_rec: tc=%d tcbw=%d tctsa=%d\n", + prefix, i, cfg->etsrec.tcbwtable[i], + cfg->etsrec.tsatable[i]); + } + dev_info(&pf->pdev->dev, + "%s pfc_cfg: willing=%d mbc=%d, pfccap=%d pfcenable=0x%x\n", + prefix, cfg->pfc.willing, cfg->pfc.mbc, + cfg->pfc.pfccap, cfg->pfc.pfcenable); + + dev_info(&pf->pdev->dev, + "%s app_table: num_apps=%d\n", prefix, (int)cfg->numapps); + for (i = 0; i < (int)cfg->numapps; i++) { + dev_info(&pf->pdev->dev, "%s app_table: %d prio=%d selector=%d protocol=0x%x\n", + prefix, i, cfg->app[i].priority, + cfg->app[i].selector, + cfg->app[i].protocolid); + } +} + +/** + * i40e_dbg_dump_fdir_filter - Dump out flow director filter contents + * @pf: the corresponding PF + * @f: the flow director filter + **/ +static inline void i40e_dbg_dump_fdir_filter(struct i40e_pf *pf, + struct i40e_fdir_filter *f) +{ + dev_info(&pf->pdev->dev, "fdir filter %d:\n", f->fd_id); + dev_info(&pf->pdev->dev, " flow_type=%d ip4_proto=%d\n", + f->flow_type, f->ip4_proto); + dev_info(&pf->pdev->dev, " dst_ip= %pi4 dst_port=%d\n", + &f->dst_ip, f->dst_port); + dev_info(&pf->pdev->dev, " src_ip= %pi4 src_port=%d\n", + &f->src_ip, f->src_port); + dev_info(&pf->pdev->dev, " sctp_v_tag=%d q_index=%d\n", + f->sctp_v_tag, f->q_index); + if (f->flex_filter) + dev_info(&pf->pdev->dev, " flex_word=%04x flex_offset=%d\n", + f->flex_word, f->flex_offset); + dev_info(&pf->pdev->dev, " pctype=%d dest_vsi=%d dest_ctl=%d\n", + f->pctype, f->dest_vsi, f->dest_ctl); + dev_info(&pf->pdev->dev, " fd_status=%d cnt_index=%d\n", + f->fd_status, f->cnt_index); +} + +/** + * i40e_dbg_dump_cloud_filter - Dump out cloud filter contents + * @pf: the corresponding PF + * @f: the flow director filter + **/ +static inline void i40e_dbg_dump_cloud_filter(struct i40e_pf *pf, + struct i40e_cloud_filter *f) +{ + dev_info(&pf->pdev->dev, "cloud filter %d:\n", f->id); + dev_info(&pf->pdev->dev, " outer_mac[]=%pM inner_mac=%pM\n", + f->outer_mac, f->inner_mac); + dev_info(&pf->pdev->dev, " inner_vlan %d, inner_ip[0] %pi4\n", + be16_to_cpu(f->inner_vlan), f->inner_ip); + dev_info(&pf->pdev->dev, " tenant_id=%d flags=0x%02x, tunnel_type=0x%02x\n", + f->tenant_id, f->flags, f->tunnel_type); + dev_info(&pf->pdev->dev, " seid=%d queue_id=%d\n", + f->seid, f->queue_id); +} + #define I40E_MAX_DEBUG_OUT_BUFFER (4096*4) /** * i40e_dbg_command_write - write into command datum @@ -781,7 +1059,48 @@ static ssize_t i40e_dbg_command_write(struct file *filp, count = cmd_buf_tmp - cmd_buf + 1; } - if (strncmp(cmd_buf, "add vsi", 7) == 0) { + if (strncmp(cmd_buf, "read", 4) == 0) { + u32 address; + u32 value; + + cnt = sscanf(&cmd_buf[4], "%i", &address); + if (cnt != 1) { + dev_info(&pf->pdev->dev, "read \n"); + goto command_write_done; + } + + /* check the range on address */ + if (address > (pf->ioremap_len - sizeof(u32))) { + dev_info(&pf->pdev->dev, "read reg address 0x%08x too large, max=0x%08lx\n", + address, (pf->ioremap_len - sizeof(u32))); + goto command_write_done; + } + + value = rd32(&pf->hw, address); + dev_info(&pf->pdev->dev, "read: 0x%08x = 0x%08x\n", + address, value); + + } else if (strncmp(cmd_buf, "write", 5) == 0) { + u32 address, value; + + cnt = sscanf(&cmd_buf[5], "%i %i", &address, &value); + if (cnt != 2) { + dev_info(&pf->pdev->dev, "write \n"); + goto command_write_done; + } + + /* check the range on address */ + if (address > (pf->ioremap_len - sizeof(u32))) { + dev_info(&pf->pdev->dev, "write reg address 0x%08x too large, max=0x%08lx\n", + address, (pf->ioremap_len - sizeof(u32))); + goto command_write_done; + } + wr32(&pf->hw, address, value); + value = rd32(&pf->hw, address); + dev_info(&pf->pdev->dev, "write: 0x%08x = 0x%08x\n", + address, value); + + } else if (strncmp(cmd_buf, "add vsi", 7) == 0) { vsi_seid = -1; cnt = sscanf(&cmd_buf[7], "%i", &vsi_seid); if (cnt == 0) { @@ -798,8 +1117,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, */ if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) { pf->flags |= I40E_FLAG_VEB_MODE_ENABLED; - i40e_do_reset_safe(pf, - BIT_ULL(__I40E_PF_RESET_REQUESTED)); + i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG); } vsi = i40e_vsi_setup(pf, I40E_VSI_VMDQ2, vsi_seid, 0); @@ -871,6 +1189,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "del relay", 9) == 0) { int i; + cnt = sscanf(&cmd_buf[9], "%i", &veb_seid); if (cnt != 1) { dev_info(&pf->pdev->dev, @@ -898,9 +1217,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "add pvid", 8) == 0) { i40e_status ret; u16 vid; - unsigned int v; + int v; - cnt = sscanf(&cmd_buf[8], "%i %u", &vsi_seid, &v); + cnt = sscanf(&cmd_buf[8], "%i %d", &vsi_seid, &v); if (cnt != 2) { dev_info(&pf->pdev->dev, "add pvid: bad command string, cnt=%d\n", cnt); @@ -914,7 +1233,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, goto command_write_done; } - vid = v; + vid = (unsigned)v; ret = i40e_vsi_add_pvid(vsi, vid); if (!ret) dev_info(&pf->pdev->dev, @@ -949,6 +1268,10 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "dump", 4) == 0) { if (strncmp(&cmd_buf[5], "switch", 6) == 0) { i40e_fetch_switch_configuration(pf, true); + } else if (strncmp(&cmd_buf[5], "resources", 9) == 0) { + i40e_dbg_dump_resources(pf); + } else if (strncmp(&cmd_buf[5], "capabilities", 7) == 0) { + i40e_dbg_dump_capabilities(pf); } else if (strncmp(&cmd_buf[5], "vsi", 3) == 0) { cnt = sscanf(&cmd_buf[8], "%i", &vsi_seid); if (cnt > 0) @@ -969,6 +1292,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, i40e_dbg_dump_vf_all(pf); } else if (strncmp(&cmd_buf[5], "desc", 4) == 0) { int ring_id, desc_n; + if (strncmp(&cmd_buf[10], "rx", 2) == 0) { cnt = sscanf(&cmd_buf[12], "%i %i %i", &vsi_seid, &ring_id, &desc_n); @@ -1007,6 +1331,8 @@ static ssize_t i40e_dbg_command_write(struct file *filp, &pf->hw.local_dcbx_config; struct i40e_dcbx_config *r_cfg = &pf->hw.remote_dcbx_config; + struct i40e_dcbx_config *d_cfg = + &pf->hw.desired_dcbx_config; int i, ret; u16 switch_id; @@ -1049,68 +1375,18 @@ static ssize_t i40e_dbg_command_write(struct file *filp, kfree(bw_data); bw_data = NULL; - dev_info(&pf->pdev->dev, - "port dcbx_mode=%d\n", cfg->dcbx_mode); - dev_info(&pf->pdev->dev, - "port ets_cfg: willing=%d cbs=%d, maxtcs=%d\n", - cfg->etscfg.willing, cfg->etscfg.cbs, - cfg->etscfg.maxtcs); - for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - dev_info(&pf->pdev->dev, "port ets_cfg: %d prio_tc=%d tcbw=%d tctsa=%d\n", - i, cfg->etscfg.prioritytable[i], - cfg->etscfg.tcbwtable[i], - cfg->etscfg.tsatable[i]); - } - for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - dev_info(&pf->pdev->dev, "port ets_rec: %d prio_tc=%d tcbw=%d tctsa=%d\n", - i, cfg->etsrec.prioritytable[i], - cfg->etsrec.tcbwtable[i], - cfg->etsrec.tsatable[i]); - } - dev_info(&pf->pdev->dev, - "port pfc_cfg: willing=%d mbc=%d, pfccap=%d pfcenable=0x%x\n", - cfg->pfc.willing, cfg->pfc.mbc, - cfg->pfc.pfccap, cfg->pfc.pfcenable); - dev_info(&pf->pdev->dev, - "port app_table: num_apps=%d\n", cfg->numapps); - for (i = 0; i < cfg->numapps; i++) { - dev_info(&pf->pdev->dev, "port app_table: %d prio=%d selector=%d protocol=0x%x\n", - i, cfg->app[i].priority, - cfg->app[i].selector, - cfg->app[i].protocolid); - } - /* Peer TLV DCBX data */ - dev_info(&pf->pdev->dev, - "remote port ets_cfg: willing=%d cbs=%d, maxtcs=%d\n", - r_cfg->etscfg.willing, - r_cfg->etscfg.cbs, r_cfg->etscfg.maxtcs); - for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - dev_info(&pf->pdev->dev, "remote port ets_cfg: %d prio_tc=%d tcbw=%d tctsa=%d\n", - i, r_cfg->etscfg.prioritytable[i], - r_cfg->etscfg.tcbwtable[i], - r_cfg->etscfg.tsatable[i]); - } - for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - dev_info(&pf->pdev->dev, "remote port ets_rec: %d prio_tc=%d tcbw=%d tctsa=%d\n", - i, r_cfg->etsrec.prioritytable[i], - r_cfg->etsrec.tcbwtable[i], - r_cfg->etsrec.tsatable[i]); - } - dev_info(&pf->pdev->dev, - "remote port pfc_cfg: willing=%d mbc=%d, pfccap=%d pfcenable=0x%x\n", - r_cfg->pfc.willing, - r_cfg->pfc.mbc, - r_cfg->pfc.pfccap, - r_cfg->pfc.pfcenable); - dev_info(&pf->pdev->dev, - "remote port app_table: num_apps=%d\n", - r_cfg->numapps); - for (i = 0; i < r_cfg->numapps; i++) { - dev_info(&pf->pdev->dev, "remote port app_table: %d prio=%d selector=%d protocol=0x%x\n", - i, r_cfg->app[i].priority, - r_cfg->app[i].selector, - r_cfg->app[i].protocolid); + if (cfg->dcbx_mode == I40E_DCBX_MODE_CEE) { + dev_info(&pf->pdev->dev, + "CEE DCBX mode with Oper TLV Status = 0x%x\n", + cfg->tlv_status); + i40e_dbg_dump_dcb_cfg(pf, d_cfg, "DesiredCfg"); + } else { + dev_info(&pf->pdev->dev, "IEEE DCBX mode\n"); } + + i40e_dbg_dump_dcb_cfg(pf, cfg, "OperCfg"); + i40e_dbg_dump_dcb_cfg(pf, r_cfg, "PeerCfg"); + } else if (strncmp(&cmd_buf[5], "debug fwdata", 12) == 0) { int cluster_id, table_id; int index, ret; @@ -1155,73 +1431,65 @@ static ssize_t i40e_dbg_command_write(struct file *filp, buff, rlen, true); kfree(buff); buff = NULL; + } else if (strncmp(&cmd_buf[5], "filters", 7) == 0) { + struct i40e_fdir_filter *f_rule; + struct i40e_cloud_filter *c_rule; + struct hlist_node *node2; + + hlist_for_each_entry_safe(f_rule, node2, + &pf->fdir_filter_list, + fdir_node) { + i40e_dbg_dump_fdir_filter(pf, f_rule); + } + + /* find the cloud filter rule ids */ + hlist_for_each_entry_safe(c_rule, node2, + &pf->cloud_filter_list, + cloud_node) { + i40e_dbg_dump_cloud_filter(pf, c_rule); + } + i40e_dbg_dump_all_vsi_filters(pf); } else { dev_info(&pf->pdev->dev, "dump desc tx [], dump desc rx [],\n"); dev_info(&pf->pdev->dev, "dump switch\n"); dev_info(&pf->pdev->dev, "dump vsi [seid]\n"); + dev_info(&pf->pdev->dev, "dump capabilities\n"); + dev_info(&pf->pdev->dev, "dump resources\n"); dev_info(&pf->pdev->dev, "dump reset stats\n"); dev_info(&pf->pdev->dev, "dump port\n"); - dev_info(&pf->pdev->dev, "dump vf [vf_id]\n"); + dev_info(&pf->pdev->dev, "dump VF [vf_id]\n"); dev_info(&pf->pdev->dev, "dump debug fwdata \n"); + dev_info(&pf->pdev->dev, "dump filters\n"); } - } else if (strncmp(cmd_buf, "pfr", 3) == 0) { - dev_info(&pf->pdev->dev, "debugfs: forcing PFR\n"); - i40e_do_reset_safe(pf, BIT(__I40E_PF_RESET_REQUESTED)); - - } else if (strncmp(cmd_buf, "corer", 5) == 0) { - dev_info(&pf->pdev->dev, "debugfs: forcing CoreR\n"); - i40e_do_reset_safe(pf, BIT(__I40E_CORE_RESET_REQUESTED)); - } else if (strncmp(cmd_buf, "globr", 5) == 0) { - dev_info(&pf->pdev->dev, "debugfs: forcing GlobR\n"); - i40e_do_reset_safe(pf, BIT(__I40E_GLOBAL_RESET_REQUESTED)); + } else if (strncmp(cmd_buf, "msg_enable", 10) == 0) { + u32 level; - } else if (strncmp(cmd_buf, "empr", 4) == 0) { - dev_info(&pf->pdev->dev, "debugfs: forcing EMPR\n"); - i40e_do_reset_safe(pf, BIT(__I40E_EMP_RESET_REQUESTED)); - - } else if (strncmp(cmd_buf, "read", 4) == 0) { - u32 address; - u32 value; - - cnt = sscanf(&cmd_buf[4], "%i", &address); - if (cnt != 1) { - dev_info(&pf->pdev->dev, "read \n"); - goto command_write_done; - } - - /* check the range on address */ - if (address > (pf->ioremap_len - sizeof(u32))) { - dev_info(&pf->pdev->dev, "read reg address 0x%08x too large, max=0x%08lx\n", - address, (unsigned long int)(pf->ioremap_len - sizeof(u32))); - goto command_write_done; - } - - value = rd32(&pf->hw, address); - dev_info(&pf->pdev->dev, "read: 0x%08x = 0x%08x\n", - address, value); - - } else if (strncmp(cmd_buf, "write", 5) == 0) { - u32 address, value; - - cnt = sscanf(&cmd_buf[5], "%i %i", &address, &value); - if (cnt != 2) { - dev_info(&pf->pdev->dev, "write \n"); - goto command_write_done; - } - - /* check the range on address */ - if (address > (pf->ioremap_len - sizeof(u32))) { - dev_info(&pf->pdev->dev, "write reg address 0x%08x too large, max=0x%08lx\n", - address, (unsigned long int)(pf->ioremap_len - sizeof(u32))); - goto command_write_done; + cnt = sscanf(&cmd_buf[10], "%i", &level); + if (cnt) { + if (I40E_DEBUG_USER & level) { + pf->hw.debug_mask = level; + dev_info(&pf->pdev->dev, + "set hw.debug_mask = 0x%08x\n", + pf->hw.debug_mask); + } + pf->msg_enable = level; + dev_info(&pf->pdev->dev, "set msg_enable = 0x%08x\n", + pf->msg_enable); + } else { + dev_info(&pf->pdev->dev, "msg_enable = 0x%08x\n", + pf->msg_enable); } - wr32(&pf->hw, address, value); - value = rd32(&pf->hw, address); - dev_info(&pf->pdev->dev, "write: 0x%08x = 0x%08x\n", - address, value); + } else if (strncmp(cmd_buf, "defport on", 10) == 0) { + dev_info(&pf->pdev->dev, "debugfs: forcing PFR with defport enabled\n"); + pf->cur_promisc = true; + i40e_do_reset_safe(pf, BIT(__I40E_PF_RESET_REQUESTED)); + } else if (strncmp(cmd_buf, "defport off", 11) == 0) { + dev_info(&pf->pdev->dev, "debugfs: forcing PFR with defport disabled\n"); + pf->cur_promisc = false; + i40e_do_reset_safe(pf, BIT(__I40E_PF_RESET_REQUESTED)); } else if (strncmp(cmd_buf, "clear_stats", 11) == 0) { if (strncmp(&cmd_buf[12], "vsi", 3) == 0) { cnt = sscanf(&cmd_buf[15], "%i", &vsi_seid); @@ -1260,7 +1528,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, struct i40e_aq_desc *desc; i40e_status ret; - desc = kzalloc(sizeof(struct i40e_aq_desc), GFP_KERNEL); + desc = kzalloc(sizeof(*desc), GFP_KERNEL); if (!desc) goto command_write_done; cnt = sscanf(&cmd_buf[11], @@ -1308,7 +1576,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, u16 buffer_len; u8 *buff; - desc = kzalloc(sizeof(struct i40e_aq_desc), GFP_KERNEL); + desc = kzalloc(sizeof(*desc), GFP_KERNEL); if (!desc) goto command_write_done; cnt = sscanf(&cmd_buf[20], @@ -1339,7 +1607,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, desc = NULL; goto command_write_done; } - desc->flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + desc->flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_BUF); ret = i40e_asq_send_command(&pf->hw, desc, buff, buffer_len, NULL); if (!ret) { @@ -1371,11 +1639,78 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "fd current cnt", 14) == 0) { dev_info(&pf->pdev->dev, "FD current total filter count for this interface: %d\n", i40e_get_current_fd_count(pf)); + /* vf base mode on/off hooks needs to be used by validation only to + * make sure vf base mode driver is not broken + */ + } else if (strncmp(cmd_buf, "vf base mode on", 15) == 0) { + if (!pf->num_alloc_vfs) { + pf->vf_base_mode_only = true; + dev_info(&pf->pdev->dev, "VF Base mode is enabled\n"); + } else + dev_info(&pf->pdev->dev, + "cannot configure VF Base mode when VFs are allocated\n"); + } else if (strncmp(cmd_buf, "vf base mode off", 16) == 0) { + if (!pf->num_alloc_vfs) { + pf->vf_base_mode_only = false; + dev_info(&pf->pdev->dev, "VF Base mode is disabled\n"); + } else + dev_info(&pf->pdev->dev, + "cannot configure VF Base mode when VFs are allocated\n"); + } else if ((strncmp(cmd_buf, "add ethtype filter", 18) == 0) || + (strncmp(cmd_buf, "rem ethtype filter", 18) == 0)) { + u16 ethtype; + u16 queue; + bool add = false; + int ret; + + if (strncmp(cmd_buf, "add", 3) == 0) + add = true; + + cnt = sscanf(&cmd_buf[18], + "%hi %hi", + ðtype, &queue); + if (cnt != 2) { + dev_info(&pf->pdev->dev, + "%s ethtype filter: bad command string, cnt=%d\n", + add ? "add" : "rem", + cnt); + goto command_write_done; + } + ret = i40e_aq_add_rem_control_packet_filter(&pf->hw, + pf->hw.mac.addr, + ethtype, 0, + pf->vsi[pf->lan_vsi]->seid, + queue, add, NULL, NULL); + if (ret) { + dev_info(&pf->pdev->dev, + "%s: add/rem Control Packet Filter AQ command failed =0x%x\n", + add ? "add" : "rem", + pf->hw.aq.asq_last_status); + goto command_write_done; + } + + } else if (strncmp(cmd_buf, "dcb off", 7) == 0) { + u8 tc = i40e_pf_get_num_tc(pf); + /* Allow disabling only when in single TC mode */ + if (tc > 1) { + dev_info(&pf->pdev->dev, "Failed to disable DCB as TC count(%d) is greater than 1.\n", + tc); + goto command_write_done; + } + pf->flags &= ~I40E_FLAG_DCB_ENABLED; + } else if (strncmp(cmd_buf, "dcb on", 6) == 0) { + pf->flags |= I40E_FLAG_DCB_ENABLED; } else if (strncmp(cmd_buf, "lldp", 4) == 0) { if (strncmp(&cmd_buf[5], "stop", 4) == 0) { int ret; - ret = i40e_aq_stop_lldp(&pf->hw, false, NULL); + if (pf->hw.flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE) { + dev_info(&pf->pdev->dev, + "Use ethtool to disable LLDP firmware agent:"\ + "\"ethtool --set-priv-flags disable-fw-lldp on\".\n"); + goto command_write_done; + } + ret = i40e_aq_stop_lldp(&pf->hw, false, false, NULL); if (ret) { dev_info(&pf->pdev->dev, "Stop LLDP AQ command failed =0x%x\n", @@ -1389,17 +1724,25 @@ static ssize_t i40e_dbg_command_write(struct file *filp, 0, true, NULL, NULL); if (ret) { dev_info(&pf->pdev->dev, - "%s: Add Control Packet Filter AQ command failed =0x%x\n", - __func__, pf->hw.aq.asq_last_status); + "%s: Add Control Packet Filter AQ command failed =0x%x\n", + __func__, pf->hw.aq.asq_last_status); goto command_write_done; } -#ifdef CONFIG_I40E_DCB +#ifdef CONFIG_DCB +#ifdef HAVE_DCBNL_IEEE pf->dcbx_cap = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE; -#endif /* CONFIG_I40E_DCB */ +#endif /* HAVE_DCBNL_IEEE */ +#endif /* CONFIG_DCB */ } else if (strncmp(&cmd_buf[5], "start", 5) == 0) { int ret; + if (pf->hw.flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE) { + dev_info(&pf->pdev->dev, + "Use ethtool to enable LLDP firmware agent:"\ + "\"ethtool --set-priv-flags disable-fw-lldp off\".\n"); + goto command_write_done; + } ret = i40e_aq_add_rem_control_packet_filter(&pf->hw, pf->hw.mac.addr, I40E_ETH_P_LLDP, 0, @@ -1407,22 +1750,23 @@ static ssize_t i40e_dbg_command_write(struct file *filp, 0, false, NULL, NULL); if (ret) { dev_info(&pf->pdev->dev, - "%s: Remove Control Packet Filter AQ command failed =0x%x\n", - __func__, pf->hw.aq.asq_last_status); + "%s: Remove Control Packet Filter AQ command failed =0x%x\n", + __func__, pf->hw.aq.asq_last_status); /* Continue and start FW LLDP anyways */ } - - ret = i40e_aq_start_lldp(&pf->hw, NULL); + ret = i40e_aq_start_lldp(&pf->hw, false, NULL); if (ret) { dev_info(&pf->pdev->dev, "Start LLDP AQ command failed =0x%x\n", pf->hw.aq.asq_last_status); goto command_write_done; } -#ifdef CONFIG_I40E_DCB +#ifdef CONFIG_DCB +#ifdef HAVE_DCBNL_IEEE pf->dcbx_cap = DCB_CAP_DCBX_LLD_MANAGED | DCB_CAP_DCBX_VER_IEEE; -#endif /* CONFIG_I40E_DCB */ +#endif /* HAVE_DCBNL_IEEE */ +#endif /* CONFIG_DCB */ } else if (strncmp(&cmd_buf[5], "get local", 9) == 0) { u16 llen, rlen; @@ -1564,6 +1908,251 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } kfree(buff); buff = NULL; + } else if (strncmp(cmd_buf, "set rss_size", 12) == 0) { + int q_count; + + cnt = sscanf(&cmd_buf[12], "%i", &q_count); + if (cnt != 1) { + dev_info(&pf->pdev->dev, + "set rss_size: bad command string, cnt=%d\n", cnt); + goto command_write_done; + } + if (q_count <= 0) { + dev_info(&pf->pdev->dev, + "set rss_size: %d is too small\n", + q_count); + goto command_write_done; + } + dev_info(&pf->pdev->dev, + "set rss_size requesting %d queues\n", q_count); + rtnl_lock(); + i40e_reconfig_rss_queues(pf, q_count); + rtnl_unlock(); + dev_info(&pf->pdev->dev, "new rss_size %d\n", + pf->alloc_rss_size); + } else if (strncmp(cmd_buf, "get bw", 6) == 0) { + i40e_status status; + u32 max_bw, min_bw; + bool min_valid, max_valid; + + status = i40e_read_bw_from_alt_ram(&pf->hw, &max_bw, &min_bw, + &min_valid, &max_valid); + + if (status) { + dev_info(&pf->pdev->dev, "get bw failed with status %d\n", + status); + goto command_write_done; + } + if (!min_valid) { + dev_info(&pf->pdev->dev, "min bw invalid\n"); + } else if (min_bw & I40E_ALT_BW_RELATIVE_MASK) { + dev_info(&pf->pdev->dev, "relative min bw = %d%%\n", + min_bw & I40E_ALT_BW_VALUE_MASK); + } else { + dev_info(&pf->pdev->dev, "absolute min bw = %dMb/s\n", + (min_bw & I40E_ALT_BW_VALUE_MASK)*128); + } + if (!max_valid) { + dev_info(&pf->pdev->dev, "max bw invalid\n"); + } else if (max_bw & I40E_ALT_BW_RELATIVE_MASK) { + dev_info(&pf->pdev->dev, "relative max bw = %d%%\n", + max_bw & I40E_ALT_BW_VALUE_MASK); + } else { + dev_info(&pf->pdev->dev, "absolute max bw = %dMb/s\n", + (max_bw & I40E_ALT_BW_VALUE_MASK)*128); + } + } else if (strncmp(cmd_buf, "set bw", 6) == 0) { + struct i40e_aqc_configure_partition_bw_data bw_data; + i40e_status status; + u32 max_bw, min_bw; + + /* Set the valid bit for this PF */ + bw_data.pf_valid_bits = cpu_to_le16(BIT(pf->hw.pf_id)); + + /* Get the bw's */ + cnt = sscanf(&cmd_buf[7], "%u %u", &max_bw, &min_bw); + if (cnt != 2) { + dev_info(&pf->pdev->dev,"set bw \n"); + goto command_write_done; + } + bw_data.max_bw[pf->hw.pf_id] = max_bw; + bw_data.min_bw[pf->hw.pf_id] = min_bw; + + /* Set the new bandwidths */ + status = i40e_aq_configure_partition_bw(&pf->hw, &bw_data, NULL); + if (status) { + dev_info(&pf->pdev->dev, "configure partition bw failed with status %d\n", + status); + goto command_write_done; + } + } else if (strncmp(cmd_buf, "commit bw", 9) == 0) { + /* Commit temporary BW setting to permanent NVM image */ + enum i40e_admin_queue_err last_aq_status; + i40e_status aq_status; + u16 nvm_word; + + if (pf->hw.partition_id != 1) { + dev_info(&pf->pdev->dev, + "Commit BW only works on first partition!\n"); + goto command_write_done; + } + + /* Acquire NVM for read access */ + aq_status = i40e_acquire_nvm(&pf->hw, I40E_RESOURCE_READ); + if (aq_status) { + dev_info(&pf->pdev->dev, + "Error %d: Cannot acquire NVM for Read Access\n", + aq_status); + goto command_write_done; + } + + /* Read word 0x10 of NVM - SW compatibility word 1 */ + aq_status = i40e_aq_read_nvm(&pf->hw, + I40E_SR_NVM_CONTROL_WORD, + 0x10, sizeof(nvm_word), &nvm_word, + false, NULL); + /* Save off last admin queue command status before releasing + * the NVM + */ + last_aq_status = pf->hw.aq.asq_last_status; + i40e_release_nvm(&pf->hw); + if (aq_status) { + dev_info(&pf->pdev->dev, "NVM read error %d:%d\n", + aq_status, last_aq_status); + goto command_write_done; + } + + /* Wait a bit for NVM release to complete */ + msleep(100); + + /* Acquire NVM for write access */ + aq_status = i40e_acquire_nvm(&pf->hw, I40E_RESOURCE_WRITE); + if (aq_status) { + dev_info(&pf->pdev->dev, + "Error %d: Cannot acquire NVM for Write Access\n", + aq_status); + goto command_write_done; + } + /* Write it back out unchanged to initiate update NVM, + * which will force a write of the shadow (alt) RAM to + * the NVM - thus storing the bandwidth values permanently. + */ + aq_status = i40e_aq_update_nvm(&pf->hw, + I40E_SR_NVM_CONTROL_WORD, + 0x10, sizeof(nvm_word), + &nvm_word, true, 0, NULL); + /* Save off last admin queue command status before releasing + * the NVM + */ + last_aq_status = pf->hw.aq.asq_last_status; + i40e_release_nvm(&pf->hw); + if (aq_status) + dev_info(&pf->pdev->dev, + "BW settings NOT SAVED - error %d:%d updating NVM\n", + aq_status, last_aq_status); + } else if (strncmp(cmd_buf, "add switch ingress mirror", 25) == 0) { + u16 rule_type = I40E_AQC_MIRROR_RULE_TYPE_ALL_INGRESS; + u16 switch_seid, dst_vsi_seid, rule_id; + i40e_status aq_status; + + cnt = sscanf(&cmd_buf[25], "%hu %hu", + &switch_seid, &dst_vsi_seid); + if (cnt != 2) { + dev_info(&pf->pdev->dev, + "add mirror: bad command string, cnt=%d\n", + cnt); + goto command_write_done; + } + + aq_status = + i40e_aq_add_mirrorrule(&pf->hw, + switch_seid, rule_type, + dst_vsi_seid, 0, NULL, NULL, + &rule_id, NULL, NULL); + if (aq_status) + dev_info(&pf->pdev->dev, + "add ingress mirror failed with status %d\n", + aq_status); + else + dev_info(&pf->pdev->dev, + "Ingress mirror rule %d added\n", rule_id); + } else if (strncmp(cmd_buf, "add switch egress mirror", 24) == 0) { + u16 rule_type = I40E_AQC_MIRROR_RULE_TYPE_ALL_EGRESS; + u16 switch_seid, dst_vsi_seid, rule_id; + i40e_status aq_status; + + cnt = sscanf(&cmd_buf[24], "%hu %hu", + &switch_seid, &dst_vsi_seid); + if (cnt != 2) { + dev_info(&pf->pdev->dev, + "add mirror: bad command string, cnt=%d\n", + cnt); + goto command_write_done; + } + + aq_status = + i40e_aq_add_mirrorrule(&pf->hw, + switch_seid, rule_type, + dst_vsi_seid, 0, NULL, NULL, + &rule_id, NULL, NULL); + if (aq_status) + dev_info(&pf->pdev->dev, + "add egress mirror failed with status %d\n", + aq_status); + else + dev_info(&pf->pdev->dev, + "Egress mirror rule %d added\n", rule_id); + } else if (strncmp(cmd_buf, "del switch ingress mirror", 25) == 0) { + u16 rule_type = I40E_AQC_MIRROR_RULE_TYPE_ALL_INGRESS; + i40e_status aq_status; + u16 switch_seid, rule_id; + + cnt = sscanf(&cmd_buf[25], "%hu %hu", + &switch_seid, &rule_id); + if (cnt != 2) { + dev_info(&pf->pdev->dev, + "del mirror: bad command string, cnt=%d\n", + cnt); + goto command_write_done; + } + + aq_status = + i40e_aq_delete_mirrorrule(&pf->hw, switch_seid, + rule_type, rule_id, 0, NULL, + NULL, NULL, NULL); + if (aq_status) + dev_info(&pf->pdev->dev, + "mirror rule remove failed with status %d\n", + aq_status); + else + dev_info(&pf->pdev->dev, + "Mirror rule %d removed\n", rule_id); + } else if (strncmp(cmd_buf, "del switch egress mirror", 24) == 0) { + u16 rule_type = I40E_AQC_MIRROR_RULE_TYPE_ALL_EGRESS; + i40e_status aq_status; + u16 switch_seid, rule_id; + + cnt = sscanf(&cmd_buf[24], "%hu %hu", + &switch_seid, &rule_id); + if (cnt != 2) { + dev_info(&pf->pdev->dev, + "del mirror: bad command string, cnt=%d\n", + cnt); + goto command_write_done; + } + + aq_status = + i40e_aq_delete_mirrorrule(&pf->hw, switch_seid, + rule_type, rule_id, 0, NULL, + NULL, NULL, NULL); + if (aq_status) + dev_info(&pf->pdev->dev, + "mirror rule remove failed with status %d\n", + aq_status); + else + dev_info(&pf->pdev->dev, + "Mirror rule %d removed\n", rule_id); + } else { dev_info(&pf->pdev->dev, "unknown command '%s'\n", cmd_buf); dev_info(&pf->pdev->dev, "available commands\n"); @@ -1575,21 +2164,27 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, " del pvid \n"); dev_info(&pf->pdev->dev, " dump switch\n"); dev_info(&pf->pdev->dev, " dump vsi [seid]\n"); + dev_info(&pf->pdev->dev, " dump capabilities\n"); + dev_info(&pf->pdev->dev, " dump resources\n"); dev_info(&pf->pdev->dev, " dump desc tx []\n"); dev_info(&pf->pdev->dev, " dump desc rx []\n"); dev_info(&pf->pdev->dev, " dump desc aq\n"); dev_info(&pf->pdev->dev, " dump reset stats\n"); dev_info(&pf->pdev->dev, " dump debug fwdata \n"); + dev_info(&pf->pdev->dev, " msg_enable [level]\n"); dev_info(&pf->pdev->dev, " read \n"); dev_info(&pf->pdev->dev, " write \n"); dev_info(&pf->pdev->dev, " clear_stats vsi [seid]\n"); dev_info(&pf->pdev->dev, " clear_stats port\n"); - dev_info(&pf->pdev->dev, " pfr\n"); - dev_info(&pf->pdev->dev, " corer\n"); - dev_info(&pf->pdev->dev, " globr\n"); + dev_info(&pf->pdev->dev, " defport on\n"); + dev_info(&pf->pdev->dev, " defport off\n"); dev_info(&pf->pdev->dev, " send aq_cmd \n"); dev_info(&pf->pdev->dev, " send indirect aq_cmd \n"); dev_info(&pf->pdev->dev, " fd current cnt"); + dev_info(&pf->pdev->dev, " vf base mode on\n"); + dev_info(&pf->pdev->dev, " vf base mode off\n"); + dev_info(&pf->pdev->dev, " add ethtype filter "); + dev_info(&pf->pdev->dev, " rem ethtype filter "); dev_info(&pf->pdev->dev, " lldp start\n"); dev_info(&pf->pdev->dev, " lldp stop\n"); dev_info(&pf->pdev->dev, " lldp get local\n"); @@ -1597,6 +2192,16 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, " lldp event on\n"); dev_info(&pf->pdev->dev, " lldp event off\n"); dev_info(&pf->pdev->dev, " nvm read [module] [word_offset] [word_count]\n"); + dev_info(&pf->pdev->dev, " set rss_size \n"); + dev_info(&pf->pdev->dev, " dcb off\n"); + dev_info(&pf->pdev->dev, " dcb on\n"); + dev_info(&pf->pdev->dev, " get bw\n"); + dev_info(&pf->pdev->dev, " set bw \n"); + dev_info(&pf->pdev->dev, " commit bw\n"); + dev_info(&pf->pdev->dev, " add switch ingress mirror \n"); + dev_info(&pf->pdev->dev, " add switch egress mirror \n"); + dev_info(&pf->pdev->dev, " del switch ingress mirror \n"); + dev_info(&pf->pdev->dev, " del switch egress mirror \n"); } command_write_done: @@ -1631,7 +2236,7 @@ static ssize_t i40e_dbg_netdev_ops_read(struct file *filp, char __user *buffer, { struct i40e_pf *pf = filp->private_data; int bytes_not_copied; - int buf_size = 256; + size_t buf_size = 256; char *buf; int len; @@ -1696,30 +2301,7 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, count = buf_tmp - i40e_dbg_netdev_ops_buf + 1; } - if (strncmp(i40e_dbg_netdev_ops_buf, "tx_timeout", 10) == 0) { - cnt = sscanf(&i40e_dbg_netdev_ops_buf[11], "%i", &vsi_seid); - if (cnt != 1) { - dev_info(&pf->pdev->dev, "tx_timeout \n"); - goto netdev_ops_write_done; - } - vsi = i40e_dbg_find_vsi(pf, vsi_seid); - if (!vsi) { - dev_info(&pf->pdev->dev, - "tx_timeout: VSI %d not found\n", vsi_seid); - } else if (!vsi->netdev) { - dev_info(&pf->pdev->dev, "tx_timeout: no netdev for VSI %d\n", - vsi_seid); - } else if (test_bit(__I40E_VSI_DOWN, vsi->state)) { - dev_info(&pf->pdev->dev, "tx_timeout: VSI %d not UP\n", - vsi_seid); - } else if (rtnl_trylock()) { - vsi->netdev->netdev_ops->ndo_tx_timeout(vsi->netdev); - rtnl_unlock(); - dev_info(&pf->pdev->dev, "tx_timeout called\n"); - } else { - dev_info(&pf->pdev->dev, "Could not acquire RTNL - please try again\n"); - } - } else if (strncmp(i40e_dbg_netdev_ops_buf, "change_mtu", 10) == 0) { + if (strncmp(i40e_dbg_netdev_ops_buf, "change_mtu", 10) == 0) { int mtu; cnt = sscanf(&i40e_dbg_netdev_ops_buf[11], "%i %i", @@ -1736,8 +2318,13 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, dev_info(&pf->pdev->dev, "change_mtu: no netdev for VSI %d\n", vsi_seid); } else if (rtnl_trylock()) { +#ifdef HAVE_RHEL7_EXTENDED_MIN_MAX_MTU + vsi->netdev->netdev_ops->extended.ndo_change_mtu( + vsi->netdev, mtu); +#else vsi->netdev->netdev_ops->ndo_change_mtu(vsi->netdev, mtu); +#endif rtnl_unlock(); dev_info(&pf->pdev->dev, "change_mtu called\n"); } else { @@ -1783,14 +2370,33 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, napi_schedule(&vsi->q_vectors[i]->napi); dev_info(&pf->pdev->dev, "napi called\n"); } + } else if (strncmp(i40e_dbg_netdev_ops_buf, + "toggle_tx_timeout", 17) == 0) { + cnt = sscanf(&i40e_dbg_netdev_ops_buf[17], "%i", &vsi_seid); + if (cnt != 1) { + dev_info(&pf->pdev->dev, "toggle_tx_timeout \n"); + goto netdev_ops_write_done; + } + vsi = i40e_dbg_find_vsi(pf, vsi_seid); + if (!vsi) { + dev_info(&pf->pdev->dev, "toggle_tx_timeout: VSI %d not found\n", + vsi_seid); + } else { + if (vsi->block_tx_timeout) + vsi->block_tx_timeout = false; + else + vsi->block_tx_timeout = true; + dev_info(&pf->pdev->dev, "toggle_tx_timeout: block_tx_timeout = %d\n", + vsi->block_tx_timeout); + } } else { dev_info(&pf->pdev->dev, "unknown command '%s'\n", i40e_dbg_netdev_ops_buf); dev_info(&pf->pdev->dev, "available commands\n"); - dev_info(&pf->pdev->dev, " tx_timeout \n"); dev_info(&pf->pdev->dev, " change_mtu \n"); dev_info(&pf->pdev->dev, " set_rx_mode \n"); dev_info(&pf->pdev->dev, " napi \n"); + dev_info(&pf->pdev->dev, " toggle_tx_timeout \n"); } netdev_ops_write_done: return count; diff --git a/drivers/net/ethernet/intel/i40e/i40e_devids.h b/drivers/net/ethernet/intel/i40e/i40e_devids.h index 8e46098ba..55a7c1079 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_devids.h +++ b/drivers/net/ethernet/intel/i40e/i40e_devids.h @@ -1,33 +1,11 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2015 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ -#ifndef _I40E_DEVIDS_H_ #define _I40E_DEVIDS_H_ /* Device IDs */ +#define I40E_DEV_ID_X710_N3000 0x0CF8 +#define I40E_DEV_ID_XXV710_N3000 0x0D58 #define I40E_DEV_ID_SFP_XL710 0x1572 #define I40E_DEV_ID_QEMU 0x1574 #define I40E_DEV_ID_KX_B 0x1580 @@ -41,6 +19,13 @@ #define I40E_DEV_ID_10G_BASE_T4 0x1589 #define I40E_DEV_ID_25G_B 0x158A #define I40E_DEV_ID_25G_SFP28 0x158B +#define I40E_DEV_ID_10G_BASE_T_BC 0x15FF +#define I40E_DEV_ID_10G_B 0x104F +#define I40E_DEV_ID_10G_SFP 0x104E +#define I40E_DEV_ID_5G_BASE_T_BC 0x101F +#define I40E_IS_X710TL_DEVICE(d) \ + (((d) == I40E_DEV_ID_10G_BASE_T_BC) || \ + ((d) == I40E_DEV_ID_5G_BASE_T_BC)) #define I40E_DEV_ID_KX_X722 0x37CE #define I40E_DEV_ID_QSFP_X722 0x37CF #define I40E_DEV_ID_SFP_X722 0x37D0 @@ -52,4 +37,6 @@ (d) == I40E_DEV_ID_QSFP_B || \ (d) == I40E_DEV_ID_QSFP_C) -#endif /* _I40E_DEVIDS_H_ */ +#define i40e_is_25G_device(d) ((d) == I40E_DEV_ID_25G_B || \ + (d) == I40E_DEV_ID_25G_SFP28) + diff --git a/drivers/net/ethernet/intel/i40e/i40e_diag.c b/drivers/net/ethernet/intel/i40e/i40e_diag.c index f141e78d4..dc2e9d6b4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_diag.c +++ b/drivers/net/ethernet/intel/i40e/i40e_diag.c @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #include "i40e_diag.h" #include "i40e_prototype.h" @@ -46,9 +23,11 @@ static i40e_status i40e_diag_reg_pattern_test(struct i40e_hw *hw, wr32(hw, reg, (pat & mask)); val = rd32(hw, reg); if ((val & mask) != (pat & mask)) { +#ifdef ETHTOOL_TEST i40e_debug(hw, I40E_DEBUG_DIAG, "%s: reg pattern test failed - reg 0x%08x pat 0x%08x val 0x%08x\n", __func__, reg, pat, val); +#endif return I40E_ERR_DIAG_TEST_FAILED; } } @@ -56,35 +35,29 @@ static i40e_status i40e_diag_reg_pattern_test(struct i40e_hw *hw, wr32(hw, reg, orig_val); val = rd32(hw, reg); if (val != orig_val) { +#ifdef ETHTOOL_TEST i40e_debug(hw, I40E_DEBUG_DIAG, "%s: reg restore test failed - reg 0x%08x orig_val 0x%08x val 0x%08x\n", __func__, reg, orig_val, val); +#endif return I40E_ERR_DIAG_TEST_FAILED; } - return 0; + return I40E_SUCCESS; } struct i40e_diag_reg_test_info i40e_reg_list[] = { /* offset mask elements stride */ - {I40E_QTX_CTL(0), 0x0000FFBF, 1, - I40E_QTX_CTL(1) - I40E_QTX_CTL(0)}, - {I40E_PFINT_ITR0(0), 0x00000FFF, 3, - I40E_PFINT_ITR0(1) - I40E_PFINT_ITR0(0)}, - {I40E_PFINT_ITRN(0, 0), 0x00000FFF, 1, - I40E_PFINT_ITRN(0, 1) - I40E_PFINT_ITRN(0, 0)}, - {I40E_PFINT_ITRN(1, 0), 0x00000FFF, 1, - I40E_PFINT_ITRN(1, 1) - I40E_PFINT_ITRN(1, 0)}, - {I40E_PFINT_ITRN(2, 0), 0x00000FFF, 1, - I40E_PFINT_ITRN(2, 1) - I40E_PFINT_ITRN(2, 0)}, + {I40E_QTX_CTL(0), 0x0000FFBF, 1, I40E_QTX_CTL(1) - I40E_QTX_CTL(0)}, + {I40E_PFINT_ITR0(0), 0x00000FFF, 3, I40E_PFINT_ITR0(1) - I40E_PFINT_ITR0(0)}, + {I40E_PFINT_ITRN(0, 0), 0x00000FFF, 1, I40E_PFINT_ITRN(0, 1) - I40E_PFINT_ITRN(0, 0)}, + {I40E_PFINT_ITRN(1, 0), 0x00000FFF, 1, I40E_PFINT_ITRN(1, 1) - I40E_PFINT_ITRN(1, 0)}, + {I40E_PFINT_ITRN(2, 0), 0x00000FFF, 1, I40E_PFINT_ITRN(2, 1) - I40E_PFINT_ITRN(2, 0)}, {I40E_PFINT_STAT_CTL0, 0x0000000C, 1, 0}, {I40E_PFINT_LNKLST0, 0x00001FFF, 1, 0}, - {I40E_PFINT_LNKLSTN(0), 0x000007FF, 1, - I40E_PFINT_LNKLSTN(1) - I40E_PFINT_LNKLSTN(0)}, - {I40E_QINT_TQCTL(0), 0x000000FF, 1, - I40E_QINT_TQCTL(1) - I40E_QINT_TQCTL(0)}, - {I40E_QINT_RQCTL(0), 0x000000FF, 1, - I40E_QINT_RQCTL(1) - I40E_QINT_RQCTL(0)}, + {I40E_PFINT_LNKLSTN(0), 0x000007FF, 1, I40E_PFINT_LNKLSTN(1) - I40E_PFINT_LNKLSTN(0)}, + {I40E_QINT_TQCTL(0), 0x000000FF, 1, I40E_QINT_TQCTL(1) - I40E_QINT_TQCTL(0)}, + {I40E_QINT_RQCTL(0), 0x000000FF, 1, I40E_QINT_RQCTL(1) - I40E_QINT_RQCTL(0)}, {I40E_PFINT_ICR0_ENA, 0xF7F20000, 1, 0}, { 0 } }; @@ -97,12 +70,12 @@ struct i40e_diag_reg_test_info i40e_reg_list[] = { **/ i40e_status i40e_diag_reg_test(struct i40e_hw *hw) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; u32 reg, mask; u32 i, j; for (i = 0; i40e_reg_list[i].offset != 0 && - !ret_code; i++) { + ret_code == I40E_SUCCESS; i++) { /* set actual reg range for dynamically allocated resources */ if (i40e_reg_list[i].offset == I40E_QTX_CTL(0) && @@ -119,9 +92,10 @@ i40e_status i40e_diag_reg_test(struct i40e_hw *hw) /* test register access */ mask = i40e_reg_list[i].mask; - for (j = 0; j < i40e_reg_list[i].elements && !ret_code; j++) { - reg = i40e_reg_list[i].offset + - (j * i40e_reg_list[i].stride); + for (j = 0; j < i40e_reg_list[i].elements && + ret_code == I40E_SUCCESS; j++) { + reg = i40e_reg_list[i].offset + + (j * i40e_reg_list[i].stride); ret_code = i40e_diag_reg_pattern_test(hw, reg, mask); } } @@ -142,7 +116,7 @@ i40e_status i40e_diag_eeprom_test(struct i40e_hw *hw) /* read NVM control word and if NVM valid, validate EEPROM checksum*/ ret_code = i40e_read_nvm_word(hw, I40E_SR_NVM_CONTROL_WORD, ®_val); - if (!ret_code && + if ((ret_code == I40E_SUCCESS) && ((reg_val & I40E_SR_CONTROL_WORD_1_MASK) == BIT(I40E_SR_CONTROL_WORD_1_SHIFT))) return i40e_validate_nvm_checksum(hw, NULL); diff --git a/drivers/net/ethernet/intel/i40e/i40e_diag.h b/drivers/net/ethernet/intel/i40e/i40e_diag.h index 0b5911652..697015b4f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_diag.h +++ b/drivers/net/ethernet/intel/i40e/i40e_diag.h @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_DIAG_H_ #define _I40E_DIAG_H_ diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 05e89864f..7f93f74d4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1,54 +1,30 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2016 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ /* ethtool support for i40e */ #include "i40e.h" #include "i40e_diag.h" -struct i40e_stats { - char stat_string[ETH_GSTRING_LEN]; - int sizeof_stat; - int stat_offset; -}; +#ifdef SIOCETHTOOL +#ifndef ETH_GSTRING_LEN +#define ETH_GSTRING_LEN 32 -#define I40E_STAT(_type, _name, _stat) { \ - .stat_string = _name, \ - .sizeof_stat = FIELD_SIZEOF(_type, _stat), \ - .stat_offset = offsetof(_type, _stat) \ -} +#endif +#ifdef ETHTOOL_GSTATS + +#include "i40e_ethtool_stats.h" -#define I40E_NETDEV_STAT(_net_stat) \ - I40E_STAT(struct rtnl_link_stats64, #_net_stat, _net_stat) #define I40E_PF_STAT(_name, _stat) \ - I40E_STAT(struct i40e_pf, _name, _stat) + I40E_STAT(struct i40e_pf, _name, _stat) #define I40E_VSI_STAT(_name, _stat) \ - I40E_STAT(struct i40e_vsi, _name, _stat) + I40E_STAT(struct i40e_vsi, _name, _stat) #define I40E_VEB_STAT(_name, _stat) \ - I40E_STAT(struct i40e_veb, _name, _stat) + I40E_STAT(struct i40e_veb, _name, _stat) +#define I40E_PFC_STAT(_name, _stat) \ + I40E_STAT(struct i40e_pfc_stats, _name, _stat) +#define I40E_QUEUE_STAT(_name, _stat) \ + I40E_STAT(struct i40e_ring, _name, _stat) static const struct i40e_stats i40e_gstrings_net_stats[] = { I40E_NETDEV_STAT(rx_packets), @@ -65,18 +41,25 @@ static const struct i40e_stats i40e_gstrings_net_stats[] = { }; static const struct i40e_stats i40e_gstrings_veb_stats[] = { - I40E_VEB_STAT("rx_bytes", stats.rx_bytes), - I40E_VEB_STAT("tx_bytes", stats.tx_bytes), - I40E_VEB_STAT("rx_unicast", stats.rx_unicast), - I40E_VEB_STAT("tx_unicast", stats.tx_unicast), - I40E_VEB_STAT("rx_multicast", stats.rx_multicast), - I40E_VEB_STAT("tx_multicast", stats.tx_multicast), - I40E_VEB_STAT("rx_broadcast", stats.rx_broadcast), - I40E_VEB_STAT("tx_broadcast", stats.tx_broadcast), - I40E_VEB_STAT("rx_discards", stats.rx_discards), - I40E_VEB_STAT("tx_discards", stats.tx_discards), - I40E_VEB_STAT("tx_errors", stats.tx_errors), - I40E_VEB_STAT("rx_unknown_protocol", stats.rx_unknown_protocol), + I40E_VEB_STAT("veb.rx_bytes", stats.rx_bytes), + I40E_VEB_STAT("veb.tx_bytes", stats.tx_bytes), + I40E_VEB_STAT("veb.rx_unicast", stats.rx_unicast), + I40E_VEB_STAT("veb.tx_unicast", stats.tx_unicast), + I40E_VEB_STAT("veb.rx_multicast", stats.rx_multicast), + I40E_VEB_STAT("veb.tx_multicast", stats.tx_multicast), + I40E_VEB_STAT("veb.rx_broadcast", stats.rx_broadcast), + I40E_VEB_STAT("veb.tx_broadcast", stats.tx_broadcast), + I40E_VEB_STAT("veb.rx_discards", stats.rx_discards), + I40E_VEB_STAT("veb.tx_discards", stats.tx_discards), + I40E_VEB_STAT("veb.tx_errors", stats.tx_errors), + I40E_VEB_STAT("veb.rx_unknown_protocol", stats.rx_unknown_protocol), +}; + +static const struct i40e_stats i40e_gstrings_veb_tc_stats[] = { + I40E_VEB_STAT("veb.tc_%u_tx_packets", tc_stats.tc_tx_packets), + I40E_VEB_STAT("veb.tc_%u_tx_bytes", tc_stats.tc_tx_bytes), + I40E_VEB_STAT("veb.tc_%u_rx_packets", tc_stats.tc_rx_packets), + I40E_VEB_STAT("veb.tc_%u_rx_bytes", tc_stats.tc_rx_bytes), }; static const struct i40e_stats i40e_gstrings_misc_stats[] = { @@ -89,6 +72,7 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = { I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol), I40E_VSI_STAT("tx_linearize", tx_linearize), I40E_VSI_STAT("tx_force_wb", tx_force_wb), + I40E_VSI_STAT("tx_busy", tx_busy), I40E_VSI_STAT("rx_alloc_fail", rx_buf_failed), I40E_VSI_STAT("rx_pg_alloc_fail", rx_page_failed), }; @@ -104,93 +88,171 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = { * is queried on the base PF netdev, not on the VMDq or FCoE netdev. */ static const struct i40e_stats i40e_gstrings_stats[] = { - I40E_PF_STAT("rx_bytes", stats.eth.rx_bytes), - I40E_PF_STAT("tx_bytes", stats.eth.tx_bytes), - I40E_PF_STAT("rx_unicast", stats.eth.rx_unicast), - I40E_PF_STAT("tx_unicast", stats.eth.tx_unicast), - I40E_PF_STAT("rx_multicast", stats.eth.rx_multicast), - I40E_PF_STAT("tx_multicast", stats.eth.tx_multicast), - I40E_PF_STAT("rx_broadcast", stats.eth.rx_broadcast), - I40E_PF_STAT("tx_broadcast", stats.eth.tx_broadcast), - I40E_PF_STAT("tx_errors", stats.eth.tx_errors), - I40E_PF_STAT("rx_dropped", stats.eth.rx_discards), - I40E_PF_STAT("tx_dropped_link_down", stats.tx_dropped_link_down), - I40E_PF_STAT("rx_crc_errors", stats.crc_errors), - I40E_PF_STAT("illegal_bytes", stats.illegal_bytes), - I40E_PF_STAT("mac_local_faults", stats.mac_local_faults), - I40E_PF_STAT("mac_remote_faults", stats.mac_remote_faults), - I40E_PF_STAT("tx_timeout", tx_timeout_count), - I40E_PF_STAT("rx_csum_bad", hw_csum_rx_error), - I40E_PF_STAT("rx_length_errors", stats.rx_length_errors), - I40E_PF_STAT("link_xon_rx", stats.link_xon_rx), - I40E_PF_STAT("link_xoff_rx", stats.link_xoff_rx), - I40E_PF_STAT("link_xon_tx", stats.link_xon_tx), - I40E_PF_STAT("link_xoff_tx", stats.link_xoff_tx), - I40E_PF_STAT("rx_size_64", stats.rx_size_64), - I40E_PF_STAT("rx_size_127", stats.rx_size_127), - I40E_PF_STAT("rx_size_255", stats.rx_size_255), - I40E_PF_STAT("rx_size_511", stats.rx_size_511), - I40E_PF_STAT("rx_size_1023", stats.rx_size_1023), - I40E_PF_STAT("rx_size_1522", stats.rx_size_1522), - I40E_PF_STAT("rx_size_big", stats.rx_size_big), - I40E_PF_STAT("tx_size_64", stats.tx_size_64), - I40E_PF_STAT("tx_size_127", stats.tx_size_127), - I40E_PF_STAT("tx_size_255", stats.tx_size_255), - I40E_PF_STAT("tx_size_511", stats.tx_size_511), - I40E_PF_STAT("tx_size_1023", stats.tx_size_1023), - I40E_PF_STAT("tx_size_1522", stats.tx_size_1522), - I40E_PF_STAT("tx_size_big", stats.tx_size_big), - I40E_PF_STAT("rx_undersize", stats.rx_undersize), - I40E_PF_STAT("rx_fragments", stats.rx_fragments), - I40E_PF_STAT("rx_oversize", stats.rx_oversize), - I40E_PF_STAT("rx_jabber", stats.rx_jabber), - I40E_PF_STAT("VF_admin_queue_requests", vf_aq_requests), - I40E_PF_STAT("arq_overflows", arq_overflows), - I40E_PF_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared), - I40E_PF_STAT("tx_hwtstamp_skipped", tx_hwtstamp_skipped), - I40E_PF_STAT("fdir_flush_cnt", fd_flush_cnt), - I40E_PF_STAT("fdir_atr_match", stats.fd_atr_match), - I40E_PF_STAT("fdir_atr_tunnel_match", stats.fd_atr_tunnel_match), - I40E_PF_STAT("fdir_atr_status", stats.fd_atr_status), - I40E_PF_STAT("fdir_sb_match", stats.fd_sb_match), - I40E_PF_STAT("fdir_sb_status", stats.fd_sb_status), + I40E_PF_STAT("port.rx_bytes", stats.eth.rx_bytes), + I40E_PF_STAT("port.tx_bytes", stats.eth.tx_bytes), + I40E_PF_STAT("port.rx_unicast", stats.eth.rx_unicast), + I40E_PF_STAT("port.tx_unicast", stats.eth.tx_unicast), + I40E_PF_STAT("port.rx_multicast", stats.eth.rx_multicast), + I40E_PF_STAT("port.tx_multicast", stats.eth.tx_multicast), + I40E_PF_STAT("port.rx_broadcast", stats.eth.rx_broadcast), + I40E_PF_STAT("port.tx_broadcast", stats.eth.tx_broadcast), + I40E_PF_STAT("port.tx_errors", stats.eth.tx_errors), + I40E_PF_STAT("port.rx_dropped", stats.eth.rx_discards), + I40E_PF_STAT("port.tx_dropped_link_down", stats.tx_dropped_link_down), + I40E_PF_STAT("port.rx_crc_errors", stats.crc_errors), + I40E_PF_STAT("port.illegal_bytes", stats.illegal_bytes), + I40E_PF_STAT("port.mac_local_faults", stats.mac_local_faults), + I40E_PF_STAT("port.mac_remote_faults", stats.mac_remote_faults), + I40E_PF_STAT("port.tx_timeout", tx_timeout_count), + I40E_PF_STAT("port.rx_csum_bad", hw_csum_rx_error), + I40E_PF_STAT("port.rx_length_errors", stats.rx_length_errors), + I40E_PF_STAT("port.link_xon_rx", stats.link_xon_rx), + I40E_PF_STAT("port.link_xoff_rx", stats.link_xoff_rx), + I40E_PF_STAT("port.link_xon_tx", stats.link_xon_tx), + I40E_PF_STAT("port.link_xoff_tx", stats.link_xoff_tx), + I40E_PF_STAT("port.rx_size_64", stats.rx_size_64), + I40E_PF_STAT("port.rx_size_127", stats.rx_size_127), + I40E_PF_STAT("port.rx_size_255", stats.rx_size_255), + I40E_PF_STAT("port.rx_size_511", stats.rx_size_511), + I40E_PF_STAT("port.rx_size_1023", stats.rx_size_1023), + I40E_PF_STAT("port.rx_size_1522", stats.rx_size_1522), + I40E_PF_STAT("port.rx_size_big", stats.rx_size_big), + I40E_PF_STAT("port.tx_size_64", stats.tx_size_64), + I40E_PF_STAT("port.tx_size_127", stats.tx_size_127), + I40E_PF_STAT("port.tx_size_255", stats.tx_size_255), + I40E_PF_STAT("port.tx_size_511", stats.tx_size_511), + I40E_PF_STAT("port.tx_size_1023", stats.tx_size_1023), + I40E_PF_STAT("port.tx_size_1522", stats.tx_size_1522), + I40E_PF_STAT("port.tx_size_big", stats.tx_size_big), + I40E_PF_STAT("port.rx_undersize", stats.rx_undersize), + I40E_PF_STAT("port.rx_fragments", stats.rx_fragments), + I40E_PF_STAT("port.rx_oversize", stats.rx_oversize), + I40E_PF_STAT("port.rx_jabber", stats.rx_jabber), + I40E_PF_STAT("port.VF_admin_queue_requests", vf_aq_requests), + I40E_PF_STAT("port.arq_overflows", arq_overflows), +#ifdef HAVE_PTP_1588_CLOCK + I40E_PF_STAT("port.tx_hwtstamp_timeouts", tx_hwtstamp_timeouts), + I40E_PF_STAT("port.rx_hwtstamp_cleared", rx_hwtstamp_cleared), + I40E_PF_STAT("port.tx_hwtstamp_skipped", tx_hwtstamp_skipped), +#endif /* HAVE_PTP_1588_CLOCK */ + I40E_PF_STAT("port.fdir_flush_cnt", fd_flush_cnt), + I40E_PF_STAT("port.fdir_atr_match", stats.fd_atr_match), + I40E_PF_STAT("port.fdir_atr_tunnel_match", stats.fd_atr_tunnel_match), + I40E_PF_STAT("port.fdir_atr_status", stats.fd_atr_status), + I40E_PF_STAT("port.fdir_sb_match", stats.fd_sb_match), + I40E_PF_STAT("port.fdir_sb_status", stats.fd_sb_status), +#ifdef I40E_ADD_PROBES + I40E_PF_STAT("port.tx_tcp_segments", tcp_segs), + I40E_PF_STAT("port.tx_tcp_cso", tx_tcp_cso), + I40E_PF_STAT("port.tx_udp_cso", tx_udp_cso), + I40E_PF_STAT("port.tx_sctp_cso", tx_sctp_cso), + I40E_PF_STAT("port.tx_ip4_cso", tx_ip4_cso), + I40E_PF_STAT("port.rx_tcp_cso", rx_tcp_cso), + I40E_PF_STAT("port.rx_udp_cso", rx_udp_cso), + I40E_PF_STAT("port.rx_sctp_cso", rx_sctp_cso), + I40E_PF_STAT("port.rx_ip4_cso", rx_ip4_cso), + I40E_PF_STAT("port.rx_csum_offload_outer", hw_csum_rx_outer), + I40E_PF_STAT("port.rx_tcp_cso_error", rx_tcp_cso_err), + I40E_PF_STAT("port.rx_udp_cso_error", rx_udp_cso_err), + I40E_PF_STAT("port.rx_sctp_cso_error", rx_sctp_cso_err), + I40E_PF_STAT("port.rx_ip4_cso_error", rx_ip4_cso_err), +#endif /* LPI stats */ - I40E_PF_STAT("tx_lpi_status", stats.tx_lpi_status), - I40E_PF_STAT("rx_lpi_status", stats.rx_lpi_status), - I40E_PF_STAT("tx_lpi_count", stats.tx_lpi_count), - I40E_PF_STAT("rx_lpi_count", stats.rx_lpi_count), + I40E_PF_STAT("port.tx_lpi_status", stats.tx_lpi_status), + I40E_PF_STAT("port.rx_lpi_status", stats.rx_lpi_status), + I40E_PF_STAT("port.tx_lpi_count", stats.tx_lpi_count), + I40E_PF_STAT("port.rx_lpi_count", stats.rx_lpi_count), + I40E_PF_STAT("port.tx_lpi_duration", stats.tx_lpi_duration), + I40E_PF_STAT("port.rx_lpi_duration", stats.rx_lpi_duration), }; -#define I40E_QUEUE_STATS_LEN(n) \ - (((struct i40e_netdev_priv *)netdev_priv((n)))->vsi->num_queue_pairs \ - * 2 /* Tx and Rx together */ \ - * (sizeof(struct i40e_queue_stats) / sizeof(u64))) -#define I40E_GLOBAL_STATS_LEN ARRAY_SIZE(i40e_gstrings_stats) -#define I40E_NETDEV_STATS_LEN ARRAY_SIZE(i40e_gstrings_net_stats) +struct i40e_pfc_stats { + u64 priority_xon_rx; + u64 priority_xoff_rx; + u64 priority_xon_tx; + u64 priority_xoff_tx; + u64 priority_xon_2_xoff; +}; + +static const struct i40e_stats i40e_gstrings_pfc_stats[] = { + I40E_PFC_STAT("port.tx_priority_%u_xon_tx", priority_xon_tx), + I40E_PFC_STAT("port.tx_priority_%u_xoff_tx", priority_xoff_tx), + I40E_PFC_STAT("port.rx_priority_%u_xon_rx", priority_xon_rx), + I40E_PFC_STAT("port.rx_priority_%u_xoff_rx", priority_xoff_rx), + I40E_PFC_STAT("port.rx_priority_%u_xon_2_xoff", priority_xon_2_xoff), +}; + +#define I40E_NETDEV_STATS_LEN ARRAY_SIZE(i40e_gstrings_net_stats) + #define I40E_MISC_STATS_LEN ARRAY_SIZE(i40e_gstrings_misc_stats) -#define I40E_VSI_STATS_LEN(n) (I40E_NETDEV_STATS_LEN + \ - I40E_MISC_STATS_LEN + \ - I40E_QUEUE_STATS_LEN((n))) -#define I40E_PFC_STATS_LEN ( \ - (FIELD_SIZEOF(struct i40e_pf, stats.priority_xoff_rx) + \ - FIELD_SIZEOF(struct i40e_pf, stats.priority_xon_rx) + \ - FIELD_SIZEOF(struct i40e_pf, stats.priority_xoff_tx) + \ - FIELD_SIZEOF(struct i40e_pf, stats.priority_xon_tx) + \ - FIELD_SIZEOF(struct i40e_pf, stats.priority_xon_2_xoff)) \ - / sizeof(u64)) -#define I40E_VEB_TC_STATS_LEN ( \ - (FIELD_SIZEOF(struct i40e_veb, tc_stats.tc_rx_packets) + \ - FIELD_SIZEOF(struct i40e_veb, tc_stats.tc_rx_bytes) + \ - FIELD_SIZEOF(struct i40e_veb, tc_stats.tc_tx_packets) + \ - FIELD_SIZEOF(struct i40e_veb, tc_stats.tc_tx_bytes)) \ - / sizeof(u64)) -#define I40E_VEB_STATS_LEN ARRAY_SIZE(i40e_gstrings_veb_stats) -#define I40E_VEB_STATS_TOTAL (I40E_VEB_STATS_LEN + I40E_VEB_TC_STATS_LEN) -#define I40E_PF_STATS_LEN(n) (I40E_GLOBAL_STATS_LEN + \ + +#define I40E_VSI_STATS_LEN (I40E_NETDEV_STATS_LEN + I40E_MISC_STATS_LEN) + +#define I40E_PFC_STATS_LEN (ARRAY_SIZE(i40e_gstrings_pfc_stats) * \ + I40E_MAX_USER_PRIORITY) + +#define I40E_VEB_STATS_LEN (ARRAY_SIZE(i40e_gstrings_veb_stats) + \ + (ARRAY_SIZE(i40e_gstrings_veb_tc_stats) * \ + I40E_MAX_TRAFFIC_CLASS)) + +#define I40E_GLOBAL_STATS_LEN ARRAY_SIZE(i40e_gstrings_stats) + +/* Length (number) of PF core stats only (i.e. without queues / extra stats): */ +#define I40E_PF_STATS_LEN (I40E_GLOBAL_STATS_LEN + \ I40E_PFC_STATS_LEN + \ - I40E_VSI_STATS_LEN((n))) + I40E_VEB_STATS_LEN + \ + I40E_VSI_STATS_LEN) + +/* Length of stats for a single queue */ +#define I40E_QUEUE_STATS_LEN ARRAY_SIZE(i40e_gstrings_queue_stats) +#ifdef HAVE_XDP_SUPPORT +#define I40E_QUEUE_STATS_XDP_LEN ARRAY_SIZE(i40e_gstrings_rx_queue_xdp_stats) +#endif +#ifndef I40E_PF_EXTRA_STATS_OFF + +#define I40E_STATS_NAME_VFID_EXTRA "vf___." +#define I40E_STATS_NAME_VFID_EXTRA_LEN (sizeof(I40E_STATS_NAME_VFID_EXTRA) - 1) + +static struct i40e_stats i40e_gstrings_eth_stats_extra[] = { + I40E_VSI_STAT(I40E_STATS_NAME_VFID_EXTRA + "rx_bytes", eth_stats.rx_bytes), + I40E_VSI_STAT(I40E_STATS_NAME_VFID_EXTRA + "rx_unicast", eth_stats.rx_unicast), + I40E_VSI_STAT(I40E_STATS_NAME_VFID_EXTRA + "rx_multicast", eth_stats.rx_multicast), + I40E_VSI_STAT(I40E_STATS_NAME_VFID_EXTRA + "rx_broadcast", eth_stats.rx_broadcast), + I40E_VSI_STAT(I40E_STATS_NAME_VFID_EXTRA + "rx_discards", eth_stats.rx_discards), + I40E_VSI_STAT(I40E_STATS_NAME_VFID_EXTRA + "rx_unknown_protocol", eth_stats.rx_unknown_protocol), + I40E_VSI_STAT(I40E_STATS_NAME_VFID_EXTRA + "tx_bytes", eth_stats.tx_bytes), + I40E_VSI_STAT(I40E_STATS_NAME_VFID_EXTRA + "tx_unicast", eth_stats.tx_unicast), + I40E_VSI_STAT(I40E_STATS_NAME_VFID_EXTRA + "tx_multicast", eth_stats.tx_multicast), + I40E_VSI_STAT(I40E_STATS_NAME_VFID_EXTRA + "tx_broadcast", eth_stats.tx_broadcast), + I40E_VSI_STAT(I40E_STATS_NAME_VFID_EXTRA + "tx_discards", eth_stats.tx_discards), + I40E_VSI_STAT(I40E_STATS_NAME_VFID_EXTRA + "tx_errors", eth_stats.tx_errors), +}; + +#define I40E_STATS_EXTRA_COUNT 128 /* as for now only I40E_MAX_VF_COUNT */ +/* Following length value does not include the length values for queues stats */ +#define I40E_STATS_EXTRA_LEN ARRAY_SIZE(i40e_gstrings_eth_stats_extra) +/* Length (number) of PF extra stats only (i.e. without core stats / queues): */ +#define I40E_PF_STATS_EXTRA_LEN (I40E_STATS_EXTRA_COUNT * I40E_STATS_EXTRA_LEN) +/* Length (number) of enhanced/all PF stats (i.e. core with extra stats): */ +#define I40E_PF_STATS_ENHANCE_LEN (I40E_PF_STATS_LEN + I40E_PF_STATS_EXTRA_LEN) + +#endif /* !I40E_PF_EXTRA_STATS_OFF */ +#endif /* ETHTOOL_GSTATS */ +#ifdef ETHTOOL_TEST enum i40e_ethtool_test_id { I40E_ETH_TEST_REG = 0, I40E_ETH_TEST_EEPROM, @@ -207,6 +269,9 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = { #define I40E_TEST_LEN (sizeof(i40e_gstrings_test) / ETH_GSTRING_LEN) +#endif /* ETHTOOL_TEST */ + +#ifdef HAVE_ETHTOOL_GET_SSET_COUNT struct i40e_priv_flags { char flag_string[ETH_GSTRING_LEN]; u64 flag; @@ -222,11 +287,21 @@ struct i40e_priv_flags { static const struct i40e_priv_flags i40e_gstrings_priv_flags[] = { /* NOTE: MFP setting cannot be changed */ I40E_PRIV_FLAG("MFP", I40E_FLAG_MFP_ENABLED, 1), + I40E_PRIV_FLAG("total-port-shutdown", I40E_FLAG_TOTAL_PORT_SHUTDOWN, 1), I40E_PRIV_FLAG("LinkPolling", I40E_FLAG_LINK_POLLING_ENABLED, 0), I40E_PRIV_FLAG("flow-director-atr", I40E_FLAG_FD_ATR_ENABLED, 0), I40E_PRIV_FLAG("veb-stats", I40E_FLAG_VEB_STATS_ENABLED, 0), I40E_PRIV_FLAG("hw-atr-eviction", I40E_FLAG_HW_ATR_EVICT_ENABLED, 0), + I40E_PRIV_FLAG("link-down-on-close", + I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED, 0), +#ifdef HAVE_SWIOTLB_SKIP_CPU_SYNC I40E_PRIV_FLAG("legacy-rx", I40E_FLAG_LEGACY_RX, 0), +#endif + I40E_PRIV_FLAG("disable-source-pruning", + I40E_FLAG_SOURCE_PRUNING_DISABLED, 0), + I40E_PRIV_FLAG("disable-fw-lldp", I40E_FLAG_DISABLE_FW_LLDP, 0), + I40E_PRIV_FLAG("rs-fec", I40E_FLAG_RS_FEC, 0), + I40E_PRIV_FLAG("base-r-fec", I40E_FLAG_BASE_R_FEC, 0), }; #define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_gstrings_priv_flags) @@ -239,6 +314,7 @@ static const struct i40e_priv_flags i40e_gl_gstrings_priv_flags[] = { #define I40E_GL_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_gl_gstrings_priv_flags) +#endif /* HAVE_ETHTOOL_GET_SSET_COUNT */ /** * i40e_partition_setting_complaint - generic complaint for MFP restriction * @pf: the PF struct @@ -251,431 +327,1001 @@ static void i40e_partition_setting_complaint(struct i40e_pf *pf) /** * i40e_phy_type_to_ethtool - convert the phy_types to ethtool link modes - * @phy_types: PHY types to convert - * @supported: pointer to the ethtool supported variable to fill in - * @advertising: pointer to the ethtool advertising variable to fill in + * @pf: PF struct with phy_types + * @ks: ethtool link ksettings struct to fill out * **/ -static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, - u32 *advertising) +static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, + struct ethtool_link_ksettings *ks) { struct i40e_link_status *hw_link_info = &pf->hw.phy.link_info; u64 phy_types = pf->hw.phy.phy_types; - *supported = 0x0; - *advertising = 0x0; + ethtool_link_ksettings_zero_link_mode(ks, supported); + ethtool_link_ksettings_zero_link_mode(ks, advertising); if (phy_types & I40E_CAP_PHY_TYPE_SGMII) { - *supported |= SUPPORTED_Autoneg | - SUPPORTED_1000baseT_Full; - *advertising |= ADVERTISED_Autoneg; + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - *advertising |= ADVERTISED_1000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Full); if (pf->hw_features & I40E_HW_100M_SGMII_CAPABLE) { - *supported |= SUPPORTED_100baseT_Full; - *advertising |= ADVERTISED_100baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, + 100baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 100baseT_Full); } } if (phy_types & I40E_CAP_PHY_TYPE_XAUI || phy_types & I40E_CAP_PHY_TYPE_XFI || phy_types & I40E_CAP_PHY_TYPE_SFI || phy_types & I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU || - phy_types & I40E_CAP_PHY_TYPE_10GBASE_AOC) - *supported |= SUPPORTED_10000baseT_Full; - if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1_CU || - phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1 || - phy_types & I40E_CAP_PHY_TYPE_10GBASE_T || - phy_types & I40E_CAP_PHY_TYPE_10GBASE_SR || - phy_types & I40E_CAP_PHY_TYPE_10GBASE_LR) { - *supported |= SUPPORTED_Autoneg | - SUPPORTED_10000baseT_Full; - *advertising |= ADVERTISED_Autoneg; + phy_types & I40E_CAP_PHY_TYPE_10GBASE_AOC) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseT_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseT_Full); + } + if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_T) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - *advertising |= ADVERTISED_10000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseT_Full); + } +#ifdef HAVE_ETHTOOL_NEW_2500MB_BITS + if (phy_types & I40E_CAP_PHY_TYPE_2_5GBASE_T) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 2500baseT_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_2_5GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 2500baseT_Full); } +#endif /* HAVE_ETHTOOL_NEW_2500MB_BITS */ +#ifdef HAVE_ETHTOOL_5G_BITS + if (phy_types & I40E_CAP_PHY_TYPE_5GBASE_T) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 5000baseT_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_5GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 5000baseT_Full); + } +#endif /* HAVE_ETHTOOL_5G_BITS */ if (phy_types & I40E_CAP_PHY_TYPE_XLAUI || phy_types & I40E_CAP_PHY_TYPE_XLPPI || phy_types & I40E_CAP_PHY_TYPE_40GBASE_AOC) - *supported |= SUPPORTED_40000baseCR4_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseCR4_Full); if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4_CU || phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4) { - *supported |= SUPPORTED_Autoneg | - SUPPORTED_40000baseCR4_Full; - *advertising |= ADVERTISED_Autoneg; + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseCR4_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_40GB) - *advertising |= ADVERTISED_40000baseCR4_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseCR4_Full); } if (phy_types & I40E_CAP_PHY_TYPE_100BASE_TX) { - *supported |= SUPPORTED_Autoneg | - SUPPORTED_100baseT_Full; - *advertising |= ADVERTISED_Autoneg; + ethtool_link_ksettings_add_link_mode(ks, supported, + 100baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) - *advertising |= ADVERTISED_100baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 100baseT_Full); } - if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_T || - phy_types & I40E_CAP_PHY_TYPE_1000BASE_SX || - phy_types & I40E_CAP_PHY_TYPE_1000BASE_LX || - phy_types & I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL) { - *supported |= SUPPORTED_Autoneg | - SUPPORTED_1000baseT_Full; - *advertising |= ADVERTISED_Autoneg; + if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_T) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - *advertising |= ADVERTISED_1000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Full); + } + if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_SR4) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseSR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseSR4_Full); + } + if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_LR4) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseLR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseLR4_Full); } - if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_SR4) - *supported |= SUPPORTED_40000baseSR4_Full; - if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_LR4) - *supported |= SUPPORTED_40000baseLR4_Full; if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_KR4) { - *supported |= SUPPORTED_40000baseKR4_Full | - SUPPORTED_Autoneg; - *advertising |= ADVERTISED_40000baseKR4_Full | - ADVERTISED_Autoneg; + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseKR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseKR4_Full); } if (phy_types & I40E_CAP_PHY_TYPE_20GBASE_KR2) { - *supported |= SUPPORTED_20000baseKR2_Full | - SUPPORTED_Autoneg; - *advertising |= ADVERTISED_Autoneg; + ethtool_link_ksettings_add_link_mode(ks, supported, + 20000baseKR2_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_20GB) - *advertising |= ADVERTISED_20000baseKR2_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 20000baseKR2_Full); } - if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR) { - if (!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) - *supported |= SUPPORTED_10000baseKR_Full | - SUPPORTED_Autoneg; - *advertising |= ADVERTISED_Autoneg; + if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseKX4_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - if (!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) - *advertising |= ADVERTISED_10000baseKR_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseKX4_Full); } - if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4) { - *supported |= SUPPORTED_10000baseKX4_Full | - SUPPORTED_Autoneg; - *advertising |= ADVERTISED_Autoneg; + if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR && + !(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseKR_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - *advertising |= ADVERTISED_10000baseKX4_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseKR_Full); } - if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX) { - if (!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) - *supported |= SUPPORTED_1000baseKX_Full | - SUPPORTED_Autoneg; - *advertising |= ADVERTISED_Autoneg; + if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX && + !(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseKX_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - if (!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) - *advertising |= ADVERTISED_1000baseKX_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseKX_Full); + } +#ifdef HAVE_ETHTOOL_25G_BITS + /* need to add 25G PHY types */ + if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_KR) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 25000baseKR_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_25GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 25000baseKR_Full); + } + if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_CR) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 25000baseCR_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_25GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 25000baseCR_Full); } + if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_SR || + phy_types & I40E_CAP_PHY_TYPE_25GBASE_LR) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 25000baseSR_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_25GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 25000baseSR_Full); + } + if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_AOC || + phy_types & I40E_CAP_PHY_TYPE_25GBASE_ACC) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 25000baseCR_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_25GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 25000baseCR_Full); + } +#ifdef ETHTOOL_GFECPARAM if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_KR || phy_types & I40E_CAP_PHY_TYPE_25GBASE_CR || phy_types & I40E_CAP_PHY_TYPE_25GBASE_SR || - phy_types & I40E_CAP_PHY_TYPE_25GBASE_LR) { - *supported |= SUPPORTED_Autoneg; - *advertising |= ADVERTISED_Autoneg; + phy_types & I40E_CAP_PHY_TYPE_25GBASE_LR || + phy_types & I40E_CAP_PHY_TYPE_25GBASE_AOC || + phy_types & I40E_CAP_PHY_TYPE_25GBASE_ACC) { + ethtool_link_ksettings_add_link_mode(ks, supported, FEC_NONE); + ethtool_link_ksettings_add_link_mode(ks, supported, FEC_RS); + ethtool_link_ksettings_add_link_mode(ks, supported, FEC_BASER); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_25GB) { + ethtool_link_ksettings_add_link_mode(ks, advertising, + FEC_NONE); + ethtool_link_ksettings_add_link_mode(ks, advertising, + FEC_RS); + ethtool_link_ksettings_add_link_mode(ks, advertising, + FEC_BASER); + } + } +#endif /* ETHTOOL_GFECPARAM */ +#endif /* HAVE_ETHTOOL_25G_BITS */ +#ifdef HAVE_ETHTOOL_NEW_10G_BITS + /* need to add new 10G PHY types */ + if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1 || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1_CU) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseCR_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseCR_Full); + } + if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_SR) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseSR_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseSR_Full); + } + if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_LR) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseLR_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseLR_Full); + } + if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_SX || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_LX || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseX_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseX_Full); + } +#else + /* need to keep backward compatibility with older kernels */ + if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1 || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1_CU || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_SR || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_LR) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseT_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseT_Full); + } + if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_SX || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_LX || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL) { + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Full); + } +#endif /* HAVE_ETHTOOL_NEW_10G_BITS */ + /* Autoneg PHY types */ + if (phy_types & I40E_CAP_PHY_TYPE_SGMII || + phy_types & I40E_CAP_PHY_TYPE_40GBASE_KR4 || + phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4_CU || + phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4 || + phy_types & I40E_CAP_PHY_TYPE_25GBASE_SR || + phy_types & I40E_CAP_PHY_TYPE_25GBASE_LR || + phy_types & I40E_CAP_PHY_TYPE_25GBASE_KR || + phy_types & I40E_CAP_PHY_TYPE_25GBASE_CR || + phy_types & I40E_CAP_PHY_TYPE_20GBASE_KR2 || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_SR || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_LR || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4 || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1_CU || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1 || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_T || + phy_types & I40E_CAP_PHY_TYPE_5GBASE_T || + phy_types & I40E_CAP_PHY_TYPE_2_5GBASE_T || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_T || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_SX || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_LX || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX || + phy_types & I40E_CAP_PHY_TYPE_100BASE_TX) { + ethtool_link_ksettings_add_link_mode(ks, supported, + Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, + Autoneg); } } +#ifdef ETHTOOL_GFECPARAM /** - * i40e_get_settings_link_up - Get the Link settings for when link is up + * i40e_get_settings_link_up_fec - Get the FEC mode encoding from mask + * @req_fec_info: mask request fec info + * @ks: ethtool ksettings to fill in + **/ +static void i40e_get_settings_link_up_fec(u8 req_fec_info, + struct ethtool_link_ksettings *ks) +{ + ethtool_link_ksettings_add_link_mode(ks, supported, FEC_NONE); + ethtool_link_ksettings_add_link_mode(ks, supported, FEC_RS); + ethtool_link_ksettings_add_link_mode(ks, supported, FEC_BASER); + + if ((I40E_AQ_SET_FEC_REQUEST_RS & req_fec_info) && + (I40E_AQ_SET_FEC_REQUEST_KR & req_fec_info)) { + ethtool_link_ksettings_add_link_mode(ks, advertising, + FEC_NONE); + ethtool_link_ksettings_add_link_mode(ks, advertising, + FEC_BASER); + ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_RS); + } else if (I40E_AQ_SET_FEC_REQUEST_RS & req_fec_info) { + ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_RS); + } else if (I40E_AQ_SET_FEC_REQUEST_KR & req_fec_info) { + ethtool_link_ksettings_add_link_mode(ks, advertising, + FEC_BASER); + } else { + ethtool_link_ksettings_add_link_mode(ks, advertising, + FEC_NONE); + } +} +#endif /* ETHTOOL_GFECPARAM */ + +/** + * i40e_get_settings_link_up - Get Link settings for when link is up * @hw: hw structure - * @ecmd: ethtool command to fill in + * @ks: ethtool ksettings to fill in * @netdev: network interface device structure - * + * @pf: pointer to physical function struct **/ static void i40e_get_settings_link_up(struct i40e_hw *hw, - struct ethtool_link_ksettings *cmd, + struct ethtool_link_ksettings *ks, struct net_device *netdev, struct i40e_pf *pf) { struct i40e_link_status *hw_link_info = &hw->phy.link_info; + struct ethtool_link_ksettings cap_ksettings; u32 link_speed = hw_link_info->link_speed; - u32 e_advertising = 0x0; - u32 e_supported = 0x0; - u32 supported, advertising; - - ethtool_convert_link_mode_to_legacy_u32(&supported, - cmd->link_modes.supported); - ethtool_convert_link_mode_to_legacy_u32(&advertising, - cmd->link_modes.advertising); /* Initialize supported and advertised settings based on phy settings */ switch (hw_link_info->phy_type) { case I40E_PHY_TYPE_40GBASE_CR4: case I40E_PHY_TYPE_40GBASE_CR4_CU: - supported = SUPPORTED_Autoneg | - SUPPORTED_40000baseCR4_Full; - advertising = ADVERTISED_Autoneg | - ADVERTISED_40000baseCR4_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseCR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseCR4_Full); break; case I40E_PHY_TYPE_XLAUI: case I40E_PHY_TYPE_XLPPI: case I40E_PHY_TYPE_40GBASE_AOC: - supported = SUPPORTED_40000baseCR4_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseCR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseCR4_Full); break; case I40E_PHY_TYPE_40GBASE_SR4: - supported = SUPPORTED_40000baseSR4_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseSR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseSR4_Full); break; case I40E_PHY_TYPE_40GBASE_LR4: - supported = SUPPORTED_40000baseLR4_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseLR4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseLR4_Full); break; + case I40E_PHY_TYPE_25GBASE_SR: + case I40E_PHY_TYPE_25GBASE_LR: case I40E_PHY_TYPE_10GBASE_SR: case I40E_PHY_TYPE_10GBASE_LR: case I40E_PHY_TYPE_1000BASE_SX: case I40E_PHY_TYPE_1000BASE_LX: - supported = SUPPORTED_10000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); +#ifdef HAVE_ETHTOOL_25G_BITS + ethtool_link_ksettings_add_link_mode(ks, supported, + 25000baseSR_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 25000baseSR_Full); +#ifdef ETHTOOL_GFECPARAM + i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks); +#endif /* ETHTOOL_GFECPARAM */ +#endif /* HAVE_ETHTOOL_25G_BITS */ +#ifdef HAVE_ETHTOOL_NEW_10G_BITS + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseSR_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseSR_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseLR_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseLR_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseX_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseX_Full); +#endif /* HAVE_ETHTOOL_NEW_10G_BITS */ + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseT_Full); if (hw_link_info->module_type[2] & I40E_MODULE_TYPE_1000BASE_SX || hw_link_info->module_type[2] & I40E_MODULE_TYPE_1000BASE_LX) { - supported |= SUPPORTED_1000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - advertising |= ADVERTISED_1000baseT_Full; + ethtool_link_ksettings_add_link_mode( + ks, advertising, 1000baseT_Full); } if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - advertising |= ADVERTISED_10000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseT_Full); break; case I40E_PHY_TYPE_10GBASE_T: + case I40E_PHY_TYPE_5GBASE_T: + case I40E_PHY_TYPE_2_5GBASE_T: case I40E_PHY_TYPE_1000BASE_T: case I40E_PHY_TYPE_100BASE_TX: - supported = SUPPORTED_Autoneg | - SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_100baseT_Full; - advertising = ADVERTISED_Autoneg; + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseT_Full); +#ifdef HAVE_ETHTOOL_5G_BITS + ethtool_link_ksettings_add_link_mode(ks, supported, + 5000baseT_Full); +#endif /* HAVE_ETHTOOL_5G_BITS */ +#ifdef HAVE_ETHTOOL_NEW_2500MB_BITS + ethtool_link_ksettings_add_link_mode(ks, supported, + 2500baseT_Full); +#endif /* HAVE_ETHTOOL_NEW_2500MB_BITS */ + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 100baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - advertising |= ADVERTISED_10000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseT_Full); +#ifdef HAVE_ETHTOOL_5G_BITS + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_5GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 5000baseT_Full); +#endif /* HAVE_ETHTOOL_5G_BITS */ +#ifdef HAVE_ETHTOOL_NEW_2500MB_BITS + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_2_5GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 2500baseT_Full); +#endif /* HAVE_ETHTOOL_NEW_2500MB_BITS */ if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - advertising |= ADVERTISED_1000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) - advertising |= ADVERTISED_100baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 100baseT_Full); break; case I40E_PHY_TYPE_1000BASE_T_OPTICAL: - supported = SUPPORTED_Autoneg | - SUPPORTED_1000baseT_Full; - advertising = ADVERTISED_Autoneg | - ADVERTISED_1000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Full); break; case I40E_PHY_TYPE_10GBASE_CR1_CU: case I40E_PHY_TYPE_10GBASE_CR1: - supported = SUPPORTED_Autoneg | - SUPPORTED_10000baseT_Full; - advertising = ADVERTISED_Autoneg | - ADVERTISED_10000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseT_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseT_Full); break; case I40E_PHY_TYPE_XAUI: case I40E_PHY_TYPE_XFI: case I40E_PHY_TYPE_SFI: case I40E_PHY_TYPE_10GBASE_SFPP_CU: case I40E_PHY_TYPE_10GBASE_AOC: - supported = SUPPORTED_10000baseT_Full; - advertising = SUPPORTED_10000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseT_Full); + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseT_Full); break; case I40E_PHY_TYPE_SGMII: - supported = SUPPORTED_Autoneg | - SUPPORTED_1000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - advertising |= ADVERTISED_1000baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseT_Full); if (pf->hw_features & I40E_HW_100M_SGMII_CAPABLE) { - supported |= SUPPORTED_100baseT_Full; + ethtool_link_ksettings_add_link_mode(ks, supported, + 100baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) - advertising |= ADVERTISED_100baseT_Full; + ethtool_link_ksettings_add_link_mode( + ks, advertising, 100baseT_Full); } break; case I40E_PHY_TYPE_40GBASE_KR4: + case I40E_PHY_TYPE_25GBASE_KR: case I40E_PHY_TYPE_20GBASE_KR2: case I40E_PHY_TYPE_10GBASE_KR: case I40E_PHY_TYPE_10GBASE_KX4: case I40E_PHY_TYPE_1000BASE_KX: - supported |= SUPPORTED_40000baseKR4_Full | - SUPPORTED_20000baseKR2_Full | - SUPPORTED_10000baseKR_Full | - SUPPORTED_10000baseKX4_Full | - SUPPORTED_1000baseKX_Full | - SUPPORTED_Autoneg; - advertising |= ADVERTISED_40000baseKR4_Full | - ADVERTISED_20000baseKR2_Full | - ADVERTISED_10000baseKR_Full | - ADVERTISED_10000baseKX4_Full | - ADVERTISED_1000baseKX_Full | - ADVERTISED_Autoneg; + ethtool_link_ksettings_add_link_mode(ks, supported, + 40000baseKR4_Full); +#ifdef HAVE_ETHTOOL_25G_BITS + ethtool_link_ksettings_add_link_mode(ks, supported, + 25000baseKR_Full); +#endif /* HAVE_ETHTOOL_25G_BITS */ + ethtool_link_ksettings_add_link_mode(ks, supported, + 20000baseKR2_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseKR_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseKX4_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, + 1000baseKX_Full); + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 40000baseKR4_Full); +#ifdef HAVE_ETHTOOL_25G_BITS + ethtool_link_ksettings_add_link_mode(ks, advertising, + 25000baseKR_Full); +#ifdef ETHTOOL_GFECPARAM + i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks); +#endif /* ETHTOOL_GFECPARAM */ +#endif /* HAVE_ETHTOOL_25G_BITS */ + ethtool_link_ksettings_add_link_mode(ks, advertising, + 20000baseKR2_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseKR_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseKX4_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 1000baseKX_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); break; - case I40E_PHY_TYPE_25GBASE_KR: case I40E_PHY_TYPE_25GBASE_CR: - case I40E_PHY_TYPE_25GBASE_SR: - case I40E_PHY_TYPE_25GBASE_LR: - supported = SUPPORTED_Autoneg; - advertising = ADVERTISED_Autoneg; - /* TODO: add speeds when ethtool is ready to support*/ + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); +#ifdef HAVE_ETHTOOL_25G_BITS + ethtool_link_ksettings_add_link_mode(ks, supported, + 25000baseCR_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 25000baseCR_Full); +#ifdef ETHTOOL_GFECPARAM + i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks); +#endif /* ETHTOOL_GFECPARAM */ + +#endif /* HAVE_ETHTOOL_25G_BITS */ + break; + case I40E_PHY_TYPE_25GBASE_AOC: + case I40E_PHY_TYPE_25GBASE_ACC: + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); +#ifdef HAVE_ETHTOOL_25G_BITS + ethtool_link_ksettings_add_link_mode(ks, supported, + 25000baseCR_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 25000baseCR_Full); +#ifdef ETHTOOL_GFECPARAM + i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks); +#endif /* ETHTOOL_GFECPARAM */ + +#endif /* HAVE_ETHTOOL_25G_BITS */ +#ifdef HAVE_ETHTOOL_NEW_10G_BITS + ethtool_link_ksettings_add_link_mode(ks, supported, + 10000baseCR_Full); + ethtool_link_ksettings_add_link_mode(ks, advertising, + 10000baseCR_Full); +#endif /* HAVE_ETHTOOL_NEW_10G_BITS */ break; default: /* if we got here and link is up something bad is afoot */ - netdev_info(netdev, "WARNING: Link is up but PHY type 0x%x is not recognized.\n", + netdev_info(netdev, + "WARNING: Link is up but PHY type 0x%x is not recognized.\n", hw_link_info->phy_type); } /* Now that we've worked out everything that could be supported by the - * current PHY type, get what is supported by the NVM and them to - * get what is truly supported + * current PHY type, get what is supported by the NVM and intersect + * them to get what is truly supported */ - i40e_phy_type_to_ethtool(pf, &e_supported, - &e_advertising); - - supported = supported & e_supported; - advertising = advertising & e_advertising; + memset(&cap_ksettings, 0, sizeof(struct ethtool_link_ksettings)); + i40e_phy_type_to_ethtool(pf, &cap_ksettings); + ethtool_intersect_link_masks(ks, &cap_ksettings); /* Set speed and duplex */ switch (link_speed) { case I40E_LINK_SPEED_40GB: - cmd->base.speed = SPEED_40000; + ks->base.speed = SPEED_40000; break; case I40E_LINK_SPEED_25GB: -#ifdef SPEED_25000 - cmd->base.speed = SPEED_25000; -#else - netdev_info(netdev, - "Speed is 25G, display not supported by this version of ethtool.\n"); -#endif + ks->base.speed = SPEED_25000; break; case I40E_LINK_SPEED_20GB: - cmd->base.speed = SPEED_20000; + ks->base.speed = SPEED_20000; break; case I40E_LINK_SPEED_10GB: - cmd->base.speed = SPEED_10000; + ks->base.speed = SPEED_10000; + break; + case I40E_LINK_SPEED_5GB: + ks->base.speed = SPEED_5000; + break; + case I40E_LINK_SPEED_2_5GB: + ks->base.speed = SPEED_2500; break; case I40E_LINK_SPEED_1GB: - cmd->base.speed = SPEED_1000; + ks->base.speed = SPEED_1000; break; case I40E_LINK_SPEED_100MB: - cmd->base.speed = SPEED_100; + ks->base.speed = SPEED_100; break; default: + ks->base.speed = SPEED_UNKNOWN; break; } - cmd->base.duplex = DUPLEX_FULL; - - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, - supported); - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, - advertising); + ks->base.duplex = DUPLEX_FULL; } /** - * i40e_get_settings_link_down - Get the Link settings for when link is down + * i40e_get_settings_link_down - Get the Link settings when link is down * @hw: hw structure - * @ecmd: ethtool command to fill in + * @ks: ethtool ksettings to fill in + * @pf: pointer to physical function struct * * Reports link settings that can be determined when link is down **/ static void i40e_get_settings_link_down(struct i40e_hw *hw, - struct ethtool_link_ksettings *cmd, + struct ethtool_link_ksettings *ks, struct i40e_pf *pf) { - u32 supported, advertising; - /* link is down and the driver needs to fall back on * supported phy types to figure out what info to display */ - i40e_phy_type_to_ethtool(pf, &supported, &advertising); - - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, - supported); - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, - advertising); - + i40e_phy_type_to_ethtool(pf, ks); /* With no link speed and duplex are unknown */ - cmd->base.speed = SPEED_UNKNOWN; - cmd->base.duplex = DUPLEX_UNKNOWN; + ks->base.speed = SPEED_UNKNOWN; + ks->base.duplex = DUPLEX_UNKNOWN; } /** - * i40e_get_settings - Get Link Speed and Duplex settings + * i40e_get_link_ksettings - Get Link Speed and Duplex settings * @netdev: network interface device structure - * @ecmd: ethtool command + * @ks: ethtool ksettings * * Reports speed/duplex settings based on media_type **/ static int i40e_get_link_ksettings(struct net_device *netdev, - struct ethtool_link_ksettings *cmd) + struct ethtool_link_ksettings *ks) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; struct i40e_hw *hw = &pf->hw; struct i40e_link_status *hw_link_info = &hw->phy.link_info; bool link_up = hw_link_info->link_info & I40E_AQ_LINK_UP; - u32 advertising; + + ethtool_link_ksettings_zero_link_mode(ks, supported); + ethtool_link_ksettings_zero_link_mode(ks, advertising); if (link_up) - i40e_get_settings_link_up(hw, cmd, netdev, pf); + i40e_get_settings_link_up(hw, ks, netdev, pf); else - i40e_get_settings_link_down(hw, cmd, pf); + i40e_get_settings_link_down(hw, ks, pf); /* Now set the settings that don't rely on link being up/down */ /* Set autoneg settings */ - cmd->base.autoneg = ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ? - AUTONEG_ENABLE : AUTONEG_DISABLE); + ks->base.autoneg = (hw_link_info->an_info & I40E_AQ_AN_COMPLETED ? + AUTONEG_ENABLE : AUTONEG_DISABLE); + /* Set media type settings */ switch (hw->phy.media_type) { case I40E_MEDIA_TYPE_BACKPLANE: - ethtool_link_ksettings_add_link_mode(cmd, supported, - Autoneg); - ethtool_link_ksettings_add_link_mode(cmd, supported, + ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, supported, Backplane); + ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); + ethtool_link_ksettings_add_link_mode(ks, advertising, Backplane); - ethtool_link_ksettings_add_link_mode(cmd, advertising, - Autoneg); - ethtool_link_ksettings_add_link_mode(cmd, advertising, - Backplane); - cmd->base.port = PORT_NONE; + ks->base.port = PORT_NONE; break; case I40E_MEDIA_TYPE_BASET: - ethtool_link_ksettings_add_link_mode(cmd, supported, TP); - ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); - cmd->base.port = PORT_TP; + ethtool_link_ksettings_add_link_mode(ks, supported, TP); + ethtool_link_ksettings_add_link_mode(ks, advertising, TP); + ks->base.port = PORT_TP; break; case I40E_MEDIA_TYPE_DA: case I40E_MEDIA_TYPE_CX4: - ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); - ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); - cmd->base.port = PORT_DA; + ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE); + ethtool_link_ksettings_add_link_mode(ks, advertising, FIBRE); + ks->base.port = PORT_DA; break; case I40E_MEDIA_TYPE_FIBER: - ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); - cmd->base.port = PORT_FIBRE; + ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE); + ethtool_link_ksettings_add_link_mode(ks, advertising, FIBRE); + ks->base.port = PORT_FIBRE; break; case I40E_MEDIA_TYPE_UNKNOWN: default: - cmd->base.port = PORT_OTHER; + ks->base.port = PORT_OTHER; break; } /* Set flow control settings */ - ethtool_link_ksettings_add_link_mode(cmd, supported, Pause); + ethtool_link_ksettings_add_link_mode(ks, supported, Pause); switch (hw->fc.requested_mode) { case I40E_FC_FULL: - ethtool_link_ksettings_add_link_mode(cmd, advertising, - Pause); + ethtool_link_ksettings_add_link_mode(ks, advertising, Pause); break; case I40E_FC_TX_PAUSE: - ethtool_link_ksettings_add_link_mode(cmd, advertising, + ethtool_link_ksettings_add_link_mode(ks, advertising, Asym_Pause); break; case I40E_FC_RX_PAUSE: - ethtool_link_ksettings_add_link_mode(cmd, advertising, - Pause); - ethtool_link_ksettings_add_link_mode(cmd, advertising, + ethtool_link_ksettings_add_link_mode(ks, advertising, Pause); + ethtool_link_ksettings_add_link_mode(ks, advertising, Asym_Pause); break; default: - ethtool_convert_link_mode_to_legacy_u32( - &advertising, cmd->link_modes.advertising); + ethtool_link_ksettings_del_link_mode(ks, advertising, Pause); + ethtool_link_ksettings_del_link_mode(ks, advertising, + Asym_Pause); + break; + } + return 0; +} - advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); +#ifdef ETHTOOL_GLINKSETTINGS +/** + * i40e_set_link_ksettings - Set Speed and Duplex + * @netdev: network interface device structure + * @ks: ethtool ksettings + * + * Set speed/duplex per media_types advertised/forced + **/ +static int i40e_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *ks) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_aq_get_phy_abilities_resp abilities; + struct ethtool_link_ksettings safe_ks; + struct ethtool_link_ksettings copy_ks; + struct i40e_aq_set_phy_config config; + struct i40e_pf *pf = np->vsi->back; + struct i40e_vsi *vsi = np->vsi; + struct i40e_hw *hw = &pf->hw; + bool autoneg_changed = false; + i40e_status status = 0; + int timeout = 50; + int err = 0; + u8 autoneg; - ethtool_convert_legacy_u32_to_link_mode( - cmd->link_modes.advertising, advertising); - break; + /* Changing port settings is not supported if this isn't the + * port's controlling PF + */ + if (hw->partition_id != 1) { + i40e_partition_setting_complaint(pf); + return -EOPNOTSUPP; + } + if (vsi != pf->vsi[pf->lan_vsi]) + return -EOPNOTSUPP; + if (hw->phy.media_type != I40E_MEDIA_TYPE_BASET && + hw->phy.media_type != I40E_MEDIA_TYPE_FIBER && + hw->phy.media_type != I40E_MEDIA_TYPE_BACKPLANE && + hw->phy.media_type != I40E_MEDIA_TYPE_DA && + hw->phy.link_info.link_info & I40E_AQ_LINK_UP) + return -EOPNOTSUPP; + if (hw->device_id == I40E_DEV_ID_KX_B || + hw->device_id == I40E_DEV_ID_KX_C || + hw->device_id == I40E_DEV_ID_20G_KR2 || + hw->device_id == I40E_DEV_ID_20G_KR2_A || + hw->device_id == I40E_DEV_ID_25G_B || + hw->device_id == I40E_DEV_ID_KX_X722) { + netdev_info(netdev, "Changing settings is not supported on backplane.\n"); + return -EOPNOTSUPP; + } + + /* copy the ksettings to copy_ks to avoid modifying the origin */ + memcpy(©_ks, ks, sizeof(struct ethtool_link_ksettings)); + + /* save autoneg out of ksettings */ + autoneg = copy_ks.base.autoneg; + + /* get our own copy of the bits to check against */ + memset(&safe_ks, 0, sizeof(struct ethtool_link_ksettings)); + safe_ks.base.cmd = copy_ks.base.cmd; + safe_ks.base.link_mode_masks_nwords = + copy_ks.base.link_mode_masks_nwords; + i40e_get_link_ksettings(netdev, &safe_ks); + + /* Get link modes supported by hardware and check against modes + * requested by user. Return an error if unsupported mode was set. + */ + if (!bitmap_subset(copy_ks.link_modes.advertising, + safe_ks.link_modes.supported, + __ETHTOOL_LINK_MODE_MASK_NBITS)) + return -EINVAL; + + /* set autoneg back to what it currently is */ + copy_ks.base.autoneg = safe_ks.base.autoneg; + + /* If copy_ks.base and safe_ks.base are not the same now, then they are + * trying to set something that we do not support. + */ + if (memcmp(©_ks.base, &safe_ks.base, + sizeof(struct ethtool_link_settings))) + return -EOPNOTSUPP; + + while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) { + timeout--; + if (!timeout) + return -EBUSY; + usleep_range(1000, 2000); + } + + /* Get the current phy config */ + status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, + NULL); + if (status) { + err = -EAGAIN; + goto done; + } + + /* Copy abilities to config in case autoneg is not + * set below + */ + memset(&config, 0, sizeof(struct i40e_aq_set_phy_config)); + config.abilities = abilities.abilities; + + /* Check autoneg */ + if (autoneg == AUTONEG_ENABLE) { + /* If autoneg was not already enabled */ + if (!(hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED)) { + /* If autoneg is not supported, return error */ + if (!ethtool_link_ksettings_test_link_mode( + &safe_ks, supported, Autoneg)) { + netdev_info(netdev, "Autoneg not supported on this phy\n"); + err = -EINVAL; + goto done; + } + /* Autoneg is allowed to change */ + config.abilities = abilities.abilities | + I40E_AQ_PHY_ENABLE_AN; + autoneg_changed = true; + } + } else { + /* If autoneg is currently enabled */ + if (hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED) { + /* If autoneg is supported 10GBASE_T is the only PHY + * that can disable it, so otherwise return error + */ + if (ethtool_link_ksettings_test_link_mode( + &safe_ks, supported, Autoneg) && + hw->phy.link_info.phy_type != + I40E_PHY_TYPE_10GBASE_T) { + netdev_info(netdev, "Autoneg cannot be disabled on this phy\n"); + err = -EINVAL; + goto done; + } + /* Autoneg is allowed to change */ + config.abilities = abilities.abilities & + ~I40E_AQ_PHY_ENABLE_AN; + autoneg_changed = true; + } + } + + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 100baseT_Full)) + config.link_speed |= I40E_LINK_SPEED_100MB; + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 1000baseT_Full) || +#ifdef HAVE_ETHTOOL_NEW_10G_BITS + ethtool_link_ksettings_test_link_mode(ks, advertising, + 1000baseX_Full) || +#endif + ethtool_link_ksettings_test_link_mode(ks, advertising, + 1000baseKX_Full)) + config.link_speed |= I40E_LINK_SPEED_1GB; + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 10000baseT_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 10000baseKX4_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 10000baseKR_Full) || +#ifdef HAVE_ETHTOOL_NEW_10G_BITS + ethtool_link_ksettings_test_link_mode(ks, advertising, + 10000baseCR_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 10000baseSR_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 10000baseLR_Full)) +#else + 0) +#endif /* HAVE_ETHTOOL_NEW_10G_BITS */ + config.link_speed |= I40E_LINK_SPEED_10GB; +#ifdef HAVE_ETHTOOL_NEW_2500MB_BITS + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 2500baseT_Full)) + config.link_speed |= I40E_LINK_SPEED_2_5GB; +#endif /* HAVE_ETHTOOL_NEW_2500MB_BITS */ +#ifdef HAVE_ETHTOOL_5G_BITS + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 5000baseT_Full)) + config.link_speed |= I40E_LINK_SPEED_5GB; +#endif /* HAVE_ETHTOOL_5G_BITS */ + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 20000baseKR2_Full)) + config.link_speed |= I40E_LINK_SPEED_20GB; +#ifdef HAVE_ETHTOOL_25G_BITS + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 25000baseCR_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 25000baseKR_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 25000baseSR_Full)) + config.link_speed |= I40E_LINK_SPEED_25GB; +#endif /* HAVE_ETHTOOL_25G_BITS */ + if (ethtool_link_ksettings_test_link_mode(ks, advertising, + 40000baseKR4_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 40000baseCR4_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 40000baseSR4_Full) || + ethtool_link_ksettings_test_link_mode(ks, advertising, + 40000baseLR4_Full)) + config.link_speed |= I40E_LINK_SPEED_40GB; + + /* If speed didn't get set, set it to what it currently is. + * This is needed because if advertise is 0 (as it is when autoneg + * is disabled) then speed won't get set. + */ + if (!config.link_speed) + config.link_speed = abilities.link_speed; + if (autoneg_changed || (abilities.link_speed != config.link_speed)) { + /* copy over the rest of the abilities */ + config.phy_type = abilities.phy_type; + config.phy_type_ext = abilities.phy_type_ext; + config.eee_capability = abilities.eee_capability; + config.eeer = abilities.eeer_val; + config.low_power_ctrl = abilities.d3_lpan; + config.fec_config = abilities.fec_cfg_curr_mod_ext_info & + I40E_AQ_PHY_FEC_CONFIG_MASK; + + /* save the requested speeds */ + hw->phy.link_info.requested_speeds = config.link_speed; + /* set link and auto negotiation so changes take effect */ + config.abilities |= I40E_AQ_PHY_ENABLE_ATOMIC_LINK; + /* If link is up put link down */ + if (hw->phy.link_info.link_info & I40E_AQ_LINK_UP) { + /* Tell the OS link is going down, the link will go + * back up when fw says it is ready asynchronously + */ + i40e_print_link_message(vsi, false); + netif_carrier_off(netdev); + netif_tx_stop_all_queues(netdev); + } + + /* make the aq call */ + status = i40e_aq_set_phy_config(hw, &config, NULL); + if (status) { + netdev_info(netdev, + "Set phy config failed, err %s aq_err %s\n", + i40e_stat_str(hw, status), + i40e_aq_str(hw, hw->aq.asq_last_status)); + err = -EAGAIN; + goto done; + } + + status = i40e_update_link_info(hw); + if (status) + netdev_dbg(netdev, + "Updating link info failed with err %s aq_err %s\n", + i40e_stat_str(hw, status), + i40e_aq_str(hw, hw->aq.asq_last_status)); + + } else { + netdev_info(netdev, "Nothing changed, exiting without setting anything.\n"); } +done: + clear_bit(__I40E_CONFIG_BUSY, pf->state); + + return err; +} + +#else /* ETHTOOL_GLINKSETTINGS */ +/** + * i40e_get_settings - Get Link Speed and Duplex settings + * @netdev: network interface device structure + * @ecmd: ethtool command + * + * Reports speed/duplex settings based on media_type. Since we've backported + * the new API constructs to use in the old API, this ends up just being a + * wrapper to i40e_get_link_ksettings. + **/ +static int i40e_get_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct ethtool_link_ksettings ks; + + i40e_get_link_ksettings(netdev, &ks); + _kc_ethtool_ksettings_to_cmd(&ks, ecmd); + ecmd->transceiver = XCVR_EXTERNAL; return 0; } @@ -686,8 +1332,8 @@ static int i40e_get_link_ksettings(struct net_device *netdev, * * Set speed/duplex per media_types advertised/forced **/ -static int i40e_set_link_ksettings(struct net_device *netdev, - const struct ethtool_link_ksettings *cmd) +static int i40e_set_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_aq_get_phy_abilities_resp abilities; @@ -695,15 +1341,14 @@ static int i40e_set_link_ksettings(struct net_device *netdev, struct i40e_pf *pf = np->vsi->back; struct i40e_vsi *vsi = np->vsi; struct i40e_hw *hw = &pf->hw; - struct ethtool_link_ksettings safe_cmd; - struct ethtool_link_ksettings copy_cmd; + struct ethtool_cmd safe_ecmd; i40e_status status = 0; bool change = false; int timeout = 50; int err = 0; - u32 autoneg; + u8 autoneg; u32 advertise; - u32 tmp; + u32 old_ethtool_advertising = 0; /* Changing port settings is not supported if this isn't the * port's controlling PF @@ -731,31 +1376,40 @@ static int i40e_set_link_ksettings(struct net_device *netdev, return -EOPNOTSUPP; } - /* copy the cmd to copy_cmd to avoid modifying the origin */ - memcpy(©_cmd, cmd, sizeof(struct ethtool_link_ksettings)); - /* get our own copy of the bits to check against */ - memset(&safe_cmd, 0, sizeof(struct ethtool_link_ksettings)); - i40e_get_link_ksettings(netdev, &safe_cmd); + memset(&safe_ecmd, 0, sizeof(struct ethtool_cmd)); + i40e_get_settings(netdev, &safe_ecmd); - /* save autoneg and speed out of cmd */ - autoneg = cmd->base.autoneg; - ethtool_convert_link_mode_to_legacy_u32(&advertise, - cmd->link_modes.advertising); + /* save autoneg and speed out of ecmd */ + autoneg = ecmd->autoneg; + advertise = ecmd->advertising; /* set autoneg and speed back to what they currently are */ - copy_cmd.base.autoneg = safe_cmd.base.autoneg; - ethtool_convert_link_mode_to_legacy_u32( - &tmp, safe_cmd.link_modes.advertising); - ethtool_convert_legacy_u32_to_link_mode( - copy_cmd.link_modes.advertising, tmp); - - copy_cmd.base.cmd = safe_cmd.base.cmd; - - /* If copy_cmd and safe_cmd are not the same now, then they are + ecmd->autoneg = safe_ecmd.autoneg; + ecmd->advertising = safe_ecmd.advertising; + + /* Due to a bug in ethtool versions < 3.6 this check is necessary */ + old_ethtool_advertising = ecmd->supported & + (ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full | + ADVERTISED_2500baseX_Full | + ADVERTISED_10000baseT_Full); + old_ethtool_advertising |= (old_ethtool_advertising | + ADVERTISED_20000baseMLD2_Full | + ADVERTISED_20000baseKR2_Full); + + if (advertise == old_ethtool_advertising) + netdev_info(netdev, "If you are not setting advertising to %x then you may have an old version of ethtool. Please update.\n", + advertise); + ecmd->cmd = safe_ecmd.cmd; + /* If ecmd and safe_ecmd are not the same now, then they are * trying to set something that we do not support */ - if (memcmp(©_cmd, &safe_cmd, sizeof(struct ethtool_link_ksettings))) + if (memcmp(ecmd, &safe_ecmd, sizeof(struct ethtool_cmd))) return -EOPNOTSUPP; while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) { @@ -784,8 +1438,7 @@ static int i40e_set_link_ksettings(struct net_device *netdev, /* If autoneg was not already enabled */ if (!(hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED)) { /* If autoneg is not supported, return error */ - if (!ethtool_link_ksettings_test_link_mode( - &safe_cmd, supported, Autoneg)) { + if (!(safe_ecmd.supported & SUPPORTED_Autoneg)) { netdev_info(netdev, "Autoneg not supported on this phy\n"); err = -EINVAL; goto done; @@ -798,11 +1451,10 @@ static int i40e_set_link_ksettings(struct net_device *netdev, } else { /* If autoneg is currently enabled */ if (hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED) { - /* If autoneg is supported 10GBASE_T is the only PHY + /* If autoneg is supported 10GBASE_T is the only phy * that can disable it, so otherwise return error */ - if (ethtool_link_ksettings_test_link_mode( - &safe_cmd, supported, Autoneg) && + if (safe_ecmd.supported & SUPPORTED_Autoneg && hw->phy.link_info.phy_type != I40E_PHY_TYPE_10GBASE_T) { netdev_info(netdev, "Autoneg cannot be disabled on this phy\n"); @@ -816,9 +1468,7 @@ static int i40e_set_link_ksettings(struct net_device *netdev, } } - ethtool_convert_link_mode_to_legacy_u32(&tmp, - safe_cmd.link_modes.supported); - if (advertise & ~tmp) { + if (advertise & ~safe_ecmd.supported) { err = -EINVAL; goto done; } @@ -847,7 +1497,7 @@ static int i40e_set_link_ksettings(struct net_device *netdev, if (!config.link_speed) config.link_speed = abilities.link_speed; - if (change || (abilities.link_speed != config.link_speed)) { + if (change || abilities.link_speed != config.link_speed) { /* copy over the rest of the abilities */ config.phy_type = abilities.phy_type; config.phy_type_ext = abilities.phy_type_ext; @@ -897,6 +1547,157 @@ done: return err; } +#endif /* ETHTOOL_GLINKSETTINGS */ + +static int i40e_set_fec_cfg(struct net_device *netdev, u8 fec_cfg) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_aq_get_phy_abilities_resp abilities; + struct i40e_pf *pf = np->vsi->back; + struct i40e_hw *hw = &pf->hw; + i40e_status status = 0; + u64 flags = 0; + int err = 0; + + flags = READ_ONCE(pf->flags); + i40e_set_fec_in_flags(fec_cfg, &flags); + + /* Get the current phy config */ + memset(&abilities, 0, sizeof(abilities)); + status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, + NULL); + if (status) { + err = -EAGAIN; + goto done; + } + + if (abilities.fec_cfg_curr_mod_ext_info != fec_cfg) { + struct i40e_aq_set_phy_config config; + + memset(&config, 0, sizeof(config)); + config.phy_type = abilities.phy_type; + config.abilities = abilities.abilities; + config.phy_type_ext = abilities.phy_type_ext; + config.link_speed = abilities.link_speed; + config.eee_capability = abilities.eee_capability; + config.eeer = abilities.eeer_val; + config.low_power_ctrl = abilities.d3_lpan; + config.fec_config = fec_cfg & I40E_AQ_PHY_FEC_CONFIG_MASK; + status = i40e_aq_set_phy_config(hw, &config, NULL); + if (status) { + netdev_info(netdev, + "Set phy config failed, err %s aq_err %s\n", + i40e_stat_str(hw, status), + i40e_aq_str(hw, hw->aq.asq_last_status)); + err = -EAGAIN; + goto done; + } + pf->flags = flags; + status = i40e_update_link_info(hw); + if (status) + /* debug level message only due to relation to the link + * itself rather than to the FEC settings + * (e.g. no physical connection etc.) + */ + netdev_dbg(netdev, + "Updating link info failed with err %s aq_err %s\n", + i40e_stat_str(hw, status), + i40e_aq_str(hw, hw->aq.asq_last_status)); + } + +done: + return err; +} + +#ifdef ETHTOOL_GFECPARAM +static int i40e_get_fec_param(struct net_device *netdev, + struct ethtool_fecparam *fecparam) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_aq_get_phy_abilities_resp abilities; + struct i40e_pf *pf = np->vsi->back; + struct i40e_hw *hw = &pf->hw; + i40e_status status = 0; + int err = 0; + u8 fec_cfg; + + /* Get the current phy config */ + memset(&abilities, 0, sizeof(abilities)); + status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, + NULL); + if (status) { + err = -EAGAIN; + goto done; + } + + fecparam->fec = 0; + fec_cfg = abilities.fec_cfg_curr_mod_ext_info; + if (fec_cfg & I40E_AQ_SET_FEC_AUTO) + fecparam->fec |= ETHTOOL_FEC_AUTO; + else if (fec_cfg & (I40E_AQ_SET_FEC_REQUEST_RS | + I40E_AQ_SET_FEC_ABILITY_RS)) + fecparam->fec |= ETHTOOL_FEC_RS; + else if (fec_cfg & (I40E_AQ_SET_FEC_REQUEST_KR | + I40E_AQ_SET_FEC_ABILITY_KR)) + fecparam->fec |= ETHTOOL_FEC_BASER; + if (fec_cfg == 0) + fecparam->fec |= ETHTOOL_FEC_OFF; + + if (hw->phy.link_info.fec_info & I40E_AQ_CONFIG_FEC_KR_ENA) + fecparam->active_fec = ETHTOOL_FEC_BASER; + else if (hw->phy.link_info.fec_info & I40E_AQ_CONFIG_FEC_RS_ENA) + fecparam->active_fec = ETHTOOL_FEC_RS; + else + fecparam->active_fec = ETHTOOL_FEC_OFF; +done: + return err; +} + +static int i40e_set_fec_param(struct net_device *netdev, + struct ethtool_fecparam *fecparam) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_pf *pf = np->vsi->back; + struct i40e_hw *hw = &pf->hw; + u8 fec_cfg = 0; + int err = 0; + + if (hw->device_id != I40E_DEV_ID_25G_SFP28 && + hw->device_id != I40E_DEV_ID_25G_B) { + err = -EPERM; + goto done; + } + + switch (fecparam->fec) { + case ETHTOOL_FEC_AUTO: + fec_cfg = I40E_AQ_SET_FEC_AUTO; + break; + case ETHTOOL_FEC_RS: + fec_cfg = (I40E_AQ_SET_FEC_REQUEST_RS | + I40E_AQ_SET_FEC_ABILITY_RS); + break; + case ETHTOOL_FEC_BASER: + fec_cfg = (I40E_AQ_SET_FEC_REQUEST_KR | + I40E_AQ_SET_FEC_ABILITY_KR); + break; + case ETHTOOL_FEC_OFF: + case ETHTOOL_FEC_NONE: + fec_cfg = 0; + break; + default: + dev_warn(&pf->pdev->dev, "Unsupported FEC mode: %d", + fecparam->fec); + err = -EINVAL; + goto done; + } + + err = i40e_set_fec_cfg(netdev, fec_cfg); + +done: + return err; +} +#endif /* ETHTOOL_GFECPARAM */ + static int i40e_nway_reset(struct net_device *netdev) { /* restart autonegotiation */ @@ -919,6 +1720,9 @@ static int i40e_nway_reset(struct net_device *netdev) /** * i40e_get_pauseparam - Get Flow Control status + * @netdev: netdevice structure + * @pause: buffer to return pause parameters + * * Return tx/rx-pause status **/ static void i40e_get_pauseparam(struct net_device *netdev, @@ -927,12 +1731,9 @@ static void i40e_get_pauseparam(struct net_device *netdev, struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; struct i40e_hw *hw = &pf->hw; - struct i40e_link_status *hw_link_info = &hw->phy.link_info; struct i40e_dcbx_config *dcbx_cfg = &hw->local_dcbx_config; - pause->autoneg = - ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ? - AUTONEG_ENABLE : AUTONEG_DISABLE); + pause->autoneg = hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED; /* PFC enabled so report LFC as off */ if (dcbx_cfg->pfc.pfcenable) { @@ -969,6 +1770,7 @@ static int i40e_set_pauseparam(struct net_device *netdev, i40e_status status; u8 aq_failures; int err = 0; + u32 is_an; /* Changing the port's flow control is not supported if this isn't the * port's controlling PF @@ -981,22 +1783,21 @@ static int i40e_set_pauseparam(struct net_device *netdev, if (vsi != pf->vsi[pf->lan_vsi]) return -EOPNOTSUPP; - if (pause->autoneg != ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ? - AUTONEG_ENABLE : AUTONEG_DISABLE)) { + is_an = hw_link_info->an_info & I40E_AQ_AN_COMPLETED; + if (pause->autoneg != is_an) { netdev_info(netdev, "To change autoneg please use: ethtool -s autoneg \n"); return -EOPNOTSUPP; } /* If we have link and don't have autoneg */ - if (!test_bit(__I40E_DOWN, pf->state) && - !(hw_link_info->an_info & I40E_AQ_AN_COMPLETED)) { + if (!test_bit(__I40E_DOWN, pf->state) && !is_an) { /* Send message that it might not necessarily work*/ netdev_info(netdev, "Autoneg did not complete so changing settings may not result in an actual change.\n"); } if (dcbx_cfg->pfc.pfcenable) { netdev_info(netdev, - "Priority flow control enabled. Cannot set link flow control.\n"); + "Priority flow control enabled. Cannot set link flow control.\n"); return -EOPNOTSUPP; } @@ -1009,7 +1810,7 @@ static int i40e_set_pauseparam(struct net_device *netdev, else if (!pause->rx_pause && !pause->tx_pause) hw->fc.requested_mode = I40E_FC_NONE; else - return -EINVAL; + return -EINVAL; /* Tell the OS link is going down, the link will go back up when fw * says it is ready asynchronously @@ -1018,46 +1819,165 @@ static int i40e_set_pauseparam(struct net_device *netdev, netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); - /* Set the fc mode and only restart an if link is up*/ - status = i40e_set_fc(hw, &aq_failures, link_up); + /* Set the fc mode and only restart an if link is up*/ + status = i40e_set_fc(hw, &aq_failures, link_up); + + if (aq_failures & I40E_SET_FC_AQ_FAIL_GET) { + netdev_info(netdev, "Set fc failed on the get_phy_capabilities call with err %s aq_err %s\n", + i40e_stat_str(hw, status), + i40e_aq_str(hw, hw->aq.asq_last_status)); + err = -EAGAIN; + } + if (aq_failures & I40E_SET_FC_AQ_FAIL_SET) { + netdev_info(netdev, "Set fc failed on the set_phy_config call with err %s aq_err %s\n", + i40e_stat_str(hw, status), + i40e_aq_str(hw, hw->aq.asq_last_status)); + err = -EAGAIN; + } + if (aq_failures & I40E_SET_FC_AQ_FAIL_UPDATE) { + netdev_info(netdev, "Set fc failed on the get_link_info call with err %s aq_err %s\n", + i40e_stat_str(hw, status), + i40e_aq_str(hw, hw->aq.asq_last_status)); + err = -EAGAIN; + } + + if (!test_bit(__I40E_DOWN, pf->state) && is_an) { + /* Give it a little more time to try to come back */ + msleep(75); + if (!test_bit(__I40E_DOWN, pf->state)) + return i40e_nway_reset(netdev); + } + + return err; +} + +#ifndef HAVE_NDO_SET_FEATURES +static u32 i40e_get_rx_csum(struct net_device *netdev) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_pf *pf = np->vsi->back; + + return pf->flags & I40E_FLAG_RX_CSUM_ENABLED; +} + +static int i40e_set_rx_csum(struct net_device *netdev, u32 data) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_pf *pf = np->vsi->back; + + if (data) + pf->flags |= I40E_FLAG_RX_CSUM_ENABLED; + else + pf->flags &= ~I40E_FLAG_RX_CSUM_ENABLED; + + return 0; +} + +static u32 i40e_get_tx_csum(struct net_device *netdev) +{ + return (netdev->features & NETIF_F_IP_CSUM) != 0; +} + +static int i40e_set_tx_csum(struct net_device *netdev, u32 data) +{ + if (data) { +#ifdef NETIF_F_IPV6_CSUM + netdev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; +#else + netdev->features |= NETIF_F_IP_CSUM; +#endif + netdev->features |= NETIF_F_SCTP_CRC; + } else { +#ifdef NETIF_F_IPV6_CSUM + netdev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_SCTP_CRC); +#else + netdev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_SCTP_CRC); +#endif + } + + return 0; +} - if (aq_failures & I40E_SET_FC_AQ_FAIL_GET) { - netdev_info(netdev, "Set fc failed on the get_phy_capabilities call with err %s aq_err %s\n", - i40e_stat_str(hw, status), - i40e_aq_str(hw, hw->aq.asq_last_status)); - err = -EAGAIN; - } - if (aq_failures & I40E_SET_FC_AQ_FAIL_SET) { - netdev_info(netdev, "Set fc failed on the set_phy_config call with err %s aq_err %s\n", - i40e_stat_str(hw, status), - i40e_aq_str(hw, hw->aq.asq_last_status)); - err = -EAGAIN; - } - if (aq_failures & I40E_SET_FC_AQ_FAIL_UPDATE) { - netdev_info(netdev, "Set fc failed on the get_link_info call with err %s aq_err %s\n", - i40e_stat_str(hw, status), - i40e_aq_str(hw, hw->aq.asq_last_status)); - err = -EAGAIN; +static int i40e_set_tso(struct net_device *netdev, u32 data) +{ + if (data) { +#ifndef HAVE_NDO_FEATURES_CHECK + if (netdev->mtu >= 576) { + netdev->features |= NETIF_F_TSO; + netdev->features |= NETIF_F_TSO6; + } else { + netdev_info(netdev, "MTU setting is too low to enable TSO\n"); + } +#else + netdev->features |= NETIF_F_TSO; + netdev->features |= NETIF_F_TSO6; +#endif + } else { +#ifndef HAVE_NETDEV_VLAN_FEATURES + struct i40e_netdev_priv *np = netdev_priv(netdev); + /* disable TSO on all VLANs if they're present */ + if (np->vsi->vlgrp) { + int i; + struct net_device *v_netdev; + + for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { + v_netdev = + vlan_group_get_device(np->vsi->vlgrp, i); + if (v_netdev) { + v_netdev->features &= ~NETIF_F_TSO; + v_netdev->features &= ~NETIF_F_TSO6; + vlan_group_set_device(np->vsi->vlgrp, i, + v_netdev); + } + } + } +#endif /* HAVE_NETDEV_VLAN_FEATURES */ + netdev->features &= ~NETIF_F_TSO; + netdev->features &= ~NETIF_F_TSO6; } - if (!test_bit(__I40E_DOWN, pf->state)) { - /* Give it a little more time to try to come back */ - msleep(75); - if (!test_bit(__I40E_DOWN, pf->state)) - return i40e_nway_reset(netdev); - } + return 0; +} +#ifdef ETHTOOL_GFLAGS +static int i40e_set_flags(struct net_device *netdev, u32 data) +{ +#ifdef ETHTOOL_GRXRINGS + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_pf *pf = np->vsi->back; +#endif + u32 supported_flags = 0; + bool need_reset = false; + int rc; - return err; +#ifdef NETIF_F_RXHASH + supported_flags |= ETH_FLAG_RXHASH; + +#endif +#ifdef ETHTOOL_GRXRINGS + if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) + supported_flags |= ETH_FLAG_NTUPLE; +#endif + rc = ethtool_op_set_flags(netdev, data, supported_flags); + if (rc) + return rc; + + /* if state changes we need to update pf->flags and maybe reset */ +#ifdef ETHTOOL_GRXRINGS + need_reset = i40e_set_ntuple(pf, netdev->features); +#endif /* ETHTOOL_GRXRINGS */ + if (need_reset) + i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED), true); + + return 0; } +#endif /* ETHTOOL_GFLAGS */ +#endif /* HAVE_NDO_SET_FEATURES */ static u32 i40e_get_msglevel(struct net_device *netdev) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; - u32 debug_mask = pf->hw.debug_mask; - - if (debug_mask) - netdev_info(netdev, "i40e debug_mask: 0x%08X\n", debug_mask); return pf->msg_enable; } @@ -1069,8 +1989,7 @@ static void i40e_set_msglevel(struct net_device *netdev, u32 data) if (I40E_DEBUG_USER & data) pf->hw.debug_mask = data; - else - pf->msg_enable = data; + pf->msg_enable = data; } static int i40e_get_regs_len(struct net_device *netdev) @@ -1277,9 +2196,11 @@ static void i40e_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(pf->pdev), sizeof(drvinfo->bus_info)); +#ifdef HAVE_ETHTOOL_GET_SSET_COUNT drvinfo->n_priv_flags = I40E_PRIV_FLAGS_STR_LEN; if (pf->hw.pf_id == 0) drvinfo->n_priv_flags += I40E_GL_PRIV_FLAGS_STR_LEN; +#endif } static void i40e_get_ringparam(struct net_device *netdev, @@ -1360,6 +2281,8 @@ static int i40e_set_ringparam(struct net_device *netdev, if (i40e_enabled_xdp_vsi(vsi)) vsi->xdp_rings[i]->count = new_tx_count; } + vsi->num_tx_desc = new_tx_count; + vsi->num_rx_desc = new_rx_count; goto done; } @@ -1373,7 +2296,7 @@ static int i40e_set_ringparam(struct net_device *netdev, (i40e_enabled_xdp_vsi(vsi) ? 2 : 1); if (new_tx_count != vsi->tx_rings[0]->count) { netdev_info(netdev, - "Changing Tx descriptor count from %d to %d.\n", + "Changing Tx descriptor count from %d to %d\n", vsi->tx_rings[0]->count, new_tx_count); tx_rings = kcalloc(tx_alloc_queue_pairs, sizeof(struct i40e_ring), GFP_KERNEL); @@ -1385,7 +2308,7 @@ static int i40e_set_ringparam(struct net_device *netdev, for (i = 0; i < tx_alloc_queue_pairs; i++) { if (!i40e_active_tx_ring_index(vsi, i)) continue; - + /* clone ring and setup updated count */ tx_rings[i] = *vsi->tx_rings[i]; tx_rings[i].count = new_tx_count; /* the desc and bi pointers will be reallocated in the @@ -1422,7 +2345,6 @@ static int i40e_set_ringparam(struct net_device *netdev, } for (i = 0; i < vsi->num_queue_pairs; i++) { - struct i40e_ring *ring; u16 unused; /* clone ring and setup updated count */ @@ -1433,6 +2355,11 @@ static int i40e_set_ringparam(struct net_device *netdev, */ rx_rings[i].desc = NULL; rx_rings[i].rx_bi = NULL; +#ifdef HAVE_XDP_BUFF_RXQ + /* Clear cloned XDP RX-queue info before setup call */ + memset(&rx_rings[i].xdp_rxq, 0, + sizeof(rx_rings[i].xdp_rxq)); +#endif /* this is to allow wr32 to have something to write to * during early allocation of Rx buffers */ @@ -1444,9 +2371,8 @@ static int i40e_set_ringparam(struct net_device *netdev, /* now allocate the Rx buffers to make sure the OS * has enough memory, any failure here means abort */ - ring = &rx_rings[i]; - unused = I40E_DESC_UNUSED(ring); - err = i40e_alloc_rx_buffers(ring, unused); + unused = I40E_DESC_UNUSED(&rx_rings[i]); + err = i40e_alloc_rx_buffers(&rx_rings[i], unused); rx_unwind: if (err) { do { @@ -1496,6 +2422,8 @@ rx_unwind: rx_rings = NULL; } + vsi->num_tx_desc = new_tx_count; + vsi->num_rx_desc = new_rx_count; i40e_up(vsi); free_tx: @@ -1515,6 +2443,67 @@ done: return err; } +/** + * i40e_get_stats_count - return the stats count for a device + * @netdev: the netdev to return the count for + * + * Returns the total number of statistics for this netdev. Note that even + * though this is a function, it is required that the count for a specific + * netdev must never change. Basing the count on static values such as the + * maximum number of queues or the device type is ok. However, the API for + * obtaining stats is *not* safe against changes based on non-static + * values such as the *current* number of queues, or runtime flags. + * + * If a statistic is not always enabled, return it as part of the count + * anyways, always return its string, and report its value as zero. + **/ +static int i40e_get_stats_count(struct net_device *netdev) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + int stats_len; + + if (vsi == pf->vsi[pf->lan_vsi] && pf->hw.partition_id == 1) + stats_len = I40E_PF_STATS_LEN; + else + stats_len = I40E_VSI_STATS_LEN; + + /* The number of stats reported for a given net_device must remain + * constant throughout the life of that device. + * + * This is because the API for obtaining the size, strings, and stats + * is spread out over three separate ethtool ioctls. There is no safe + * way to lock the number of stats across these calls, so we must + * assume that they will never change. + * + * Due to this, we report the maximum number of queues, even if not + * every queue is currently configured. Since we always allocate + * queues in pairs, we'll just use netdev->num_tx_queues * 2. This + * works because the num_tx_queues is set at device creation and never + * changes. + */ +#ifndef I40E_PF_EXTRA_STATS_OFF + /* The same applies to additional stats showing here the network usage + * counters for VFs. In order to handle it in a safe way, we also + * report here, similarly as in the queues case described above, + * the maximum possible, fixed number of these extra stats items. + */ +#endif /* !I40E_PF_EXTRA_STATS_OFF */ + stats_len += I40E_QUEUE_STATS_LEN * 2 * netdev->real_num_tx_queues; +#ifdef HAVE_XDP_SUPPORT + stats_len += I40E_QUEUE_STATS_XDP_LEN * netdev->real_num_tx_queues; +#endif + +#ifndef I40E_PF_EXTRA_STATS_OFF + if (vsi == pf->vsi[pf->lan_vsi] && pf->hw.partition_id == 1) + stats_len += I40E_PF_STATS_EXTRA_LEN; + +#endif /* !I40E_PF_EXTRA_STATS_OFF */ + return stats_len; +} + +#ifdef HAVE_ETHTOOL_GET_SSET_COUNT static int i40e_get_sset_count(struct net_device *netdev, int sset) { struct i40e_netdev_priv *np = netdev_priv(netdev); @@ -1525,16 +2514,7 @@ static int i40e_get_sset_count(struct net_device *netdev, int sset) case ETH_SS_TEST: return I40E_TEST_LEN; case ETH_SS_STATS: - if (vsi == pf->vsi[pf->lan_vsi] && pf->hw.partition_id == 1) { - int len = I40E_PF_STATS_LEN(netdev); - - if ((pf->lan_veb != I40E_NO_VEB) && - (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) - len += I40E_VEB_STATS_TOTAL; - return len; - } else { - return I40E_VSI_STATS_LEN(netdev); - } + return i40e_get_stats_count(netdev); case ETH_SS_PRIV_FLAGS: return I40E_PRIV_FLAGS_STR_LEN + (pf->hw.pf_id == 0 ? I40E_GL_PRIV_FLAGS_STR_LEN : 0); @@ -1542,96 +2522,262 @@ static int i40e_get_sset_count(struct net_device *netdev, int sset) return -EOPNOTSUPP; } } +#endif /* HAVE_ETHTOOL_GET_SSET_COUNT */ + +/** + * i40e_get_pfc_stats - copy HW PFC statistics to formatted structure + * @pf: the PF device structure + * @i: the priority value to copy + * + * The PFC stats are found as arrays in pf->stats, which is not easy to pass + * into i40e_add_ethtool_stats. Produce a formatted i40e_pfc_stats structure + * of the PFC stats for the given priority. + **/ +static inline struct i40e_pfc_stats +i40e_get_pfc_stats(struct i40e_pf *pf, unsigned int i) +{ +#define I40E_GET_PFC_STAT(stat, priority) \ + .stat = pf->stats.stat[priority] + + struct i40e_pfc_stats pfc = { + I40E_GET_PFC_STAT(priority_xon_rx, i), + I40E_GET_PFC_STAT(priority_xoff_rx, i), + I40E_GET_PFC_STAT(priority_xon_tx, i), + I40E_GET_PFC_STAT(priority_xoff_tx, i), + I40E_GET_PFC_STAT(priority_xon_2_xoff, i), + }; + return pfc; +} +/** + * i40e_get_ethtool_stats - copy stat values into supplied buffer + * @netdev: the netdev to collect stats for + * @stats: ethtool stats command structure + * @data: ethtool supplied buffer + * + * Copy the stats values for this netdev into the buffer. Expects data to be + * pre-allocated to the size returned by i40e_get_stats_count.. Note that all + * statistics must be copied in a static order, and the count must not change + * for a given netdev. See i40e_get_stats_count for more details. + * + * If a statistic is not currently valid (such as a disabled queue), this + * function reports its value as zero. + **/ static void i40e_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_ring *tx_ring, *rx_ring; struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - unsigned int j; - int i = 0; - char *p; - struct rtnl_link_stats64 *net_stats = i40e_get_vsi_stats_struct(vsi); - unsigned int start; + struct i40e_veb *veb = NULL; +#ifndef I40E_PF_EXTRA_STATS_OFF + unsigned int vsi_idx; + unsigned int vf_idx; + unsigned int vf_id; + bool is_vf_valid; +#endif /* !I40E_PF_EXTRA_STATS_OFF */ + unsigned int i; + bool veb_stats; + u64 *p = data; i40e_update_stats(vsi); - for (j = 0; j < I40E_NETDEV_STATS_LEN; j++) { - p = (char *)net_stats + i40e_gstrings_net_stats[j].stat_offset; - data[i++] = (i40e_gstrings_net_stats[j].sizeof_stat == - sizeof(u64)) ? *(u64 *)p : *(u32 *)p; - } - for (j = 0; j < I40E_MISC_STATS_LEN; j++) { - p = (char *)vsi + i40e_gstrings_misc_stats[j].stat_offset; - data[i++] = (i40e_gstrings_misc_stats[j].sizeof_stat == - sizeof(u64)) ? *(u64 *)p : *(u32 *)p; - } - rcu_read_lock(); - for (j = 0; j < vsi->num_queue_pairs; j++) { - tx_ring = ACCESS_ONCE(vsi->tx_rings[j]); + i40e_add_ethtool_stats(&data, i40e_get_vsi_stats_struct(vsi), + i40e_gstrings_net_stats); - if (!tx_ring) - continue; + i40e_add_ethtool_stats(&data, vsi, i40e_gstrings_misc_stats); - /* process Tx ring statistics */ - do { - start = u64_stats_fetch_begin_irq(&tx_ring->syncp); - data[i] = tx_ring->stats.packets; - data[i + 1] = tx_ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&tx_ring->syncp, start)); - i += 2; - - /* Rx ring is the 2nd half of the queue pair */ - rx_ring = &tx_ring[1]; - do { - start = u64_stats_fetch_begin_irq(&rx_ring->syncp); - data[i] = rx_ring->stats.packets; - data[i + 1] = rx_ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&rx_ring->syncp, start)); - i += 2; + rcu_read_lock(); + for (i = 0; i < netdev->real_num_tx_queues; i++) { + i40e_add_queue_stats(&data, READ_ONCE(vsi->tx_rings[i])); + i40e_add_queue_stats(&data, READ_ONCE(vsi->rx_rings[i])); +#ifdef HAVE_XDP_SUPPORT + i40e_add_rx_queue_xdp_stats(&data, READ_ONCE(vsi->rx_rings[i])); +#endif } rcu_read_unlock(); + if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1) - return; + goto check_data_pointer; + + veb_stats = ((pf->lan_veb != I40E_NO_VEB) && + (pf->lan_veb < I40E_MAX_VEB) && + (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)); + + if (veb_stats) { + veb = pf->veb[pf->lan_veb]; + i40e_update_veb_stats(veb); + } + + /* If veb stats aren't enabled, pass NULL instead of the veb so that + * we initialize stats to zero and update the data pointer + * intelligently + */ + i40e_add_ethtool_stats(&data, veb_stats ? veb : NULL, + i40e_gstrings_veb_stats); + + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) + i40e_add_ethtool_stats(&data, veb_stats ? veb : NULL, + i40e_gstrings_veb_tc_stats); - if ((pf->lan_veb != I40E_NO_VEB) && - (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) { - struct i40e_veb *veb = pf->veb[pf->lan_veb]; + i40e_add_ethtool_stats(&data, pf, i40e_gstrings_stats); + + for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { + struct i40e_pfc_stats pfc = i40e_get_pfc_stats(pf, i); + + i40e_add_ethtool_stats(&data, &pfc, i40e_gstrings_pfc_stats); + } + +#ifndef I40E_PF_EXTRA_STATS_OFF + /* As for now, we only process the SRIOV type VSIs (as extra stats to + * PF core stats) which are correlated with VF LAN VSI (hence below, + * in this for-loop instruction block, only VF's LAN VSIs are currently + * processed). + */ + for (vf_id = 0; vf_id < pf->num_alloc_vfs; vf_id++) { + is_vf_valid = true; + for (vf_idx = 0; vf_idx < pf->num_alloc_vfs; vf_idx++) + if (pf->vf[vf_idx].vf_id == vf_id) + break; + if (vf_idx >= pf->num_alloc_vfs) { + dev_info(&pf->pdev->dev, + "In the PF's array, there is no VF instance with VF_ID identifier %d or it is not set/initialized correctly yet\n", + vf_id); + is_vf_valid = false; + goto check_vf; + } + vsi_idx = pf->vf[vf_idx].lan_vsi_idx; - for (j = 0; j < I40E_VEB_STATS_LEN; j++) { - p = (char *)veb; - p += i40e_gstrings_veb_stats[j].stat_offset; - data[i++] = (i40e_gstrings_veb_stats[j].sizeof_stat == - sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + vsi = pf->vsi[vsi_idx]; + if (!vsi) { + /* It means empty field in the PF VSI array... */ + dev_info(&pf->pdev->dev, + "No LAN VSI instance referenced by VF %d or it is not set/initialized correctly yet\n", + vf_id); + is_vf_valid = false; + goto check_vf; + } + if (vsi->vf_id != vf_id) { + dev_info(&pf->pdev->dev, + "In the PF's array, there is incorrectly set/initialized LAN VSI or reference to it from VF %d is not set/initialized correctly yet\n", + vf_id); + is_vf_valid = false; + goto check_vf; + } + if (vsi->vf_id != pf->vf[vf_idx].vf_id || + !i40e_find_vsi_from_id(pf, pf->vf[vsi->vf_id].lan_vsi_id)) { + /* Disjointed identifiers or broken references VF-VSI */ + dev_warn(&pf->pdev->dev, + "SRIOV LAN VSI (index %d in PF VSI array) with invalid VF Identifier %d (referenced by VF %d, ordered as %d in VF array)\n", + vsi_idx, pf->vsi[vsi_idx]->vf_id, + pf->vf[vf_idx].vf_id, vf_idx); + is_vf_valid = false; } - for (j = 0; j < I40E_MAX_TRAFFIC_CLASS; j++) { - data[i++] = veb->tc_stats.tc_tx_packets[j]; - data[i++] = veb->tc_stats.tc_tx_bytes[j]; - data[i++] = veb->tc_stats.tc_rx_packets[j]; - data[i++] = veb->tc_stats.tc_rx_bytes[j]; +check_vf: + if (!is_vf_valid) { + i40e_add_ethtool_stats(&data, NULL, + i40e_gstrings_eth_stats_extra); + } else { + i40e_update_eth_stats(vsi); + i40e_add_ethtool_stats(&data, vsi, + i40e_gstrings_eth_stats_extra); } } - for (j = 0; j < I40E_GLOBAL_STATS_LEN; j++) { - p = (char *)pf + i40e_gstrings_stats[j].stat_offset; - data[i++] = (i40e_gstrings_stats[j].sizeof_stat == - sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + for (; vf_id < I40E_STATS_EXTRA_COUNT; vf_id++) + i40e_add_ethtool_stats(&data, NULL, + i40e_gstrings_eth_stats_extra); + +#endif /* !I40E_PF_EXTRA_STATS_OFF */ +check_data_pointer: + WARN_ONCE(data - p != i40e_get_stats_count(netdev), + "ethtool stats count mismatch!"); +} + +#ifndef I40E_PF_EXTRA_STATS_OFF +/** + * i40e_update_vfid_in_stats - print VF num to stats names + * @stats_extra: array of stats structs with stats name strings + * @strings_num: number of stats name strings in array above (length) + * @vf_id: VF number to update stats name strings with + * + * Helper function to i40e_get_stat_strings() in case of extra stats. + **/ +static inline void +i40e_update_vfid_in_stats(struct i40e_stats stats_extra[], + int strings_num, int vf_id) +{ + int i; + + for (i = 0; i < strings_num; i++) { + snprintf(stats_extra[i].stat_string, + I40E_STATS_NAME_VFID_EXTRA_LEN, "vf%03d", vf_id); + stats_extra[i].stat_string[I40E_STATS_NAME_VFID_EXTRA_LEN - + 1] = '.'; } - for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) { - data[i++] = pf->stats.priority_xon_tx[j]; - data[i++] = pf->stats.priority_xoff_tx[j]; +} +#endif /* !I40E_PF_EXTRA_STATS_OFF */ +/** + * i40e_get_stat_strings - copy stat strings into supplied buffer + * @netdev: the netdev to collect strings for + * @data: supplied buffer to copy strings into + * + * Copy the strings related to stats for this netdev. Expects data to be + * pre-allocated with the size reported by i40e_get_stats_count. Note that the + * strings must be copied in a static order and the total count must not + * change for a given netdev. See i40e_get_stats_count for more details. + **/ +static void i40e_get_stat_strings(struct net_device *netdev, u8 *data) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + unsigned int i; + u8 *p = data; + + i40e_add_stat_strings(&data, i40e_gstrings_net_stats); + + i40e_add_stat_strings(&data, i40e_gstrings_misc_stats); + + for (i = 0; i < netdev->real_num_tx_queues; i++) { + i40e_add_stat_strings(&data, i40e_gstrings_queue_stats, + "tx", i); + i40e_add_stat_strings(&data, i40e_gstrings_queue_stats, + "rx", i); +#ifdef HAVE_XDP_SUPPORT + i40e_add_stat_strings(&data, i40e_gstrings_rx_queue_xdp_stats, + "rx", i); +#endif } - for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) { - data[i++] = pf->stats.priority_xon_rx[j]; - data[i++] = pf->stats.priority_xoff_rx[j]; + + if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1) + goto check_data_pointer; + + i40e_add_stat_strings(&data, i40e_gstrings_veb_stats); + + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) + i40e_add_stat_strings(&data, i40e_gstrings_veb_tc_stats, i); + + i40e_add_stat_strings(&data, i40e_gstrings_stats); + + for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) + i40e_add_stat_strings(&data, i40e_gstrings_pfc_stats, i); + +#ifndef I40E_PF_EXTRA_STATS_OFF + for (i = 0; i < I40E_STATS_EXTRA_COUNT; i++) { + i40e_update_vfid_in_stats + (i40e_gstrings_eth_stats_extra, + ARRAY_SIZE(i40e_gstrings_eth_stats_extra), i); + i40e_add_stat_strings(&data, i40e_gstrings_eth_stats_extra); } - for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) - data[i++] = pf->stats.priority_xon_2_xoff[j]; + +#endif /* !I40E_PF_EXTRA_STATS_OFF */ +check_data_pointer: + WARN_ONCE(data - p != i40e_get_stats_count(netdev) * ETH_GSTRING_LEN, + "stat strings count mismatch!"); } -static void i40e_get_strings(struct net_device *netdev, u32 stringset, - u8 *data) +#ifdef HAVE_ETHTOOL_GET_SSET_COUNT +static void i40e_get_priv_flag_strings(struct net_device *netdev, u8 *data) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -1639,107 +2785,47 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset, char *p = (char *)data; unsigned int i; + for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) { + snprintf(p, ETH_GSTRING_LEN, "%s", + i40e_gstrings_priv_flags[i].flag_string); + p += ETH_GSTRING_LEN; + } + if (pf->hw.pf_id != 0) + return; + for (i = 0; i < I40E_GL_PRIV_FLAGS_STR_LEN; i++) { + snprintf(p, ETH_GSTRING_LEN, "%s", + i40e_gl_gstrings_priv_flags[i].flag_string); + p += ETH_GSTRING_LEN; + } +} +#endif + +static void i40e_get_strings(struct net_device *netdev, u32 stringset, + u8 *data) +{ switch (stringset) { case ETH_SS_TEST: memcpy(data, i40e_gstrings_test, I40E_TEST_LEN * ETH_GSTRING_LEN); break; case ETH_SS_STATS: - for (i = 0; i < I40E_NETDEV_STATS_LEN; i++) { - snprintf(p, ETH_GSTRING_LEN, "%s", - i40e_gstrings_net_stats[i].stat_string); - p += ETH_GSTRING_LEN; - } - for (i = 0; i < I40E_MISC_STATS_LEN; i++) { - snprintf(p, ETH_GSTRING_LEN, "%s", - i40e_gstrings_misc_stats[i].stat_string); - p += ETH_GSTRING_LEN; - } - for (i = 0; i < vsi->num_queue_pairs; i++) { - snprintf(p, ETH_GSTRING_LEN, "tx-%d.tx_packets", i); - p += ETH_GSTRING_LEN; - snprintf(p, ETH_GSTRING_LEN, "tx-%d.tx_bytes", i); - p += ETH_GSTRING_LEN; - snprintf(p, ETH_GSTRING_LEN, "rx-%d.rx_packets", i); - p += ETH_GSTRING_LEN; - snprintf(p, ETH_GSTRING_LEN, "rx-%d.rx_bytes", i); - p += ETH_GSTRING_LEN; - } - if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1) - return; - - if ((pf->lan_veb != I40E_NO_VEB) && - (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) { - for (i = 0; i < I40E_VEB_STATS_LEN; i++) { - snprintf(p, ETH_GSTRING_LEN, "veb.%s", - i40e_gstrings_veb_stats[i].stat_string); - p += ETH_GSTRING_LEN; - } - for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - snprintf(p, ETH_GSTRING_LEN, - "veb.tc_%d_tx_packets", i); - p += ETH_GSTRING_LEN; - snprintf(p, ETH_GSTRING_LEN, - "veb.tc_%d_tx_bytes", i); - p += ETH_GSTRING_LEN; - snprintf(p, ETH_GSTRING_LEN, - "veb.tc_%d_rx_packets", i); - p += ETH_GSTRING_LEN; - snprintf(p, ETH_GSTRING_LEN, - "veb.tc_%d_rx_bytes", i); - p += ETH_GSTRING_LEN; - } - } - for (i = 0; i < I40E_GLOBAL_STATS_LEN; i++) { - snprintf(p, ETH_GSTRING_LEN, "port.%s", - i40e_gstrings_stats[i].stat_string); - p += ETH_GSTRING_LEN; - } - for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { - snprintf(p, ETH_GSTRING_LEN, - "port.tx_priority_%d_xon", i); - p += ETH_GSTRING_LEN; - snprintf(p, ETH_GSTRING_LEN, - "port.tx_priority_%d_xoff", i); - p += ETH_GSTRING_LEN; - } - for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { - snprintf(p, ETH_GSTRING_LEN, - "port.rx_priority_%d_xon", i); - p += ETH_GSTRING_LEN; - snprintf(p, ETH_GSTRING_LEN, - "port.rx_priority_%d_xoff", i); - p += ETH_GSTRING_LEN; - } - for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { - snprintf(p, ETH_GSTRING_LEN, - "port.rx_priority_%d_xon_2_xoff", i); - p += ETH_GSTRING_LEN; - } - /* BUG_ON(p - data != I40E_STATS_LEN * ETH_GSTRING_LEN); */ + i40e_get_stat_strings(netdev, data); break; +#ifdef HAVE_ETHTOOL_GET_SSET_COUNT case ETH_SS_PRIV_FLAGS: - for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) { - snprintf(p, ETH_GSTRING_LEN, "%s", - i40e_gstrings_priv_flags[i].flag_string); - p += ETH_GSTRING_LEN; - } - if (pf->hw.pf_id != 0) - break; - for (i = 0; i < I40E_GL_PRIV_FLAGS_STR_LEN; i++) { - snprintf(p, ETH_GSTRING_LEN, "%s", - i40e_gl_gstrings_priv_flags[i].flag_string); - p += ETH_GSTRING_LEN; - } + i40e_get_priv_flag_strings(netdev, data); break; +#endif default: break; } } +#ifdef HAVE_ETHTOOL_GET_TS_INFO static int i40e_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info) { +#ifdef HAVE_PTP_1588_CLOCK struct i40e_pf *pf = i40e_netdev_to_pf(dev); /* only report HW timestamping if PTP is enabled */ @@ -1776,9 +2862,13 @@ static int i40e_get_ts_info(struct net_device *dev, BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ); return 0; +#else /* HAVE_PTP_1588_CLOCK */ + return ethtool_op_get_ts_info(dev, info); +#endif /* HAVE_PTP_1588_CLOCK */ } -static int i40e_link_test(struct net_device *netdev, u64 *data) +#endif /* HAVE_ETHTOOL_GET_TS_INFO */ +static u64 i40e_link_test(struct net_device *netdev, u64 *data) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; @@ -1787,7 +2877,7 @@ static int i40e_link_test(struct net_device *netdev, u64 *data) netif_info(pf, hw, netdev, "link test\n"); status = i40e_get_link_status(&pf->hw, &link_up); - if (status) { + if (status != I40E_SUCCESS) { netif_err(pf, drv, netdev, "link query timed out, please retry test\n"); *data = 1; return *data; @@ -1801,7 +2891,7 @@ static int i40e_link_test(struct net_device *netdev, u64 *data) return *data; } -static int i40e_reg_test(struct net_device *netdev, u64 *data) +static u64 i40e_reg_test(struct net_device *netdev, u64 *data) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; @@ -1812,7 +2902,7 @@ static int i40e_reg_test(struct net_device *netdev, u64 *data) return *data; } -static int i40e_eeprom_test(struct net_device *netdev, u64 *data) +static u64 i40e_eeprom_test(struct net_device *netdev, u64 *data) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; @@ -1826,7 +2916,7 @@ static int i40e_eeprom_test(struct net_device *netdev, u64 *data) return *data; } -static int i40e_intr_test(struct net_device *netdev, u64 *data) +static u64 i40e_intr_test(struct net_device *netdev, u64 *data) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; @@ -1845,6 +2935,13 @@ static int i40e_intr_test(struct net_device *netdev, u64 *data) return *data; } +#ifndef HAVE_ETHTOOL_GET_SSET_COUNT +static int i40e_diag_test_count(struct net_device *netdev) +{ + return I40E_TEST_LEN; +} + +#endif /* HAVE_ETHTOOL_GET_SSET_COUNT */ static inline bool i40e_active_vfs(struct i40e_pf *pf) { struct i40e_vf *vfs = pf->vf; @@ -1993,11 +3090,12 @@ static int i40e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) return 0; } +#ifdef HAVE_ETHTOOL_SET_PHYS_ID static int i40e_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state) { struct i40e_netdev_priv *np = netdev_priv(netdev); - i40e_status ret = 0; + i40e_status ret = I40E_SUCCESS; struct i40e_pf *pf = np->vsi->back; struct i40e_hw *hw = &pf->hw; int blink_freq = 2; @@ -2008,7 +3106,9 @@ static int i40e_set_phys_id(struct net_device *netdev, if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) { pf->led_status = i40e_led_get(hw); } else { - i40e_aq_set_phy_debug(hw, I40E_PHY_DEBUG_ALL, NULL); + if (!(hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE)) + i40e_aq_set_phy_debug(hw, I40E_PHY_DEBUG_ALL, + NULL); ret = i40e_led_get_phy(hw, &temp_status, &pf->phy_led_val); pf->led_status = temp_status; @@ -2033,7 +3133,8 @@ static int i40e_set_phys_id(struct net_device *netdev, ret = i40e_led_set_phy(hw, false, pf->led_status, (pf->phy_led_val | I40E_PHY_LED_MODE_ORIG)); - i40e_aq_set_phy_debug(hw, 0, NULL); + if (!(hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE)) + i40e_aq_set_phy_debug(hw, 0, NULL); } break; default: @@ -2044,6 +3145,53 @@ static int i40e_set_phys_id(struct net_device *netdev, else return 0; } +#else /* HAVE_ETHTOOL_SET_PHYS_ID */ +static int i40e_phys_id(struct net_device *netdev, u32 data) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_pf *pf = np->vsi->back; + struct i40e_hw *hw = &pf->hw; + i40e_status ret = I40E_SUCCESS; + u16 temp_status; + int i; + + if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) { + pf->led_status = i40e_led_get(hw); + } else { + ret = i40e_led_get_phy(hw, &temp_status, + &pf->phy_led_val); + pf->led_status = temp_status; + } + + if (!data || data > 300) + data = 300; + + /* 10GBaseT PHY controls led's through PHY, not MAC */ + for (i = 0; i < (data * 1000); i += 400) { + if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) + i40e_led_set(hw, 0xF, false); + else + ret = i40e_led_set_phy(hw, true, pf->led_status, 0); + msleep_interruptible(200); + if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) + i40e_led_set(hw, 0x0, false); + else + ret = i40e_led_set_phy(hw, false, pf->led_status, 0); + msleep_interruptible(200); + } + if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) + i40e_led_set(hw, pf->led_status, false); + else + ret = i40e_led_set_phy(hw, false, pf->led_status, + (pf->led_status | + I40E_PHY_LED_MODE_ORIG)); + + if (ret) + return -ENOENT; + else + return 0; +} +#endif /* HAVE_ETHTOOL_SET_PHYS_ID */ /* NOTE: i40e hardware uses a conversion factor of 2 for Interrupt * Throttle Rate (ITR) ie. ITR(1) = 2us ITR(10) = 20 us, and also @@ -2071,27 +3219,25 @@ static int __i40e_get_coalesce(struct net_device *netdev, ec->tx_max_coalesced_frames_irq = vsi->work_limit; ec->rx_max_coalesced_frames_irq = vsi->work_limit; - /* rx and tx usecs has per queue value. If user doesn't specify the queue, - * return queue 0's value to represent. + /* rx and tx usecs has per queue value. If user doesn't specify the + * queue, return queue 0's value to represent. */ - if (queue < 0) { + if (queue < 0) queue = 0; - } else if (queue >= vsi->num_queue_pairs) { + else if (queue >= vsi->num_queue_pairs) return -EINVAL; - } rx_ring = vsi->rx_rings[queue]; tx_ring = vsi->tx_rings[queue]; - if (ITR_IS_DYNAMIC(rx_ring->rx_itr_setting)) + if (ITR_IS_DYNAMIC(rx_ring->itr_setting)) ec->use_adaptive_rx_coalesce = 1; - if (ITR_IS_DYNAMIC(tx_ring->tx_itr_setting)) + if (ITR_IS_DYNAMIC(tx_ring->itr_setting)) ec->use_adaptive_tx_coalesce = 1; - ec->rx_coalesce_usecs = rx_ring->rx_itr_setting & ~I40E_ITR_DYNAMIC; - ec->tx_coalesce_usecs = tx_ring->tx_itr_setting & ~I40E_ITR_DYNAMIC; - + ec->rx_coalesce_usecs = rx_ring->itr_setting & ~I40E_ITR_DYNAMIC; + ec->tx_coalesce_usecs = tx_ring->itr_setting & ~I40E_ITR_DYNAMIC; /* we use the _usecs_high to store/set the interrupt rate limit * that the hardware supports, that almost but not quite @@ -2120,6 +3266,7 @@ static int i40e_get_coalesce(struct net_device *netdev, return __i40e_get_coalesce(netdev, ec, -1); } +#ifdef ETHTOOL_PERQUEUE /** * i40e_get_per_queue_coalesce - gets coalesce settings for particular queue * @netdev: netdev structure @@ -2134,6 +3281,7 @@ static int i40e_get_per_queue_coalesce(struct net_device *netdev, u32 queue, return __i40e_get_coalesce(netdev, ec, queue); } +#endif /* ETHTOOL_PERQUEUE */ /** * i40e_set_itr_per_queue - set ITR values for specific queue * @vsi: the VSI to set values for @@ -2142,45 +3290,93 @@ static int i40e_get_per_queue_coalesce(struct net_device *netdev, u32 queue, * * Change the ITR settings for a specific queue. **/ - static void i40e_set_itr_per_queue(struct i40e_vsi *vsi, struct ethtool_coalesce *ec, int queue) { + struct i40e_ring *rx_ring = vsi->rx_rings[queue]; + struct i40e_ring *tx_ring = vsi->tx_rings[queue]; struct i40e_pf *pf = vsi->back; struct i40e_hw *hw = &pf->hw; struct i40e_q_vector *q_vector; - u16 vector, intrl; + u16 intrl; intrl = i40e_intrl_usec_to_reg(vsi->int_rate_limit); - vsi->rx_rings[queue]->rx_itr_setting = ec->rx_coalesce_usecs; - vsi->tx_rings[queue]->tx_itr_setting = ec->tx_coalesce_usecs; + rx_ring->itr_setting = ITR_REG_ALIGN(ec->rx_coalesce_usecs); + tx_ring->itr_setting = ITR_REG_ALIGN(ec->tx_coalesce_usecs); if (ec->use_adaptive_rx_coalesce) - vsi->rx_rings[queue]->rx_itr_setting |= I40E_ITR_DYNAMIC; + rx_ring->itr_setting |= I40E_ITR_DYNAMIC; else - vsi->rx_rings[queue]->rx_itr_setting &= ~I40E_ITR_DYNAMIC; + rx_ring->itr_setting &= ~I40E_ITR_DYNAMIC; if (ec->use_adaptive_tx_coalesce) - vsi->tx_rings[queue]->tx_itr_setting |= I40E_ITR_DYNAMIC; + tx_ring->itr_setting |= I40E_ITR_DYNAMIC; else - vsi->tx_rings[queue]->tx_itr_setting &= ~I40E_ITR_DYNAMIC; + tx_ring->itr_setting &= ~I40E_ITR_DYNAMIC; + + q_vector = rx_ring->q_vector; + q_vector->rx.target_itr = ITR_TO_REG(rx_ring->itr_setting); - q_vector = vsi->rx_rings[queue]->q_vector; - q_vector->rx.itr = ITR_TO_REG(vsi->rx_rings[queue]->rx_itr_setting); - vector = vsi->base_vector + q_vector->v_idx; - wr32(hw, I40E_PFINT_ITRN(I40E_RX_ITR, vector - 1), q_vector->rx.itr); + q_vector = tx_ring->q_vector; + q_vector->tx.target_itr = ITR_TO_REG(tx_ring->itr_setting); - q_vector = vsi->tx_rings[queue]->q_vector; - q_vector->tx.itr = ITR_TO_REG(vsi->tx_rings[queue]->tx_itr_setting); - vector = vsi->base_vector + q_vector->v_idx; - wr32(hw, I40E_PFINT_ITRN(I40E_TX_ITR, vector - 1), q_vector->tx.itr); + /* The interrupt handler itself will take care of programming + * the Tx and Rx ITR values based on the values we have entered + * into the q_vector, no need to write the values now. + */ - wr32(hw, I40E_PFINT_RATEN(vector - 1), intrl); + wr32(hw, I40E_PFINT_RATEN(q_vector->reg_idx), intrl); i40e_flush(hw); } +/** + * i40e_is_coalesce_param_invalid - check for unsupported coalesce parameters + * @netdev: pointer to the netdev associated with this query + * @ec: ethtool structure to fill with driver's coalesce settings + * + * Print netdev info if driver doesn't support one of the parameters + * and return error. When any parameters will be implemented, remove only + * this parameter from param array. + */ +static +int i40e_is_coalesce_param_invalid(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct i40e_ethtool_not_used { + u32 value; + const char *name; + } param[] = { + {ec->stats_block_coalesce_usecs, "stats-block-usecs"}, + {ec->rate_sample_interval, "sample-interval"}, + {ec->pkt_rate_low, "pkt-rate-low"}, + {ec->pkt_rate_high, "pkt-rate-high"}, + {ec->rx_max_coalesced_frames, "rx-frames"}, + {ec->rx_coalesce_usecs_irq, "rx-usecs-irq"}, + {ec->tx_max_coalesced_frames, "tx-frames"}, + {ec->tx_coalesce_usecs_irq, "tx-usecs-irq"}, + {ec->rx_coalesce_usecs_low, "rx-usecs-low"}, + {ec->rx_max_coalesced_frames_low, "rx-frames-low"}, + {ec->tx_coalesce_usecs_low, "tx-usecs-low"}, + {ec->tx_max_coalesced_frames_low, "tx-frames-low"}, + {ec->rx_max_coalesced_frames_high, "rx-frames-high"}, + {ec->tx_max_coalesced_frames_high, "tx-frames-high"} + }; + int i; + + for (i = 0; i < ARRAY_SIZE(param); i++) { + if (param[i].value) { + netdev_info(netdev, + "Setting %s not supported\n", + param[i].name); + return -EOPNOTSUPP; + } + } + + return 0; +} + /** * __i40e_set_coalesce - set coalesce settings for particular queue * @netdev: the netdev to change @@ -2199,15 +3395,18 @@ static int __i40e_set_coalesce(struct net_device *netdev, struct i40e_pf *pf = vsi->back; int i; + if (i40e_is_coalesce_param_invalid(netdev, ec)) + return -EOPNOTSUPP; + if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq) vsi->work_limit = ec->tx_max_coalesced_frames_irq; if (queue < 0) { - cur_rx_itr = vsi->rx_rings[0]->rx_itr_setting; - cur_tx_itr = vsi->tx_rings[0]->tx_itr_setting; + cur_rx_itr = vsi->rx_rings[0]->itr_setting; + cur_tx_itr = vsi->tx_rings[0]->itr_setting; } else if (queue < vsi->num_queue_pairs) { - cur_rx_itr = vsi->rx_rings[queue]->rx_itr_setting; - cur_tx_itr = vsi->tx_rings[queue]->tx_itr_setting; + cur_rx_itr = vsi->rx_rings[queue]->itr_setting; + cur_tx_itr = vsi->tx_rings[queue]->itr_setting; } else { netif_info(pf, drv, netdev, "Invalid queue value, queue range is 0 - %d\n", vsi->num_queue_pairs - 1); @@ -2235,7 +3434,7 @@ static int __i40e_set_coalesce(struct net_device *netdev, return -EINVAL; } - if (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1)) { + if (ec->rx_coalesce_usecs > I40E_MAX_ITR) { netif_info(pf, drv, netdev, "Invalid value, rx-usecs range is 0-8160\n"); return -EINVAL; } @@ -2246,16 +3445,16 @@ static int __i40e_set_coalesce(struct net_device *netdev, return -EINVAL; } - if (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1)) { + if (ec->tx_coalesce_usecs > I40E_MAX_ITR) { netif_info(pf, drv, netdev, "Invalid value, tx-usecs range is 0-8160\n"); return -EINVAL; } if (ec->use_adaptive_rx_coalesce && !cur_rx_itr) - ec->rx_coalesce_usecs = I40E_MIN_ITR << 1; + ec->rx_coalesce_usecs = I40E_MIN_ITR; if (ec->use_adaptive_tx_coalesce && !cur_tx_itr) - ec->tx_coalesce_usecs = I40E_MIN_ITR << 1; + ec->tx_coalesce_usecs = I40E_MIN_ITR; intrl_reg = i40e_intrl_usec_to_reg(ec->rx_coalesce_usecs_high); vsi->int_rate_limit = INTRL_REG_TO_USEC(intrl_reg); @@ -2264,8 +3463,8 @@ static int __i40e_set_coalesce(struct net_device *netdev, vsi->int_rate_limit); } - /* rx and tx usecs has per queue value. If user doesn't specify the queue, - * apply to all queues. + /* rx and tx usecs has per queue value. If user doesn't specify the + * queue, apply to all queues. */ if (queue < 0) { for (i = 0; i < vsi->num_queue_pairs; i++) @@ -2290,6 +3489,20 @@ static int i40e_set_coalesce(struct net_device *netdev, return __i40e_set_coalesce(netdev, ec, -1); } +#ifdef ETHTOOL_SRXNTUPLE +/* We need to keep this around for kernels 2.6.33 - 2.6.39 in order to avoid + * a null pointer dereference as it was assumend if the NETIF_F_NTUPLE flag + * was defined that this function was present. + */ +static int i40e_set_rx_ntuple(struct net_device *dev, + struct ethtool_rx_ntuple *cmd) +{ + return -EOPNOTSUPP; +} + +#endif /* ETHTOOL_SRXNTUPLE */ + +#ifdef ETHTOOL_PERQUEUE /** * i40e_set_per_queue_coalesce - set specific queue's coalesce settings * @netdev: the netdev to change @@ -2303,7 +3516,9 @@ static int i40e_set_per_queue_coalesce(struct net_device *netdev, u32 queue, { return __i40e_set_coalesce(netdev, ec, queue); } +#endif /* ETHTOOL_PERQUEUE */ +#ifdef ETHTOOL_GRXRINGS /** * i40e_get_rss_hash_opts - Get RSS hash Input Set for each flow type * @pf: pointer to the physical function struct @@ -2385,7 +3600,7 @@ static int i40e_get_rss_hash_opts(struct i40e_pf *pf, struct ethtool_rxnfc *cmd) /** * i40e_check_mask - Check whether a mask field is set * @mask: the full mask value - * @field; mask of the field to check + * @field: mask of the field to check * * If the given mask is fully set, return positive value. If the mask for the * field is fully unset, return zero. Otherwise return a negative error code. @@ -2436,18 +3651,69 @@ static int i40e_parse_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp, value = be64_to_cpu(*((__be64 *)fsp->h_ext.data)); mask = be64_to_cpu(*((__be64 *)fsp->m_ext.data)); -#define I40E_USERDEF_FLEX_WORD GENMASK_ULL(15, 0) -#define I40E_USERDEF_FLEX_OFFSET GENMASK_ULL(31, 16) +#define I40E_USERDEF_CLOUD_FILTER BIT_ULL(63) + +#define I40E_USERDEF_CLOUD_RESERVED GENMASK_ULL(62, 32) +#define I40E_USERDEF_TUNNEL_TYPE GENMASK_ULL(31, 24) +#define I40E_USERDEF_TENANT_ID GENMASK_ULL(23, 0) + +#define I40E_USERDEF_RESERVED GENMASK_ULL(62, 32) #define I40E_USERDEF_FLEX_FILTER GENMASK_ULL(31, 0) - valid = i40e_check_mask(mask, I40E_USERDEF_FLEX_FILTER); - if (valid < 0) { - return -EINVAL; - } else if (valid) { - data->flex_word = value & I40E_USERDEF_FLEX_WORD; - data->flex_offset = - (value & I40E_USERDEF_FLEX_OFFSET) >> 16; - data->flex_filter = true; +#define I40E_USERDEF_FLEX_OFFSET GENMASK_ULL(31, 16) +#define I40E_USERDEF_FLEX_WORD GENMASK_ULL(15, 0) + + if ((mask & I40E_USERDEF_CLOUD_FILTER) && + (value & I40E_USERDEF_CLOUD_FILTER)) + data->cloud_filter = true; + + if (data->cloud_filter) { + /* Make sure that the reserved bits are not set */ + valid = i40e_check_mask(mask, I40E_USERDEF_CLOUD_RESERVED); + if (valid < 0) { + return -EINVAL; + } else if (valid) { + if ((value & I40E_USERDEF_CLOUD_RESERVED) != 0) + return -EINVAL; + } + + /* These fields are only valid if this is a cloud filter */ + valid = i40e_check_mask(mask, I40E_USERDEF_TENANT_ID); + if (valid < 0) { + return -EINVAL; + } else if (valid) { + data->tenant_id = value & I40E_USERDEF_TENANT_ID; + data->tenant_id_valid = true; + } + + valid = i40e_check_mask(mask, I40E_USERDEF_TUNNEL_TYPE); + if (valid < 0) { + return -EINVAL; + } else if (valid) { + data->tunnel_type = + (value & I40E_USERDEF_TUNNEL_TYPE) >> 24; + data->tunnel_type_valid = true; + } + } else { + /* Make sure that the reserved bits are not set */ + valid = i40e_check_mask(mask, I40E_USERDEF_RESERVED); + if (valid < 0) { + return -EINVAL; + } else if (valid) { + if ((value & I40E_USERDEF_RESERVED) != 0) + return -EINVAL; + } + + /* These fields are only valid if this isn't a cloud filter */ + valid = i40e_check_mask(mask, I40E_USERDEF_FLEX_FILTER); + if (valid < 0) { + return -EINVAL; + } else if (valid) { + data->flex_word = value & I40E_USERDEF_FLEX_WORD; + data->flex_offset = + (value & I40E_USERDEF_FLEX_OFFSET) >> 16; + data->flex_filter = true; + } } return 0; @@ -2456,6 +3722,7 @@ static int i40e_parse_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp, /** * i40e_fill_rx_flow_user_data - Fill in user-defined data field * @fsp: pointer to rx_flow specification + * @data: pointer to return userdef data * * Reads the userdef data structure and properly fills in the user defined * fields of the rx_flow_spec. @@ -2465,10 +3732,25 @@ static void i40e_fill_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp, { u64 value = 0, mask = 0; - if (data->flex_filter) { - value |= data->flex_word; - value |= (u64)data->flex_offset << 16; - mask |= I40E_USERDEF_FLEX_FILTER; + if (data->cloud_filter) { + value |= I40E_USERDEF_CLOUD_FILTER; + mask |= I40E_USERDEF_CLOUD_FILTER; + + if (data->tenant_id_valid) { + value |= data->tenant_id; + mask |= I40E_USERDEF_TENANT_ID; + } + + if (data->tunnel_type_valid) { + value |= (u64)data->tunnel_type << 24; + mask |= I40E_USERDEF_TUNNEL_TYPE; + } + } else { + if (data->flex_filter) { + value |= data->flex_word; + value |= (u64)data->flex_offset << 16; + mask |= I40E_USERDEF_FLEX_FILTER; + } } if (value || mask) @@ -2479,7 +3761,7 @@ static void i40e_fill_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp, } /** - * i40e_get_ethtool_fdir_all - Populates the rule count of a command + * i40e_get_rx_filter_ids - Populates the rule count of a command * @pf: Pointer to the physical function struct * @cmd: The command to get or set Rx flow classification rules * @rule_locs: Array of used rule locations @@ -2489,23 +3771,34 @@ static void i40e_fill_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp, * * Returns 0 on success or -EMSGSIZE if entry not found **/ -static int i40e_get_ethtool_fdir_all(struct i40e_pf *pf, - struct ethtool_rxnfc *cmd, - u32 *rule_locs) +static int i40e_get_rx_filter_ids(struct i40e_pf *pf, + struct ethtool_rxnfc *cmd, + u32 *rule_locs) { - struct i40e_fdir_filter *rule; + struct i40e_fdir_filter *f_rule; + struct i40e_cloud_filter *c_rule; struct hlist_node *node2; - int cnt = 0; + unsigned int cnt = 0; /* report total rule count */ cmd->data = i40e_get_fd_cnt_all(pf); - hlist_for_each_entry_safe(rule, node2, + hlist_for_each_entry_safe(f_rule, node2, &pf->fdir_filter_list, fdir_node) { if (cnt == cmd->rule_cnt) return -EMSGSIZE; - rule_locs[cnt] = rule->fd_id; + rule_locs[cnt] = f_rule->fd_id; + cnt++; + } + + /* find the cloud filter rule ids */ + hlist_for_each_entry_safe(c_rule, node2, + &pf->cloud_filter_list, cloud_node) { + if (cnt == cmd->rule_cnt) + return -EMSGSIZE; + + rule_locs[cnt] = c_rule->id; cnt++; } @@ -2588,16 +3881,16 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf, no_input_set: if (input_set & I40E_L3_SRC_MASK) - fsp->m_u.tcp_ip4_spec.ip4src = htonl(0xFFFF); + fsp->m_u.tcp_ip4_spec.ip4src = htonl(0xFFFFFFFF); if (input_set & I40E_L3_DST_MASK) - fsp->m_u.tcp_ip4_spec.ip4dst = htonl(0xFFFF); + fsp->m_u.tcp_ip4_spec.ip4dst = htonl(0xFFFFFFFF); if (input_set & I40E_L4_SRC_MASK) - fsp->m_u.tcp_ip4_spec.psrc = htons(0xFFFFFFFF); + fsp->m_u.tcp_ip4_spec.psrc = htons(0xFFFF); if (input_set & I40E_L4_DST_MASK) - fsp->m_u.tcp_ip4_spec.pdst = htons(0xFFFFFFFF); + fsp->m_u.tcp_ip4_spec.pdst = htons(0xFFFF); if (rule->dest_ctl == I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET) fsp->ring_cookie = RX_CLS_FLOW_DISC; @@ -2630,15 +3923,114 @@ no_input_set: return 0; } +#define VXLAN_PORT 8472 + +/** + * i40e_get_cloud_filter_entry - get a cloud filter by loc + * @pf: pointer to the physical function struct + * @cmd: The command to get or set Rx flow classification rules + * + * get cloud filter by loc. + * Returns 0 if success. + **/ +static int i40e_get_cloud_filter_entry(struct i40e_pf *pf, + struct ethtool_rxnfc *cmd) +{ + struct ethtool_rx_flow_spec *fsp = + (struct ethtool_rx_flow_spec *)&cmd->fs; + static const u8 mac_broadcast[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + struct i40e_cloud_filter *rule, *filter = NULL; + struct i40e_rx_flow_userdef userdef = {0}; + struct hlist_node *node2; + + hlist_for_each_entry_safe(rule, node2, + &pf->cloud_filter_list, cloud_node) { + /* filter found */ + if (rule->id == fsp->location) + filter = rule; + + /* bail out if we've passed the likely location in the list */ + if (rule->id >= fsp->location) + break; + + } + if (!filter) { + dev_info(&pf->pdev->dev, "No cloud filter with loc %d\n", + fsp->location); + return -ENOENT; + } + + userdef.cloud_filter = true; + + fsp->ring_cookie = filter->queue_id; + if (filter->seid != pf->vsi[pf->lan_vsi]->seid) { + struct i40e_vsi *vsi; + + vsi = i40e_find_vsi_from_seid(pf, filter->seid); + if (vsi && vsi->type == I40E_VSI_SRIOV) { + /* VFs are zero-indexed by the driver, but ethtool + * expects them to be one-indexed, so add one here + */ + u64 ring_vf = vsi->vf_id + 1; + + ring_vf <<= ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF; + fsp->ring_cookie |= ring_vf; + } + } + + ether_addr_copy(fsp->h_u.ether_spec.h_dest, filter->outer_mac); + ether_addr_copy(fsp->h_u.ether_spec.h_source, filter->inner_mac); + + if (filter->flags & I40E_CLOUD_FIELD_OMAC) + ether_addr_copy(fsp->m_u.ether_spec.h_dest, mac_broadcast); + if (filter->flags & I40E_CLOUD_FIELD_IMAC) + ether_addr_copy(fsp->m_u.ether_spec.h_source, mac_broadcast); + if (filter->flags & I40E_CLOUD_FIELD_IVLAN) + fsp->h_ext.vlan_tci = filter->inner_vlan; + if (filter->flags & I40E_CLOUD_FIELD_TEN_ID) { + userdef.tenant_id_valid = true; + userdef.tenant_id = filter->tenant_id; + } + if (filter->tunnel_type != I40E_CLOUD_TNL_TYPE_NONE) { + userdef.tunnel_type_valid = true; + userdef.tunnel_type = filter->tunnel_type; + } + + if (filter->flags & I40E_CLOUD_FIELD_IIP) { + if (i40e_is_l4mode_enabled()) { + fsp->flow_type = UDP_V4_FLOW; + fsp->h_u.udp_ip4_spec.pdst = filter->dst_port; + } else { + fsp->flow_type = IP_USER_FLOW; + } + + fsp->h_u.usr_ip4_spec.ip4dst = filter->inner_ip[0]; + fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4; + } else { + fsp->flow_type = ETHER_FLOW; + } + + i40e_fill_rx_flow_user_data(fsp, &userdef); + + fsp->flow_type |= FLOW_EXT; + + return 0; +} + /** * i40e_get_rxnfc - command to get RX flow classification rules * @netdev: network interface device structure * @cmd: ethtool rxnfc command + * @rule_locs: pointer to store rule data * * Returns Success if the command is supported. **/ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, +#ifdef HAVE_ETHTOOL_GET_RXNFC_VOID_RULE_LOCS + void *rule_locs) +#else u32 *rule_locs) +#endif { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -2647,7 +4039,7 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, switch (cmd->cmd) { case ETHTOOL_GRXRINGS: - cmd->data = vsi->num_queue_pairs; + cmd->data = vsi->rss_size; ret = 0; break; case ETHTOOL_GRXFH: @@ -2655,15 +4047,23 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, break; case ETHTOOL_GRXCLSRLCNT: cmd->rule_cnt = pf->fdir_pf_active_filters; + cmd->rule_cnt += pf->num_cloud_filters; /* report total rule count */ cmd->data = i40e_get_fd_cnt_all(pf); ret = 0; break; case ETHTOOL_GRXCLSRULE: ret = i40e_get_ethtool_fdir_entry(pf, cmd); + /* if no such fdir filter then try the cloud list */ + if (ret) + ret = i40e_get_cloud_filter_entry(pf, cmd); break; case ETHTOOL_GRXCLSRLALL: - ret = i40e_get_ethtool_fdir_all(pf, cmd, rule_locs); +#ifdef HAVE_ETHTOOL_GET_RXNFC_VOID_RULE_LOCS + ret = i40e_get_rx_filter_ids(pf, cmd, (u32 *)rule_locs); +#else + ret = i40e_get_rx_filter_ids(pf, cmd, rule_locs); +#endif break; default: break; @@ -2675,7 +4075,7 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, /** * i40e_get_rss_hash_bits - Read RSS Hash bits from register * @nfc: pointer to user request - * @i_setc bits currently set + * @i_setc: bits currently set * * Returns value of bits to be set per user request **/ @@ -2720,7 +4120,7 @@ static u64 i40e_get_rss_hash_bits(struct ethtool_rxnfc *nfc, u64 i_setc) /** * i40e_set_rss_hash_opt - Enable/Disable flow types for RSS hash * @pf: pointer to the physical function struct - * @cmd: ethtool rxnfc command + * @nfc: ethtool rxnfc command * * Returns Success if the flow input set is supported. **/ @@ -2829,12 +4229,325 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc) return 0; } +/** + * i40e_cloud_filter_mask2flags- Convert cloud filter details to filter type + * @pf: pointer to the physical function struct + * @fsp: RX flow classification rules + * @userdef: pointer to userdef field data + * @flags: Resultant combination of all the fields to decide the tuple + * + * The general trick in setting these flags is that if the mask field for + * a value is non-zero, then the field itself was set to something, so we + * use this to tell us what has been selected. + * + * Returns 0 if a valid filter type was identified. + **/ +static int i40e_cloud_filter_mask2flags(struct i40e_pf *pf, + struct ethtool_rx_flow_spec *fsp, + struct i40e_rx_flow_userdef *userdef, + u8 *flags) +{ + u8 i = 0; + + *flags = 0; + + switch (fsp->flow_type & ~FLOW_EXT) { + case ETHER_FLOW: + /* use is_broadcast and is_zero to check for all 0xf or 0 */ + if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_dest)) { + i |= I40E_CLOUD_FIELD_OMAC; + } else if (is_zero_ether_addr(fsp->m_u.ether_spec.h_dest)) { + i &= ~I40E_CLOUD_FIELD_OMAC; + } else { + dev_info(&pf->pdev->dev, "Bad ether dest mask %pM\n", + fsp->m_u.ether_spec.h_dest); + return I40E_ERR_CONFIG; + } + + if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_source)) { + i |= I40E_CLOUD_FIELD_IMAC; + } else if (is_zero_ether_addr(fsp->m_u.ether_spec.h_source)) { + i &= ~I40E_CLOUD_FIELD_IMAC; + } else { + dev_info(&pf->pdev->dev, "Bad ether source mask %pM\n", + fsp->m_u.ether_spec.h_source); + return I40E_ERR_CONFIG; + } + break; + + case IP_USER_FLOW: + if (pf->hw.mac.type == I40E_MAC_X722) { + dev_info(&pf->pdev->dev, "Failed to set filter. Destination IP filters are not supported for this device.\n"); + return I40E_ERR_CONFIG; + } + if (fsp->m_u.usr_ip4_spec.ip4dst == cpu_to_be32(0xffffffff)) { + i |= I40E_CLOUD_FIELD_IIP; + } else if (!fsp->m_u.usr_ip4_spec.ip4dst) { + i &= ~I40E_CLOUD_FIELD_IIP; + } else { + dev_info(&pf->pdev->dev, "Bad ip dst mask 0x%08x\n", + be32_to_cpu(fsp->m_u.usr_ip4_spec.ip4dst)); + return I40E_ERR_CONFIG; + } + break; + + case UDP_V4_FLOW: + if (fsp->m_u.udp_ip4_spec.pdst == cpu_to_be16(0xffff)) { + i |= I40E_CLOUD_FIELD_IIP; + } else { + dev_info(&pf->pdev->dev, "Bad UDP dst mask 0x%04x\n", + be32_to_cpu(fsp->m_u.udp_ip4_spec.pdst)); + return I40E_ERR_CONFIG; + } + break; + + default: + return I40E_ERR_CONFIG; + } + + switch (be16_to_cpu(fsp->m_ext.vlan_tci)) { + case 0xffff: + if (fsp->h_ext.vlan_tci & cpu_to_be16(~0x7fff)) { + dev_info(&pf->pdev->dev, "Bad vlan %u\n", + be16_to_cpu(fsp->h_ext.vlan_tci)); + return I40E_ERR_CONFIG; + } + i |= I40E_CLOUD_FIELD_IVLAN; + break; + case 0: + i &= ~I40E_CLOUD_FIELD_IVLAN; + break; + default: + dev_info(&pf->pdev->dev, "Bad vlan mask %u\n", + be16_to_cpu(fsp->m_ext.vlan_tci)); + return I40E_ERR_CONFIG; + } + + /* We already know that we're a cloud filter, so we don't need to + * re-check that. + */ + if (userdef->tenant_id_valid) { + if (userdef->tenant_id == 0) + i &= ~I40E_CLOUD_FIELD_TEN_ID; + else + i |= I40E_CLOUD_FIELD_TEN_ID; + } + + /* Make sure the flags produce a valid type */ + if (i40e_get_cloud_filter_type(i, NULL)) { + dev_info(&pf->pdev->dev, "Invalid mask config, flags = %d\n", + i); + return I40E_ERR_CONFIG; + } + + *flags = i; + return I40E_SUCCESS; +} + +/* i40e_add_cloud_filter_ethtool needs i40e_del_fdir_ethtool() */ +static int i40e_del_fdir_entry(struct i40e_vsi *vsi, + struct ethtool_rxnfc *cmd); + +/** + * i40e_add_cloud_filter_ethtool - Add cloud filter + * @vsi: pointer to the VSI structure + * @cmd: The command to get or set Rx flow classification rules + * @userdef: pointer to userdef field data + * + * Add cloud filter for a specific flow spec. + * Returns 0 if the filter were successfully added. + **/ +static int i40e_add_cloud_filter_ethtool(struct i40e_vsi *vsi, + struct ethtool_rxnfc *cmd, + struct i40e_rx_flow_userdef *userdef) +{ + struct i40e_cloud_filter *rule, *parent, *filter = NULL; + struct ethtool_rx_flow_spec *fsp; + u16 dest_seid = 0, q_index = 0; + struct i40e_pf *pf = vsi->back; + struct hlist_node *node2; + u32 ring, vf; + u8 flags = 0; + int ret; + + if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED)) + return -EOPNOTSUPP; + + if (pf->flags & I40E_FLAG_MFP_ENABLED) + return -EOPNOTSUPP; + + if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) || + test_bit(__I40E_RESET_INTR_RECEIVED, pf->state)) + return -EBUSY; + + fsp = (struct ethtool_rx_flow_spec *)&cmd->fs; + + /* The ring_cookie is a mask of queue index and VF id we wish to + * target. This is the same for regular flow director filters. + */ + if (fsp->ring_cookie == RX_CLS_FLOW_DISC) { + dev_warn(&pf->pdev->dev, "Cloud filters do not support the drop action.\n"); + return -EOPNOTSUPP; + } + + ring = ethtool_get_flow_spec_ring(fsp->ring_cookie); + vf = ethtool_get_flow_spec_ring_vf(fsp->ring_cookie); + + if (!vf) { + if (ring >= vsi->num_queue_pairs) + return -EINVAL; + dest_seid = vsi->seid; + } else { + /* VFs are zero-indexed, so we subtract one here */ + vf--; + + if (vf >= pf->num_alloc_vfs) + return -EINVAL; + if (!i40e_is_l4mode_enabled() && + ring >= pf->vf[vf].num_queue_pairs) + return -EINVAL; + dest_seid = pf->vsi[pf->vf[vf].lan_vsi_idx]->seid; + } + q_index = ring; + + ret = i40e_cloud_filter_mask2flags(pf, fsp, userdef, &flags); + if (ret) + return -EINVAL; + + /* if filter exists with same id, delete the old one */ + parent = NULL; + hlist_for_each_entry_safe(rule, node2, + &pf->cloud_filter_list, cloud_node) { + /* filter exists with the id */ + if (rule->id == fsp->location) + filter = rule; + + /* bail out if we've passed the likely location in the list */ + if (rule->id >= fsp->location) + break; + + /* track where we left off */ + parent = rule; + } + if (filter && (filter->id == fsp->location)) { + /* found it in the cloud list, so remove it */ + ret = i40e_add_del_cloud_filter_ex(pf, filter, false); + if (ret && pf->hw.aq.asq_last_status != I40E_AQ_RC_ENOENT) + return ret; + hlist_del(&filter->cloud_node); + kfree(filter); + pf->num_cloud_filters--; + } else { + /* not in the cloud list, so check the PF's fdir list */ + (void)i40e_del_fdir_entry(pf->vsi[pf->lan_vsi], cmd); + } + + filter = kzalloc(sizeof(*filter), GFP_KERNEL); + if (!filter) + return -ENOMEM; + + switch (fsp->flow_type & ~FLOW_EXT) { + case ETHER_FLOW: + ether_addr_copy(filter->outer_mac, + fsp->h_u.ether_spec.h_dest); + ether_addr_copy(filter->inner_mac, + fsp->h_u.ether_spec.h_source); + break; + + case IP_USER_FLOW: + if (flags & I40E_CLOUD_FIELD_TEN_ID) { + dev_info(&pf->pdev->dev, "Tenant id not allowed for ip filter\n"); + kfree(filter); + return I40E_ERR_CONFIG; + } + filter->inner_ip[0] = fsp->h_u.usr_ip4_spec.ip4dst; + break; + + case UDP_V4_FLOW: + filter->dst_port = fsp->h_u.udp_ip4_spec.pdst; + break; + + default: + dev_info(&pf->pdev->dev, "unknown flow type 0x%x\n", + (fsp->flow_type & ~FLOW_EXT)); + kfree(filter); + return I40E_ERR_CONFIG; + } + + if (userdef->tenant_id_valid) + filter->tenant_id = userdef->tenant_id; + else + filter->tenant_id = 0; + if (userdef->tunnel_type_valid) + filter->tunnel_type = userdef->tunnel_type; + else + filter->tunnel_type = I40E_CLOUD_TNL_TYPE_NONE; + + filter->id = fsp->location; + filter->seid = dest_seid; + filter->queue_id = q_index; + filter->flags = flags; + filter->inner_vlan = fsp->h_ext.vlan_tci; + + ret = i40e_add_del_cloud_filter_ex(pf, filter, true); + if (ret) { + kfree(filter); + return ret; + } + + /* add filter to the ordered list */ + INIT_HLIST_NODE(&filter->cloud_node); + if (parent) + hlist_add_behind(&filter->cloud_node, &parent->cloud_node); + else + hlist_add_head(&filter->cloud_node, &pf->cloud_filter_list); + pf->num_cloud_filters++; + + return 0; +} + +/** + * i40e_del_cloud_filter_ethtool - del vxlan filter + * @pf: pointer to the physical function struct + * @cmd: RX flow classification rules + * + * Delete vxlan filter for a specific flow spec. + * Returns 0 if the filter was successfully deleted. + **/ +static int i40e_del_cloud_filter_ethtool(struct i40e_pf *pf, + struct ethtool_rxnfc *cmd) +{ + struct i40e_cloud_filter *rule, *filter = NULL; + struct ethtool_rx_flow_spec *fsp; + struct hlist_node *node2; + + fsp = (struct ethtool_rx_flow_spec *)&cmd->fs; + hlist_for_each_entry_safe(rule, node2, + &pf->cloud_filter_list, cloud_node) { + /* filter found */ + if (rule->id == fsp->location) + filter = rule; + + /* bail out if we've passed the likely location in the list */ + if (rule->id >= fsp->location) + break; + } + if (!filter) + return -ENOENT; + + /* remove filter from the list even if failed to remove from device */ + (void)i40e_add_del_cloud_filter_ex(pf, filter, false); + hlist_del(&filter->cloud_node); + kfree(filter); + pf->num_cloud_filters--; + + return 0; +} /** * i40e_update_ethtool_fdir_entry - Updates the fdir filter entry * @vsi: Pointer to the targeted VSI * @input: The filter to update or NULL to indicate deletion * @sw_idx: Software index to the filter - * @cmd: The command to get or set Rx flow classification rules * * This function updates (or deletes) a Flow Director entry from * the hlist of the corresponding PF @@ -2843,38 +4556,38 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc) **/ static int i40e_update_ethtool_fdir_entry(struct i40e_vsi *vsi, struct i40e_fdir_filter *input, - u16 sw_idx, - struct ethtool_rxnfc *cmd) + u16 sw_idx) { struct i40e_fdir_filter *rule, *parent; struct i40e_pf *pf = vsi->back; struct hlist_node *node2; - int err = -EINVAL; + int err = -ENOENT; parent = NULL; rule = NULL; hlist_for_each_entry_safe(rule, node2, &pf->fdir_filter_list, fdir_node) { - /* hash found, or no matching entry */ + /* rule id found, or passed its spot in the list */ if (rule->fd_id >= sw_idx) break; parent = rule; } - /* if there is an old rule occupying our place remove it */ + /* is there is an old rule occupying our target filter slot? */ if (rule && (rule->fd_id == sw_idx)) { /* Remove this rule, since we're either deleting it, or * replacing it. */ err = i40e_add_del_fdir(vsi, rule, false); hlist_del(&rule->fdir_node); - kfree(rule); pf->fdir_pf_active_filters--; + + kfree(rule); } /* If we weren't given an input, this is a delete, so just return the - * error code indicating if there was an entry at the requested slot + * error code indicating if there was an entry at the requested slot. */ if (!input) return err; @@ -2882,12 +4595,11 @@ static int i40e_update_ethtool_fdir_entry(struct i40e_vsi *vsi, /* Otherwise, install the new rule as requested */ INIT_HLIST_NODE(&input->fdir_node); - /* add filter to the list */ + /* add filter to the ordered list */ if (parent) hlist_add_behind(&input->fdir_node, &parent->fdir_node); else - hlist_add_head(&input->fdir_node, - &pf->fdir_filter_list); + hlist_add_head(&input->fdir_node, &pf->fdir_filter_list); /* update counts */ pf->fdir_pf_active_filters++; @@ -2983,7 +4695,7 @@ static int i40e_del_fdir_entry(struct i40e_vsi *vsi, if (test_bit(__I40E_FD_FLUSH_REQUESTED, pf->state)) return -EBUSY; - ret = i40e_update_ethtool_fdir_entry(vsi, NULL, fsp->location, cmd); + ret = i40e_update_ethtool_fdir_entry(vsi, NULL, fsp->location); i40e_prune_flex_pit_list(pf); @@ -3119,7 +4831,7 @@ static int i40e_add_flex_offset(struct list_head *flex_pit_list, * __i40e_reprogram_flex_pit - Re-program specific FLX_PIT table * @pf: Pointer to the PF structure * @flex_pit_list: list of flexible src offsets in use - * #flex_pit_start: index to first entry for this section of the table + * @flex_pit_start: index to first entry for this section of the table * * In order to handle flexible data, the hardware uses a table of values * called the FLX_PIT table. This table is used to indicate which sections of @@ -3233,7 +4945,7 @@ static void i40e_reprogram_flex_pit(struct i40e_pf *pf) /** * i40e_flow_str - Converts a flow_type into a human readable string - * @flow_type: the flow type from a flow specification + * @fsp: the flow specification * * Currently only flow types we support are included here, and the string * value attempts to match what ethtool would use to configure this flow type. @@ -3648,22 +5360,113 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, i40e_write_fd_input_set(pf, index, new_mask); - /* Add the new offset and update table, if necessary */ - if (new_flex_offset) { - err = i40e_add_flex_offset(&pf->l4_flex_pit_list, src_offset, - pit_index); - if (err) - return err; + /* IP_USER_FLOW filters match both IPv4/Other and IPv4/Fragmented + * frames. If we're programming the input set for IPv4/Other, we also + * need to program the IPv4/Fragmented input set. Since we don't have + * separate support, we'll always assume and enforce that the two flow + * types must have matching input sets. + */ + if (index == I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_FRAG_IPV4, + new_mask); + + /* Add the new offset and update table, if necessary */ + if (new_flex_offset) { + err = i40e_add_flex_offset(&pf->l4_flex_pit_list, src_offset, + pit_index); + if (err) + return err; + + if (flex_l3) { + err = i40e_add_flex_offset(&pf->l3_flex_pit_list, + src_offset, + pit_index); + if (err) + return err; + } + + i40e_reprogram_flex_pit(pf); + } + + return 0; +} + +/** + * i40e_match_fdir_filter - Return true of two filters match + * @a: pointer to filter struct + * @b: pointer to filter struct + * + * Returns true if the two filters match exactly the same criteria. I.e. they + * match the same flow type and have the same parameters. We don't need to + * check any input-set since all filters of the same flow type must use the + * same input set. + **/ +static bool i40e_match_fdir_filter(struct i40e_fdir_filter *a, + struct i40e_fdir_filter *b) +{ + /* The filters do not match if any of these criteria differ. */ + if (a->dst_ip != b->dst_ip || + a->src_ip != b->src_ip || + a->dst_port != b->dst_port || + a->src_port != b->src_port || + a->flow_type != b->flow_type || + a->ip4_proto != b->ip4_proto) + return false; + + return true; +} + +/** + * i40e_disallow_matching_filters - Check that new filters differ + * @vsi: pointer to the targeted VSI + * @input: new filter to check + * + * Due to hardware limitations, it is not possible for two filters that match + * similar criteria to be programmed at the same time. This is true for a few + * reasons: + * + * (a) all filters matching a particular flow type must use the same input + * set, that is they must match the same criteria. + * (b) different flow types will never match the same packet, as the flow type + * is decided by hardware before checking which rules apply. + * (c) hardware has no way to distinguish which order filters apply in. + * + * Due to this, we can't really support using the location data to order + * filters in the hardware parsing. It is technically possible for the user to + * request two filters matching the same criteria but which select different + * queues. In this case, rather than keep both filters in the list, we reject + * the 2nd filter when the user requests adding it. + * + * This avoids needing to track location for programming the filter to + * hardware, and ensures that we avoid some strange scenarios involving + * deleting filters which match the same criteria. + **/ +static int i40e_disallow_matching_filters(struct i40e_vsi *vsi, + struct i40e_fdir_filter *input) +{ + struct i40e_pf *pf = vsi->back; + struct i40e_fdir_filter *rule; + struct hlist_node *node2; + + /* Loop through every filter, and check that it doesn't match */ + hlist_for_each_entry_safe(rule, node2, + &pf->fdir_filter_list, fdir_node) { + /* Don't check the filters match if they share the same fd_id, + * since the new filter is actually just updating the target + * of the old filter. + */ + if (rule->fd_id == input->fd_id) + continue; - if (flex_l3) { - err = i40e_add_flex_offset(&pf->l3_flex_pit_list, - src_offset, - pit_index); - if (err) - return err; + /* If any filters match, then print a warning message to the + * kernel message buffer and bail out. + */ + if (i40e_match_fdir_filter(rule, input)) { + dev_warn(&pf->pdev->dev, + "Existing user defined filter %d already matches this flow.\n", + rule->fd_id); + return -EINVAL; } - - i40e_reprogram_flex_pit(pf); } return 0; @@ -3695,7 +5498,7 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED)) return -EOPNOTSUPP; - if (pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED) + if (test_bit(__I40E_FD_SB_AUTO_DISABLED, pf->state)) return -ENOSPC; if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) || @@ -3711,6 +5514,9 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, if (i40e_parse_rx_flow_user_data(fsp, &userdef)) return -EINVAL; + if (userdef.cloud_filter) + return i40e_add_cloud_filter_ethtool(vsi, cmd, &userdef); + /* Extended MAC field is not supported */ if (fsp->flow_type & FLOW_MAC_EXT) return -EINVAL; @@ -3752,7 +5558,6 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, } input = kzalloc(sizeof(*input), GFP_KERNEL); - if (!input) return -ENOMEM; @@ -3762,8 +5567,6 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, input->dest_ctl = dest_ctl; input->fd_status = I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID; input->cnt_index = I40E_FD_SB_STAT_IDX(pf->hw.pf_id); - input->dst_ip = fsp->h_u.tcp_ip4_spec.ip4src; - input->src_ip = fsp->h_u.tcp_ip4_spec.ip4dst; input->flow_type = fsp->flow_type & ~FLOW_EXT; input->ip4_proto = fsp->h_u.usr_ip4_spec.proto; @@ -3781,19 +5584,29 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, input->flex_offset = userdef.flex_offset; } - ret = i40e_add_del_fdir(vsi, input, true); + /* Avoid programming two filters with identical match criteria. */ + ret = i40e_disallow_matching_filters(vsi, input); if (ret) - goto free_input; + goto free_filter_memory; - /* Add the input filter to the fdir_input_list, possibly replacing + /* Add the input filter to the fdir_filter_list, possibly replacing * a previous filter. Do not free the input structure after adding it - * to the list as this would cause a use-after-free bug. + * to the list as this would cause a use after free bug. */ - i40e_update_ethtool_fdir_entry(vsi, input, fsp->location, NULL); + i40e_update_ethtool_fdir_entry(vsi, input, fsp->location); + + (void)i40e_del_cloud_filter_ethtool(pf, cmd); + ret = i40e_add_del_fdir(vsi, input, true); + + if (ret) + goto remove_sw_rule; return 0; -free_input: +remove_sw_rule: + hlist_del(&input->fdir_node); + pf->fdir_pf_active_filters--; +free_filter_memory: kfree(input); return ret; } @@ -3816,12 +5629,17 @@ static int i40e_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd) case ETHTOOL_SRXFH: ret = i40e_set_rss_hash_opt(pf, cmd); break; + case ETHTOOL_SRXCLSRLINS: ret = i40e_add_fdir_ethtool(vsi, cmd); break; + case ETHTOOL_SRXCLSRLDEL: ret = i40e_del_fdir_entry(vsi, cmd); + if (ret == -ENOENT) + ret = i40e_del_cloud_filter_ethtool(pf, cmd); break; + default: break; } @@ -3829,6 +5647,8 @@ static int i40e_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd) return ret; } +#endif /* ETHTOOL_GRXRINGS */ +#ifdef ETHTOOL_SCHANNELS /** * i40e_max_channels - get Max number of combined channels supported * @vsi: vsi pointer @@ -3841,7 +5661,7 @@ static unsigned int i40e_max_channels(struct i40e_vsi *vsi) /** * i40e_get_channels - Get the current channels enabled and max supported etc. - * @netdev: network interface device structure + * @dev: network interface device structure * @ch: ethtool channels structure * * We don't support separate tx and rx queues as channels. The other count @@ -3850,7 +5670,7 @@ static unsigned int i40e_max_channels(struct i40e_vsi *vsi) * q_vectors since we support a lot more queue pairs than q_vectors. **/ static void i40e_get_channels(struct net_device *dev, - struct ethtool_channels *ch) + struct ethtool_channels *ch) { struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; @@ -3869,14 +5689,14 @@ static void i40e_get_channels(struct net_device *dev, /** * i40e_set_channels - Set the new channels count. - * @netdev: network interface device structure + * @dev: network interface device structure * @ch: ethtool channels structure * * The new channels count may not be the same as requested by the user * since it gets rounded down to a power of 2 value. **/ static int i40e_set_channels(struct net_device *dev, - struct ethtool_channels *ch) + struct ethtool_channels *ch) { const u8 drop = I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET; struct i40e_netdev_priv *np = netdev_priv(dev); @@ -3892,6 +5712,12 @@ static int i40e_set_channels(struct net_device *dev, if (vsi->type != I40E_VSI_MAIN) return -EINVAL; + /* We do not support setting channels via ethtool when TCs are + * configured through mqprio + */ + if (pf->flags & I40E_FLAG_TC_MQPRIO) + return -EINVAL; + /* verify they are not requesting separate vectors */ if (!count || ch->rx_count || ch->tx_count) return -EINVAL; @@ -3937,6 +5763,9 @@ static int i40e_set_channels(struct net_device *dev, return -EINVAL; } +#endif /* ETHTOOL_SCHANNELS */ + +#if defined(ETHTOOL_GRSSH) && defined(ETHTOOL_SRSSH) /** * i40e_get_rxfh_key_size - get the RSS hash key size * @netdev: network interface device structure @@ -3959,8 +5788,22 @@ static u32 i40e_get_rxfh_indir_size(struct net_device *netdev) return I40E_HLUT_ARRAY_SIZE; } +/** + * i40e_get_rxfh - get the rx flow hash indirection table + * @netdev: network interface device structure + * @indir: indirection table + * @key: hash key + * @hfunc: hash function + * + * Reads the indirection table directly from the hardware. Returns 0 on + * success. + **/ +#ifdef HAVE_RXFH_HASHFUNC static int i40e_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, u8 *hfunc) +#else +static int i40e_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key) +#endif { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -3968,9 +5811,11 @@ static int i40e_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, int ret; u16 i; +#ifdef HAVE_RXFH_HASHFUNC if (hfunc) *hfunc = ETH_RSS_HASH_TOP; +#endif if (!indir) return 0; @@ -3995,12 +5840,22 @@ out: * @netdev: network interface device structure * @indir: indirection table * @key: hash key + * @hfunc: hash function to use * * Returns -EINVAL if the table specifies an invalid queue id, otherwise * returns 0 after programming the table. **/ +#ifdef HAVE_RXFH_HASHFUNC static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir, const u8 *key, const u8 hfunc) +#else +#ifdef HAVE_RXFH_NONCONST +static int i40e_set_rxfh(struct net_device *netdev, u32 *indir, u8 *key) +#else +static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir, + const u8 *key) +#endif /* HAVE_RXFH_NONCONST */ +#endif /* HAVE_RXFH_HASHFUNC */ { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -4008,8 +5863,18 @@ static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir, u8 *seed = NULL; u16 i; +#ifdef HAVE_RXFH_HASHFUNC if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP; +#endif + + /* Verify user input. */ + if (indir) { + for (i = 0; i < I40E_HLUT_ARRAY_SIZE; i++) { + if (indir[i] >= vsi->rss_size) + return -EINVAL; + } + } if (key) { if (!vsi->rss_hkey_user) { @@ -4038,7 +5903,9 @@ static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir, return i40e_config_rss(vsi, seed, vsi->rss_lut_user, I40E_HLUT_ARRAY_SIZE); } +#endif /* ETHTOOL_GRSSH && ETHTOOL_SRSSH */ +#ifdef HAVE_ETHTOOL_GET_SSET_COUNT /** * i40e_get_priv_flags - report device private flags * @dev: network interface device structure @@ -4088,9 +5955,12 @@ static u32 i40e_get_priv_flags(struct net_device *dev) static int i40e_set_priv_flags(struct net_device *dev, u32 flags) { struct i40e_netdev_priv *np = netdev_priv(dev); + u64 orig_flags, new_flags, changed_flags; + enum i40e_admin_queue_err adq_err; struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - u64 orig_flags, new_flags, changed_flags; + bool is_reset_needed; + i40e_status status; u32 i, j; orig_flags = READ_ONCE(pf->flags); @@ -4132,6 +6002,12 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) } flags_complete: + changed_flags = orig_flags ^ new_flags; + + is_reset_needed = !!(changed_flags & (I40E_FLAG_VEB_STATS_ENABLED | + I40E_FLAG_LEGACY_RX | I40E_FLAG_SOURCE_PRUNING_DISABLED | + I40E_FLAG_DISABLE_FW_LLDP)); + /* Before we finalize any flag changes, we need to perform some * checks to ensure that the changes are supported and safe. */ @@ -4141,19 +6017,30 @@ flags_complete: !(pf->hw_features & I40E_HW_ATR_EVICT_CAPABLE)) return -EOPNOTSUPP; - /* Compare and exchange the new flags into place. If we failed, that - * is if cmpxchg64 returns anything but the old value, this means that - * something else has modified the flags variable since we copied it - * originally. We'll just punt with an error and log something in the - * message buffer. + /* If the driver detected FW LLDP was disabled on init, this flag could + * be set, however we do not support _changing_ the flag: + * - on XL710 if NPAR is enabled or FW API version < 1.7 + * - on X722 with FW API version < 1.6 + * There are situations where older FW versions/NPAR enabled PFs could + * disable LLDP, however we _must_ not allow the user to enable/disable + * LLDP with this flag on unsupported FW versions. */ - if (cmpxchg64(&pf->flags, orig_flags, new_flags) != orig_flags) { - dev_warn(&pf->pdev->dev, - "Unable to update pf->flags as it was modified by another thread...\n"); - return -EAGAIN; + if (changed_flags & I40E_FLAG_DISABLE_FW_LLDP) { + if (!(pf->hw.flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE)) { + dev_warn(&pf->pdev->dev, + "Device does not support changing FW LLDP\n"); + return -EOPNOTSUPP; + } } - changed_flags = orig_flags ^ new_flags; + if (((changed_flags & I40E_FLAG_RS_FEC) || + (changed_flags & I40E_FLAG_BASE_R_FEC)) && + pf->hw.device_id != I40E_DEV_ID_25G_SFP28 && + pf->hw.device_id != I40E_DEV_ID_25G_B) { + dev_warn(&pf->pdev->dev, + "Device does not support changing FEC configuration\n"); + return -EOPNOTSUPP; + } /* Process any additional changes needed as a result of flag changes. * The changed_flags value reflects the list of bits that were @@ -4162,8 +6049,8 @@ flags_complete: /* Flush current ATR settings if ATR was disabled */ if ((changed_flags & I40E_FLAG_FD_ATR_ENABLED) && - !(pf->flags & I40E_FLAG_FD_ATR_ENABLED)) { - pf->flags |= I40E_FLAG_FD_ATR_AUTO_DISABLED; + !(new_flags & I40E_FLAG_FD_ATR_ENABLED)) { + set_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state); set_bit(__I40E_FD_FLUSH_REQUESTED, pf->state); } @@ -4171,11 +6058,11 @@ flags_complete: u16 sw_flags = 0, valid_flags = 0; int ret; - if (!(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) + if (!(new_flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) sw_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC; valid_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC; ret = i40e_aq_set_switch_config(&pf->hw, sw_flags, valid_flags, - NULL); + 0, NULL); if (ret && pf->hw.aq.asq_last_status != I40E_AQ_RC_ESRCH) { dev_info(&pf->pdev->dev, "couldn't set switch config bits, err %s aq_err %s\n", @@ -4186,17 +6073,399 @@ flags_complete: } } + if ((changed_flags & I40E_FLAG_RS_FEC) || + (changed_flags & I40E_FLAG_BASE_R_FEC)) { + u8 fec_cfg = 0; + + if (new_flags & I40E_FLAG_RS_FEC && + new_flags & I40E_FLAG_BASE_R_FEC) { + fec_cfg = I40E_AQ_SET_FEC_AUTO; + } else if (new_flags & I40E_FLAG_RS_FEC) { + fec_cfg = (I40E_AQ_SET_FEC_REQUEST_RS | + I40E_AQ_SET_FEC_ABILITY_RS); + } else if (new_flags & I40E_FLAG_BASE_R_FEC) { + fec_cfg = (I40E_AQ_SET_FEC_REQUEST_KR | + I40E_AQ_SET_FEC_ABILITY_KR); + } + if (i40e_set_fec_cfg(dev, fec_cfg)) + dev_warn(&pf->pdev->dev, "Cannot change FEC config\n"); + } + + if ((changed_flags & I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED) && + (orig_flags & I40E_FLAG_TOTAL_PORT_SHUTDOWN)) { + dev_err(&pf->pdev->dev, + "Setting link-down-on-close not supported on this port\n"); + return -EOPNOTSUPP; + } + + if ((changed_flags & new_flags & + I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED) && + (new_flags & I40E_FLAG_MFP_ENABLED)) + dev_warn(&pf->pdev->dev, + "Turning on link-down-on-close flag may affect other partitions\n"); + + if (changed_flags & I40E_FLAG_DISABLE_FW_LLDP) { + if (new_flags & I40E_FLAG_DISABLE_FW_LLDP) { + struct i40e_dcbx_config *dcbcfg; + + i40e_aq_stop_lldp(&pf->hw, true, false, NULL); + i40e_aq_set_dcb_parameters(&pf->hw, true, NULL); + /* reset local_dcbx_config to default */ + dcbcfg = &pf->hw.local_dcbx_config; + dcbcfg->etscfg.willing = 1; + dcbcfg->etscfg.maxtcs = 0; + dcbcfg->etscfg.tcbwtable[0] = 100; + for (i = 1; i < I40E_MAX_TRAFFIC_CLASS; i++) + dcbcfg->etscfg.tcbwtable[i] = 0; + for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) + dcbcfg->etscfg.prioritytable[i] = 0; + dcbcfg->etscfg.tsatable[0] = I40E_IEEE_TSA_ETS; + dcbcfg->pfc.willing = 1; + dcbcfg->pfc.pfccap = I40E_MAX_TRAFFIC_CLASS; + } else { + status = i40e_aq_start_lldp(&pf->hw, false, NULL); + if (status != I40E_SUCCESS) { + adq_err = pf->hw.aq.asq_last_status; + switch (adq_err) { + case I40E_AQ_RC_EEXIST: + dev_warn(&pf->pdev->dev, + "FW LLDP agent is already running\n"); + is_reset_needed = false; + break; + case I40E_AQ_RC_EPERM: + dev_warn(&pf->pdev->dev, + "Device configuration forbids SW from starting the LLDP agent.\n"); + return (-EINVAL); + default: + dev_warn(&pf->pdev->dev, + "Starting FW LLDP agent failed: error: %s, %s\n", + i40e_stat_str(&pf->hw, + status), + i40e_aq_str(&pf->hw, + adq_err)); + return (-EINVAL); + } + } + } + } + + /* Now that we've checked to ensure that the new flags are valid, load + * them into place. Since we only modify flags either (a) during + * initialization or (b) while holding the RTNL lock, we don't need + * anything fancy here. + */ + pf->flags = new_flags; + /* Issue reset to cause things to take effect, as additional bits * are added we will need to create a mask of bits requiring reset */ - if ((changed_flags & I40E_FLAG_VEB_STATS_ENABLED) || - ((changed_flags & I40E_FLAG_LEGACY_RX) && netif_running(dev))) + if (is_reset_needed) i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED), true); return 0; } +#endif /* HAVE_ETHTOOL_GET_SSET_COUNT */ + +#ifdef ETHTOOL_GMODULEINFO + +/** + * i40e_get_module_info - get (Q)SFP+ module type info + * @netdev: network interface device structure + * @modinfo: module EEPROM size and layout information structure + **/ +static int i40e_get_module_info(struct net_device *netdev, + struct ethtool_modinfo *modinfo) +{ + i40e_status status; + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; + u32 sff8472_comp = 0; + u32 sff8472_swap = 0; + u32 sff8636_rev = 0; + u8 type; + + /* Check if firmware supports reading module EEPROM. */ + if (!(hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE)) { + netdev_err(vsi->netdev, "Module EEPROM memory read not supported. Please update the NVM image.\n"); + return -EINVAL; + } + + status = i40e_update_link_info(hw); + if (status) + return -EIO; + + if (hw->phy.link_info.phy_type == I40E_PHY_TYPE_EMPTY) { + netdev_err(vsi->netdev, "Cannot read module EEPROM memory. No module connected.\n"); + return -EINVAL; + } + + type = hw->phy.link_info.module_type[0]; + + switch (type) { + case I40E_MODULE_TYPE_SFP: + status = i40e_aq_get_phy_register(hw, + I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE, + I40E_I2C_EEPROM_DEV_ADDR, true, + I40E_MODULE_SFF_8472_COMP, + &sff8472_comp, NULL); + if (status) + return -EIO; + + status = i40e_aq_get_phy_register(hw, + I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE, + I40E_I2C_EEPROM_DEV_ADDR, true, + I40E_MODULE_SFF_8472_SWAP, + &sff8472_swap, NULL); + if (status) + return -EIO; + + /* Check if the module requires address swap to access + * the other EEPROM memory page. + */ + if (sff8472_swap & I40E_MODULE_SFF_ADDR_MODE) { + netdev_warn(vsi->netdev, "Module address swap to access page 0xA2 is not supported.\n"); + modinfo->type = ETH_MODULE_SFF_8079; + modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; + } else if (sff8472_comp && + (sff8472_swap & I40E_MODULE_SFF_DIAG_CAPAB)) { + modinfo->type = ETH_MODULE_SFF_8472; + modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; + } else { + /* Module is not SFF-8472 compliant */ + modinfo->type = ETH_MODULE_SFF_8079; + modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; + } + break; + case I40E_MODULE_TYPE_QSFP_PLUS: + /* Read from memory page 0. */ + status = i40e_aq_get_phy_register(hw, + I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE, + 0, true, + I40E_MODULE_REVISION_ADDR, + &sff8636_rev, NULL); + if (status) + return -EIO; + /* Determine revision compliance byte */ + if (sff8636_rev > 0x02) { + /* Module is SFF-8636 compliant */ + modinfo->type = ETH_MODULE_SFF_8636; + modinfo->eeprom_len = I40E_MODULE_QSFP_MAX_LEN; + } else { + modinfo->type = ETH_MODULE_SFF_8436; + modinfo->eeprom_len = I40E_MODULE_QSFP_MAX_LEN; + } + break; + case I40E_MODULE_TYPE_QSFP28: + modinfo->type = ETH_MODULE_SFF_8636; + modinfo->eeprom_len = I40E_MODULE_QSFP_MAX_LEN; + break; + default: + netdev_err(vsi->netdev, "Module type unrecognized\n"); + return -EINVAL; + } + return 0; +} + +/** + * i40e_get_module_eeprom - fills buffer with (Q)SFP+ module memory contents + * @netdev: network interface device structure + * @ee: EEPROM dump request structure + * @data: buffer to be filled with EEPROM contents + **/ +static int i40e_get_module_eeprom(struct net_device *netdev, + struct ethtool_eeprom *ee, + u8 *data) +{ + i40e_status status; + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; + bool is_sfp = false; + u32 value = 0; + int i; + + if (!ee || !ee->len || !data) + return -EINVAL; + + if (hw->phy.link_info.module_type[0] == I40E_MODULE_TYPE_SFP) + is_sfp = true; + + for (i = 0; i < ee->len; i++) { + u32 offset = i + ee->offset; + u32 addr = is_sfp ? I40E_I2C_EEPROM_DEV_ADDR : 0; + + /* Check if we need to access the other memory page */ + if (is_sfp) { + if (offset >= ETH_MODULE_SFF_8079_LEN) { + offset -= ETH_MODULE_SFF_8079_LEN; + addr = I40E_I2C_EEPROM_DEV_ADDR2; + } + } else { + while (offset >= ETH_MODULE_SFF_8436_LEN) { + /* Compute memory page number and offset. */ + offset -= ETH_MODULE_SFF_8436_LEN / 2; + addr++; + } + } + + status = i40e_aq_get_phy_register(hw, + I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE, + addr, true, offset, &value, NULL); + if (status) + return -EIO; + data[i] = value; + } + return 0; +} +#endif /* ETHTOOL_GMODULEINFO */ + +#ifdef ETHTOOL_GEEE +static int i40e_get_eee(struct net_device *netdev, struct ethtool_eee *edata) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_aq_get_phy_abilities_resp phy_cfg; + i40e_status status = 0; + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; + + /* Get initial PHY capabilities */ + status = i40e_aq_get_phy_capabilities(hw, false, true, &phy_cfg, NULL); + if (status) + return -EAGAIN; + + /* Check whether NIC configuration is compatible with Energy Efficient + * Ethernet (EEE) mode. + */ + if (phy_cfg.eee_capability == 0) + return -EOPNOTSUPP; + + edata->supported = SUPPORTED_Autoneg; + edata->lp_advertised = edata->supported; + + /* Get current configuration */ + status = i40e_aq_get_phy_capabilities(hw, false, false, &phy_cfg, NULL); + if (status) + return -EAGAIN; + + edata->advertised = phy_cfg.eee_capability ? SUPPORTED_Autoneg : 0U; + edata->eee_enabled = !!edata->advertised; + edata->tx_lpi_enabled = pf->stats.tx_lpi_status; + + edata->eee_active = pf->stats.tx_lpi_status && pf->stats.rx_lpi_status; + + return 0; +} +#endif /* ETHTOOL_GEEE */ + +#ifdef ETHTOOL_SEEE +static int i40e_is_eee_param_supported(struct net_device *netdev, + struct ethtool_eee *edata) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + struct i40e_ethtool_not_used { + u32 value; + const char *name; + } param[] = { + {edata->advertised & ~SUPPORTED_Autoneg, "advertise"}, + {edata->tx_lpi_timer, "tx-timer"}, + {edata->tx_lpi_enabled != pf->stats.tx_lpi_status, "tx-lpi"} + }; + int i; + + for (i = 0; i < ARRAY_SIZE(param); i++) { + if (param[i].value) { + netdev_info(netdev, + "EEE setting %s not supported\n", + param[i].name); + return -EOPNOTSUPP; + } + } + + return 0; +} + +static int i40e_set_eee(struct net_device *netdev, struct ethtool_eee *edata) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_aq_get_phy_abilities_resp abilities; + i40e_status status = I40E_SUCCESS; + struct i40e_aq_set_phy_config config; + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; + __le16 eee_capability; + + /* Deny parameters we don't support */ + if (i40e_is_eee_param_supported(netdev, edata)) + return -EOPNOTSUPP; + + /* Get initial PHY capabilities */ + status = i40e_aq_get_phy_capabilities(hw, false, true, &abilities, + NULL); + if (status) + return -EAGAIN; + + /* Check whether NIC configuration is compatible with Energy Efficient + * Ethernet (EEE) mode. + */ + if (abilities.eee_capability == 0) + return -EOPNOTSUPP; + + /* Cache initial EEE capability */ + eee_capability = abilities.eee_capability; + + /* Get current PHY configuration */ + status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, + NULL); + if (status) + return -EAGAIN; + + /* Cache current PHY configuration */ + config.phy_type = abilities.phy_type; + config.link_speed = abilities.link_speed; + config.abilities = abilities.abilities | + I40E_AQ_PHY_ENABLE_ATOMIC_LINK; + config.eeer = abilities.eeer_val; + config.low_power_ctrl = abilities.d3_lpan; + config.fec_config = abilities.fec_cfg_curr_mod_ext_info & + I40E_AQ_PHY_FEC_CONFIG_MASK; + + /* Set desired EEE state */ + if (edata->eee_enabled) { + config.eee_capability = eee_capability; + config.eeer |= I40E_PRTPM_EEER_TX_LPI_EN_MASK; + } else { + config.eee_capability = 0; + config.eeer &= ~I40E_PRTPM_EEER_TX_LPI_EN_MASK; + } + + /* Apply modified PHY configuration */ + status = i40e_aq_set_phy_config(hw, &config, NULL); + if (status) + return -EAGAIN; + + return 0; +} +#endif /* ETHTOOL_SEEE */ + +static const struct ethtool_ops i40e_ethtool_recovery_mode_ops = { + .get_drvinfo = i40e_get_drvinfo, + .set_eeprom = i40e_set_eeprom, + .get_eeprom_len = i40e_get_eeprom_len, + .get_eeprom = i40e_get_eeprom, +}; static const struct ethtool_ops i40e_ethtool_ops = { +#ifndef ETHTOOL_GLINKSETTINGS + .get_settings = i40e_get_settings, + .set_settings = i40e_set_settings, +#endif .get_drvinfo = i40e_get_drvinfo, .get_regs_len = i40e_get_regs_len, .get_regs = i40e_get_regs, @@ -4213,31 +6482,141 @@ static const struct ethtool_ops i40e_ethtool_ops = { .set_pauseparam = i40e_set_pauseparam, .get_msglevel = i40e_get_msglevel, .set_msglevel = i40e_set_msglevel, +#ifndef HAVE_NDO_SET_FEATURES + .get_rx_csum = i40e_get_rx_csum, + .set_rx_csum = i40e_set_rx_csum, + .get_tx_csum = i40e_get_tx_csum, + .set_tx_csum = i40e_set_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, + .get_tso = ethtool_op_get_tso, + .set_tso = i40e_set_tso, +#ifdef ETHTOOL_GFLAGS + .get_flags = ethtool_op_get_flags, + .set_flags = i40e_set_flags, +#endif +#endif /* HAVE_NDO_SET_FEATURES */ +#ifdef ETHTOOL_GRXRINGS .get_rxnfc = i40e_get_rxnfc, .set_rxnfc = i40e_set_rxnfc, +#ifdef ETHTOOL_SRXNTUPLE + .set_rx_ntuple = i40e_set_rx_ntuple, +#endif +#endif +#ifndef HAVE_ETHTOOL_GET_SSET_COUNT + .self_test_count = i40e_diag_test_count, +#endif /* HAVE_ETHTOOL_GET_SSET_COUNT */ .self_test = i40e_diag_test, .get_strings = i40e_get_strings, +#ifndef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT +#ifdef ETHTOOL_GEEE + .get_eee = i40e_get_eee, +#endif /* ETHTOOL_GEEE */ +#ifdef ETHTOOL_SEEE + .set_eee = i40e_set_eee, +#endif /* ETHTOOL_SEEE */ +#ifdef HAVE_ETHTOOL_SET_PHYS_ID .set_phys_id = i40e_set_phys_id, +#else + .phys_id = i40e_phys_id, +#endif /* HAVE_ETHTOOL_SET_PHYS_ID */ +#endif /* HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT */ +#ifndef HAVE_ETHTOOL_GET_SSET_COUNT + .get_stats_count = i40e_get_stats_count, +#else /* HAVE_ETHTOOL_GET_SSET_COUNT */ .get_sset_count = i40e_get_sset_count, + .get_priv_flags = i40e_get_priv_flags, + .set_priv_flags = i40e_set_priv_flags, +#endif /* HAVE_ETHTOOL_GET_SSET_COUNT */ .get_ethtool_stats = i40e_get_ethtool_stats, +#ifdef HAVE_ETHTOOL_GET_PERM_ADDR + .get_perm_addr = ethtool_op_get_perm_addr, +#endif .get_coalesce = i40e_get_coalesce, .set_coalesce = i40e_set_coalesce, +#ifndef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT +#if defined(ETHTOOL_GRSSH) && defined(ETHTOOL_SRSSH) .get_rxfh_key_size = i40e_get_rxfh_key_size, .get_rxfh_indir_size = i40e_get_rxfh_indir_size, .get_rxfh = i40e_get_rxfh, .set_rxfh = i40e_set_rxfh, +#endif /* ETHTOOL_GRSSH && ETHTOOL_SRSSH */ +#ifdef ETHTOOL_SCHANNELS .get_channels = i40e_get_channels, .set_channels = i40e_set_channels, +#endif +#ifdef ETHTOOL_GMODULEINFO + .get_module_info = i40e_get_module_info, + .get_module_eeprom = i40e_get_module_eeprom, +#endif +#ifdef HAVE_ETHTOOL_GET_TS_INFO .get_ts_info = i40e_get_ts_info, - .get_priv_flags = i40e_get_priv_flags, - .set_priv_flags = i40e_set_priv_flags, +#endif /* HAVE_ETHTOOL_GET_TS_INFO */ +#ifdef ETHTOOL_PERQUEUE .get_per_queue_coalesce = i40e_get_per_queue_coalesce, .set_per_queue_coalesce = i40e_set_per_queue_coalesce, - .get_link_ksettings = i40e_get_link_ksettings, - .set_link_ksettings = i40e_set_link_ksettings, +#endif /* ETHTOOL_PERQUEUE */ +#endif /* HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT */ +#ifdef ETHTOOL_GLINKSETTINGS + .get_link_ksettings = i40e_get_link_ksettings, + .set_link_ksettings = i40e_set_link_ksettings, +#endif /* ETHTOOL_GLINKSETTINGS */ +#ifdef ETHTOOL_GFECPARAM + .get_fecparam = i40e_get_fec_param, + .set_fecparam = i40e_set_fec_param, +#endif /* ETHTOOL_GFECPARAM */ +#ifdef HAVE_DDP_PROFILE_UPLOAD_SUPPORT + .flash_device = i40e_ddp_flash, +#endif /* DDP_PROFILE_UPLOAD_SUPPORT */ +}; + +#ifdef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT +static const struct ethtool_ops_ext i40e_ethtool_ops_ext = { + .size = sizeof(struct ethtool_ops_ext), + .get_ts_info = i40e_get_ts_info, + .set_phys_id = i40e_set_phys_id, + .get_channels = i40e_get_channels, + .set_channels = i40e_set_channels, +#if defined(ETHTOOL_GRSSH) && defined(ETHTOOL_SRSSH) + .get_rxfh_key_size = i40e_get_rxfh_key_size, + .get_rxfh_indir_size = i40e_get_rxfh_indir_size, + .get_rxfh = i40e_get_rxfh, + .set_rxfh = i40e_set_rxfh, +#endif /* ETHTOOL_GRSSH && ETHTOOL_SRSSH */ +#ifdef ETHTOOL_GEEE + .get_eee = i40e_get_eee, +#endif /* ETHTOOL_GEEE */ +#ifdef ETHTOOL_SEEE + .set_eee = i40e_set_eee, +#endif /* ETHTOOL_SEEE */ +#ifdef ETHTOOL_GMODULEINFO + .get_module_info = i40e_get_module_info, + .get_module_eeprom = i40e_get_module_eeprom, +#endif }; void i40e_set_ethtool_ops(struct net_device *netdev) { - netdev->ethtool_ops = &i40e_ethtool_ops; + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_pf *pf = np->vsi->back; + + if (test_bit(__I40E_RECOVERY_MODE, pf->state)) { + netdev->ethtool_ops = &i40e_ethtool_recovery_mode_ops; + } else { + netdev->ethtool_ops = &i40e_ethtool_ops; + set_ethtool_ops_ext(netdev, &i40e_ethtool_ops_ext); + } +} +#else +void i40e_set_ethtool_ops(struct net_device *netdev) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_pf *pf = np->vsi->back; + + if (test_bit(__I40E_RECOVERY_MODE, pf->state)) + netdev->ethtool_ops = &i40e_ethtool_recovery_mode_ops; + else + netdev->ethtool_ops = &i40e_ethtool_ops; } +#endif /* HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT */ +#endif /* SIOCETHTOOL */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool_stats.h b/drivers/net/ethernet/intel/i40e/i40e_ethtool_stats.h new file mode 100644 index 000000000..dc26d38ee --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool_stats.h @@ -0,0 +1,293 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ + +/* ethtool statistics helpers */ + +/** + * struct i40e_stats - definition for an ethtool statistic + * @stat_string: statistic name to display in ethtool -S output + * @sizeof_stat: the sizeof() the stat, must be no greater than sizeof(u64) + * @stat_offset: offsetof() the stat from a base pointer + * + * This structure defines a statistic to be added to the ethtool stats buffer. + * It defines a statistic as offset from a common base pointer. Stats should + * be defined in constant arrays using the I40E_STAT macro, with every element + * of the array using the same _type for calculating the sizeof_stat and + * stat_offset. + * + * The @sizeof_stat is expected to be sizeof(u8), sizeof(u16), sizeof(u32) or + * sizeof(u64). Other sizes are not expected and will produce a WARN_ONCE from + * the i40e_add_ethtool_stat() helper function. + * + * The @stat_string is interpreted as a format string, allowing formatted + * values to be inserted while looping over multiple structures for a given + * statistics array. Thus, every statistic string in an array should have the + * same type and number of format specifiers, to be formatted by variadic + * arguments to the i40e_add_stat_string() helper function. + **/ +struct i40e_stats { + char stat_string[ETH_GSTRING_LEN]; + int sizeof_stat; + int stat_offset; +}; + +/* Helper macro to define an i40e_stat structure with proper size and type. + * Use this when defining constant statistics arrays. Note that @_type expects + * only a type name and is used multiple times. + */ +#define I40E_STAT(_type, _name, _stat) { \ + .stat_string = _name, \ + .sizeof_stat = sizeof_field(_type, _stat), \ + .stat_offset = offsetof(_type, _stat) \ +} + +/* Helper macro for defining some statistics directly copied from the netdev + * stats structure. + */ +#ifdef HAVE_NDO_GET_STATS64 +#define I40E_NETDEV_STAT(_net_stat) \ + I40E_STAT(struct rtnl_link_stats64, #_net_stat, _net_stat) +#else +#define I40E_NETDEV_STAT(_net_stat) \ + I40E_STAT(struct net_device_stats, #_net_stat, _net_stat) +#endif + +/* Helper macro for defining some statistics related to queues */ +#define I40E_QUEUE_STAT(_name, _stat) \ + I40E_STAT(struct i40e_ring, _name, _stat) + +/* Stats associated with a Tx or Rx ring */ +static const struct i40e_stats i40e_gstrings_queue_stats[] = { + I40E_QUEUE_STAT("%s-%u.packets", stats.packets), + I40E_QUEUE_STAT("%s-%u.bytes", stats.bytes), +}; + +#ifdef HAVE_XDP_SUPPORT +/* Stats associated with Rx ring's XDP prog */ +static const struct i40e_stats i40e_gstrings_rx_queue_xdp_stats[] = { + I40E_QUEUE_STAT("%s-%u.xdp.pass", xdp_stats.xdp_pass), + I40E_QUEUE_STAT("%s-%u.xdp.drop", xdp_stats.xdp_drop), + I40E_QUEUE_STAT("%s-%u.xdp.tx", xdp_stats.xdp_tx), + I40E_QUEUE_STAT("%s-%u.xdp.unknown", xdp_stats.xdp_unknown), + I40E_QUEUE_STAT("%s-%u.xdp.redirect", xdp_stats.xdp_redirect), + I40E_QUEUE_STAT("%s-%u.xdp.redirect_fail", xdp_stats.xdp_redirect_fail), +}; +#endif + +/** + * i40e_add_one_ethtool_stat - copy the stat into the supplied buffer + * @data: location to store the stat value + * @pointer: basis for where to copy from + * @stat: the stat definition + * + * Copies the stat data defined by the pointer and stat structure pair into + * the memory supplied as data. Used to implement i40e_add_ethtool_stats and + * i40e_add_queue_stats. If the pointer is null, data will be zero'd. + */ +static void +i40e_add_one_ethtool_stat(u64 *data, void *pointer, + const struct i40e_stats *stat) +{ + char *p; + + if (!pointer) { + /* ensure that the ethtool data buffer is zero'd for any stats + * which don't have a valid pointer. + */ + *data = 0; + return; + } + + p = (char *)pointer + stat->stat_offset; + switch (stat->sizeof_stat) { + case sizeof(u64): + *data = *((u64 *)p); + break; + case sizeof(u32): + *data = *((u32 *)p); + break; + case sizeof(u16): + *data = *((u16 *)p); + break; + case sizeof(u8): + *data = *((u8 *)p); + break; + default: + WARN_ONCE(1, "unexpected stat size for %s", + stat->stat_string); + *data = 0; + } +} + +/** + * __i40e_add_ethtool_stats - copy stats into the ethtool supplied buffer + * @data: ethtool stats buffer + * @pointer: location to copy stats from + * @stats: array of stats to copy + * @size: the size of the stats definition + * + * Copy the stats defined by the stats array using the pointer as a base into + * the data buffer supplied by ethtool. Updates the data pointer to point to + * the next empty location for successive calls to __i40e_add_ethtool_stats. + * If pointer is null, set the data values to zero and update the pointer to + * skip these stats. + **/ +static void +__i40e_add_ethtool_stats(u64 **data, void *pointer, + const struct i40e_stats stats[], + const unsigned int size) +{ + unsigned int i; + + for (i = 0; i < size; i++) + i40e_add_one_ethtool_stat((*data)++, pointer, &stats[i]); +} + +/** + * i40e_add_ethtool_stats - copy stats into ethtool supplied buffer + * @data: ethtool stats buffer + * @pointer: location where stats are stored + * @stats: static const array of stat definitions + * + * Macro to ease the use of __i40e_add_ethtool_stats by taking a static + * constant stats array and passing the ARRAY_SIZE(). This avoids typos by + * ensuring that we pass the size associated with the given stats array. + * + * The parameter @stats is evaluated twice, so parameters with side effects + * should be avoided. + **/ +#define i40e_add_ethtool_stats(data, pointer, stats) \ + __i40e_add_ethtool_stats(data, pointer, stats, ARRAY_SIZE(stats)) + +/** + * i40e_add_queue_stats - copy queue statistics into supplied buffer + * @data: ethtool stats buffer + * @ring: the ring to copy + * + * Queue statistics must be copied while protected by + * u64_stats_fetch_begin_irq, so we can't directly use i40e_add_ethtool_stats. + * Assumes that queue stats are defined in i40e_gstrings_queue_stats. If the + * ring pointer is null, zero out the queue stat values and update the data + * pointer. Otherwise safely copy the stats from the ring into the supplied + * buffer and update the data pointer when finished. + * + * This function expects to be called while under rcu_read_lock(). + **/ +static void +i40e_add_queue_stats(u64 **data, struct i40e_ring *ring) +{ + const unsigned int size = ARRAY_SIZE(i40e_gstrings_queue_stats); + const struct i40e_stats *stats = i40e_gstrings_queue_stats; +#ifdef HAVE_NDO_GET_STATS64 + unsigned int start; +#endif + unsigned int i; + + /* To avoid invalid statistics values, ensure that we keep retrying + * the copy until we get a consistent value according to + * u64_stats_fetch_retry_irq. But first, make sure our ring is + * non-null before attempting to access its syncp. + */ +#ifdef HAVE_NDO_GET_STATS64 + do { + start = !ring ? 0 : u64_stats_fetch_begin_irq(&ring->syncp); +#endif + for (i = 0; i < size; i++) { + i40e_add_one_ethtool_stat(&(*data)[i], ring, + &stats[i]); + } +#ifdef HAVE_NDO_GET_STATS64 + } while (ring && u64_stats_fetch_retry_irq(&ring->syncp, start)); +#endif + + /* Once we successfully copy the stats in, update the data pointer */ + *data += size; +} + +#ifdef HAVE_XDP_SUPPORT +/** + * i40e_add_rx_queue_xdp_stats - copy XDP statistics into supplied buffer + * @data: ethtool stats buffer + * @rx_ring: the rx ring to copy + * + * RX queue XDP statistics must be copied while protected by + * u64_stats_fetch_begin_irq, so we can't directly use i40e_add_ethtool_stats. + * Assumes that queue stats are defined in i40e_gstrings_rx_queue_xdp_stats. If + * the ring pointer is null, zero out the queue stat values and update the data + * pointer. Otherwise safely copy the stats from the ring into the supplied + * buffer and update the data pointer when finished. + * + * This function expects to be called while under rcu_read_lock(). + **/ +static void +i40e_add_rx_queue_xdp_stats(u64 **data, struct i40e_ring *rx_ring) +{ + const unsigned int xdp_size = + ARRAY_SIZE(i40e_gstrings_rx_queue_xdp_stats); + const struct i40e_stats *xdp_stats = i40e_gstrings_rx_queue_xdp_stats; +#ifdef HAVE_NDO_GET_STATS64 + unsigned int start; +#endif + unsigned int i; + + /* To avoid invalid statistics values, ensure that we keep retrying + * the copy until we get a consistent value according to + * u64_stats_fetch_retry_irq. But first, make sure our ring is + * non-null before attempting to access its syncp. + */ +#ifdef HAVE_NDO_GET_STATS64 + do { + start = !rx_ring ? 0 : + u64_stats_fetch_begin_irq(&rx_ring->syncp); +#endif + for (i = 0; i < xdp_size; i++) { + i40e_add_one_ethtool_stat(&(*data)[i], rx_ring, + &xdp_stats[i]); + } +#ifdef HAVE_NDO_GET_STATS64 + } while (rx_ring && u64_stats_fetch_retry_irq(&rx_ring->syncp, start)); +#endif + + /* Once we successfully copy the stats in, update the data pointer */ + *data += xdp_size; +} +#endif + +/** + * __i40e_add_stat_strings - copy stat strings into ethtool buffer + * @p: ethtool supplied buffer + * @stats: stat definitions array + * @size: size of the stats array + * + * Format and copy the strings described by stats into the buffer pointed at + * by p. + **/ +static void __i40e_add_stat_strings(u8 **p, const struct i40e_stats stats[], + const unsigned int size, ...) +{ + unsigned int i; + + for (i = 0; i < size; i++) { + va_list args; + + va_start(args, size); + vsnprintf(*p, ETH_GSTRING_LEN, stats[i].stat_string, args); + *p += ETH_GSTRING_LEN; + va_end(args); + } +} + +/** + * 40e_add_stat_strings - copy stat strings into ethtool buffer + * @p: ethtool supplied buffer + * @stats: stat definitions array + * + * Format and copy the strings described by the const static stats value into + * the buffer pointed at by p. + * + * The parameter @stats is evaluated twice, so parameters with side effects + * should be avoided. Additionally, stats must be an array such that + * ARRAY_SIZE can be called on it. + **/ +#define i40e_add_stat_strings(p, stats, ...) \ + __i40e_add_stat_strings(p, stats, ARRAY_SIZE(stats), ## __VA_ARGS__) diff --git a/drivers/net/ethernet/intel/i40e/i40e_filters.c b/drivers/net/ethernet/intel/i40e/i40e_filters.c new file mode 100644 index 000000000..ce301a41e --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/i40e_filters.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ + +#include "i40e_filters.h" + +/** + * __i40e_del_filter - Remove a specific filter from the VSI + * @vsi: VSI to remove from + * @f: the filter to remove from the list + * + * This function should be called instead of i40e_del_filter only if you know + * the exact filter you will remove already, such as via i40e_find_filter or + * i40e_find_mac. + * + * NOTE: This function is expected to be called with mac_filter_hash_lock + * being held. + * ANOTHER NOTE: This function MUST be called from within the context of + * the "safe" variants of any list iterators, e.g. list_for_each_entry_safe() + * instead of list_for_each_entry(). + **/ +void __i40e_del_filter(struct i40e_vsi *vsi, struct i40e_mac_filter *f) +{ + if (!f) + return; + + /* If the filter was never added to firmware then we can just delete it + * directly and we don't want to set the status to remove or else an + * admin queue command will unnecessarily fire. + */ + if (f->state == I40E_FILTER_FAILED || f->state == I40E_FILTER_NEW) { + hash_del(&f->hlist); + kfree(f); + } else { + f->state = I40E_FILTER_REMOVE; + } + + vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; + set_bit(__I40E_MACVLAN_SYNC_PENDING, vsi->back->state); +} + diff --git a/drivers/net/ethernet/intel/i40e/i40e_filters.h b/drivers/net/ethernet/intel/i40e/i40e_filters.h new file mode 100644 index 000000000..7f618fdab --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/i40e_filters.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ + +#ifndef _I40E_FILTERS_H_ +#define _I40E_FILTERS_H_ + +#include "i40e.h" + +void __i40e_del_filter(struct i40e_vsi *vsi, struct i40e_mac_filter *f); + +#endif /* _I40E_FILTERS_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_helper.h b/drivers/net/ethernet/intel/i40e/i40e_helper.h new file mode 100644 index 000000000..4e25e230c --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/i40e_helper.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ + +#ifndef _I40E_HELPER_H_ +#define _I40E_HELPER_H_ + +#include "i40e_alloc.h" + +/** + * i40e_allocate_dma_mem_d - OS specific memory alloc for shared code + * @hw: pointer to the HW structure + * @mem: ptr to mem struct to fill out + * @size: size of memory requested + * @alignment: what to align the allocation to + **/ +inline int i40e_allocate_dma_mem_d(struct i40e_hw *hw, + struct i40e_dma_mem *mem, + __always_unused enum i40e_memory_type mtype, + u64 size, u32 alignment) +{ + struct i40e_pf *nf = (struct i40e_pf *)hw->back; + + mem->size = ALIGN(size, alignment); +#ifdef HAVE_DMA_ALLOC_COHERENT_ZEROES_MEM + mem->va = dma_alloc_coherent(&nf->pdev->dev, mem->size, + &mem->pa, GFP_KERNEL); +#else + mem->va = dma_zalloc_coherent(&nf->pdev->dev, mem->size, + &mem->pa, GFP_KERNEL); +#endif + if (!mem->va) + return -ENOMEM; + + return 0; +} + +/** + * i40e_free_dma_mem_d - OS specific memory free for shared code + * @hw: pointer to the HW structure + * @mem: ptr to mem struct to free + **/ +inline int i40e_free_dma_mem_d(struct i40e_hw *hw, struct i40e_dma_mem *mem) +{ + struct i40e_pf *nf = (struct i40e_pf *)hw->back; + + dma_free_coherent(&nf->pdev->dev, mem->size, mem->va, mem->pa); + mem->va = NULL; + mem->pa = 0; + mem->size = 0; + + return 0; +} + +/** + * i40e_allocate_virt_mem_d - OS specific memory alloc for shared code + * @hw: pointer to the HW structure + * @mem: ptr to mem struct to fill out + * @size: size of memory requested + **/ +inline int i40e_allocate_virt_mem_d(struct i40e_hw *hw, + struct i40e_virt_mem *mem, + u32 size) +{ + mem->size = size; + mem->va = kzalloc(size, GFP_KERNEL); + + if (!mem->va) + return -ENOMEM; + + return 0; +} + +/** + * i40e_free_virt_mem_d - OS specific memory free for shared code + * @hw: pointer to the HW structure + * @mem: ptr to mem struct to free + **/ +inline int i40e_free_virt_mem_d(struct i40e_hw *hw, struct i40e_virt_mem *mem) +{ + /* it's ok to kfree a NULL pointer */ + kfree(mem->va); + mem->va = NULL; + mem->size = 0; + + return 0; +} + +/* prototype */ +inline void i40e_destroy_spinlock_d(struct i40e_spinlock *sp); +inline void i40e_acquire_spinlock_d(struct i40e_spinlock *sp); +inline void i40e_release_spinlock_d(struct i40e_spinlock *sp); + +/** + * i40e_init_spinlock_d - OS specific spinlock init for shared code + * @sp: pointer to a spinlock declared in driver space + **/ +static inline void i40e_init_spinlock_d(struct i40e_spinlock *sp) +{ + mutex_init((struct mutex *)sp); +} + +/** + * i40e_acquire_spinlock_d - OS specific spinlock acquire for shared code + * @sp: pointer to a spinlock declared in driver space + **/ +inline void i40e_acquire_spinlock_d(struct i40e_spinlock *sp) +{ + mutex_lock((struct mutex *)sp); +} + +/** + * i40e_release_spinlock_d - OS specific spinlock release for shared code + * @sp: pointer to a spinlock declared in driver space + **/ +inline void i40e_release_spinlock_d(struct i40e_spinlock *sp) +{ + mutex_unlock((struct mutex *)sp); +} + +/** + * i40e_destroy_spinlock_d - OS specific spinlock destroy for shared code + * @sp: pointer to a spinlock declared in driver space + **/ +inline void i40e_destroy_spinlock_d(struct i40e_spinlock *sp) +{ + mutex_destroy((struct mutex *)sp); +} +#endif /* _I40E_HELPER_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_hmc.c index a7c7b1d9b..5fb10c407 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_hmc.c +++ b/drivers/net/ethernet/intel/i40e/i40e_hmc.c @@ -1,35 +1,14 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #include "i40e_osdep.h" #include "i40e_register.h" #include "i40e_status.h" #include "i40e_alloc.h" #include "i40e_hmc.h" +#ifndef I40E_NO_TYPE_HEADER #include "i40e_type.h" +#endif /** * i40e_add_sd_table_entry - Adds a segment descriptor to the table @@ -45,11 +24,11 @@ i40e_status i40e_add_sd_table_entry(struct i40e_hw *hw, enum i40e_sd_entry_type type, u64 direct_mode_sz) { - enum i40e_memory_type mem_type __attribute__((unused)); + i40e_status ret_code = I40E_SUCCESS; struct i40e_hmc_sd_entry *sd_entry; + enum i40e_memory_type mem_type; bool dma_mem_alloc_done = false; struct i40e_dma_mem mem; - i40e_status ret_code = I40E_SUCCESS; u64 alloc_len; if (NULL == hmc_info->sd_table.sd_entry) { @@ -89,9 +68,13 @@ i40e_status i40e_add_sd_table_entry(struct i40e_hw *hw, sd_entry->u.pd_table.pd_entry = (struct i40e_hmc_pd_entry *) sd_entry->u.pd_table.pd_entry_virt_mem.va; - sd_entry->u.pd_table.pd_page_addr = mem; + i40e_memcpy(&sd_entry->u.pd_table.pd_page_addr, + &mem, sizeof(struct i40e_dma_mem), + I40E_NONDMA_TO_NONDMA); } else { - sd_entry->u.bp.addr = mem; + i40e_memcpy(&sd_entry->u.bp.addr, + &mem, sizeof(struct i40e_dma_mem), + I40E_NONDMA_TO_NONDMA); sd_entry->u.bp.sd_pd_index = sd_index; } /* initialize the sd entry */ @@ -104,7 +87,7 @@ i40e_status i40e_add_sd_table_entry(struct i40e_hw *hw, if (I40E_SD_TYPE_DIRECT == sd_entry->entry_type) I40E_INC_BP_REFCNT(&sd_entry->u.bp); exit: - if (ret_code) + if (I40E_SUCCESS != ret_code) if (dma_mem_alloc_done) i40e_free_dma_mem(hw, &mem); @@ -133,7 +116,7 @@ i40e_status i40e_add_pd_table_entry(struct i40e_hw *hw, u32 pd_index, struct i40e_dma_mem *rsrc_pg) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; struct i40e_hmc_pd_table *pd_table; struct i40e_hmc_pd_entry *pd_entry; struct i40e_dma_mem mem; @@ -171,7 +154,8 @@ i40e_status i40e_add_pd_table_entry(struct i40e_hw *hw, pd_entry->rsrc_pg = false; } - pd_entry->bp.addr = *page; + i40e_memcpy(&pd_entry->bp.addr, page, + sizeof(struct i40e_dma_mem), I40E_NONDMA_TO_NONDMA); pd_entry->bp.sd_pd_index = pd_index; pd_entry->bp.entry_type = I40E_SD_TYPE_PAGED; /* Set page address and valid bit */ @@ -181,7 +165,8 @@ i40e_status i40e_add_pd_table_entry(struct i40e_hw *hw, pd_addr += rel_pd_idx; /* Add the backing page physical address in the pd entry */ - memcpy(pd_addr, &page_desc, sizeof(u64)); + i40e_memcpy(pd_addr, &page_desc, sizeof(u64), + I40E_NONDMA_TO_DMA); pd_entry->sd_index = sd_idx; pd_entry->valid = true; @@ -197,7 +182,6 @@ exit: * @hw: pointer to our HW structure * @hmc_info: pointer to the HMC configuration information structure * @idx: the page index - * @is_pf: distinguishes a VF from a PF * * This function: * 1. Marks the entry in pd tabe (for paged address mode) or in sd table @@ -212,7 +196,7 @@ i40e_status i40e_remove_pd_bp(struct i40e_hw *hw, struct i40e_hmc_info *hmc_info, u32 idx) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; struct i40e_hmc_pd_entry *pd_entry; struct i40e_hmc_pd_table *pd_table; struct i40e_hmc_sd_entry *sd_entry; @@ -245,13 +229,13 @@ i40e_status i40e_remove_pd_bp(struct i40e_hw *hw, I40E_DEC_PD_REFCNT(pd_table); pd_addr = (u64 *)pd_table->pd_page_addr.va; pd_addr += rel_pd_idx; - memset(pd_addr, 0, sizeof(u64)); + i40e_memset(pd_addr, 0, sizeof(u64), I40E_DMA_MEM); I40E_INVALIDATE_PF_HMC_PD(hw, sd_idx, idx); /* free memory here */ if (!pd_entry->rsrc_pg) - ret_code = i40e_free_dma_mem(hw, &pd_entry->bp.addr); - if (ret_code) + ret_code = i40e_free_dma_mem(hw, &(pd_entry->bp.addr)); + if (I40E_SUCCESS != ret_code) goto exit; if (!pd_table->ref_cnt) i40e_free_virt_mem(hw, &pd_table->pd_entry_virt_mem); @@ -267,7 +251,7 @@ exit: i40e_status i40e_prep_remove_sd_bp(struct i40e_hmc_info *hmc_info, u32 idx) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; struct i40e_hmc_sd_entry *sd_entry; /* get the entry and decrease its ref counter */ @@ -305,7 +289,7 @@ i40e_status i40e_remove_sd_bp_new(struct i40e_hw *hw, sd_entry = &hmc_info->sd_table.sd_entry[idx]; I40E_CLEAR_PF_SD_ENTRY(hw, idx, I40E_SD_TYPE_DIRECT); - return i40e_free_dma_mem(hw, &sd_entry->u.bp.addr); + return i40e_free_dma_mem(hw, &(sd_entry->u.bp.addr)); } /** @@ -316,7 +300,7 @@ i40e_status i40e_remove_sd_bp_new(struct i40e_hw *hw, i40e_status i40e_prep_remove_pd_page(struct i40e_hmc_info *hmc_info, u32 idx) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; struct i40e_hmc_sd_entry *sd_entry; sd_entry = &hmc_info->sd_table.sd_entry[idx]; @@ -353,5 +337,5 @@ i40e_status i40e_remove_pd_page_new(struct i40e_hw *hw, sd_entry = &hmc_info->sd_table.sd_entry[idx]; I40E_CLEAR_PF_SD_ENTRY(hw, idx, I40E_SD_TYPE_PAGED); - return i40e_free_dma_mem(hw, &sd_entry->u.pd_table.pd_page_addr); + return i40e_free_dma_mem(hw, &(sd_entry->u.pd_table.pd_page_addr)); } diff --git a/drivers/net/ethernet/intel/i40e/i40e_hmc.h b/drivers/net/ethernet/intel/i40e/i40e_hmc.h index d90669211..ddfef1603 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_hmc.h +++ b/drivers/net/ethernet/intel/i40e/i40e_hmc.h @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_HMC_H_ #define _I40E_HMC_H_ diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c index daa920442..4e55a095e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c +++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #include "i40e_osdep.h" #include "i40e_register.h" @@ -101,7 +78,7 @@ i40e_status i40e_init_lan_hmc(struct i40e_hw *hw, u32 txq_num, u32 fcoe_filt_num) { struct i40e_hmc_obj_info *obj, *full_obj; - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; u64 l2fpm_size; u32 size_exp; @@ -136,7 +113,7 @@ i40e_status i40e_init_lan_hmc(struct i40e_hw *hw, u32 txq_num, ret_code = I40E_ERR_INVALID_HMC_OBJ_COUNT; hw_dbg(hw, "i40e_init_lan_hmc: Tx context: asks for 0x%x but max allowed is 0x%x, returns error %d\n", txq_num, obj->max_cnt, ret_code); - goto init_lan_hmc_out; + goto free_hmc_out; } /* aggregate values into the full LAN object for later */ @@ -159,7 +136,7 @@ i40e_status i40e_init_lan_hmc(struct i40e_hw *hw, u32 txq_num, ret_code = I40E_ERR_INVALID_HMC_OBJ_COUNT; hw_dbg(hw, "i40e_init_lan_hmc: Rx context: asks for 0x%x but max allowed is 0x%x, returns error %d\n", rxq_num, obj->max_cnt, ret_code); - goto init_lan_hmc_out; + goto free_hmc_out; } /* aggregate values into the full LAN object for later */ @@ -182,7 +159,7 @@ i40e_status i40e_init_lan_hmc(struct i40e_hw *hw, u32 txq_num, ret_code = I40E_ERR_INVALID_HMC_OBJ_COUNT; hw_dbg(hw, "i40e_init_lan_hmc: FCoE context: asks for 0x%x but max allowed is 0x%x, returns error %d\n", fcoe_cntx_num, obj->max_cnt, ret_code); - goto init_lan_hmc_out; + goto free_hmc_out; } /* aggregate values into the full LAN object for later */ @@ -205,7 +182,7 @@ i40e_status i40e_init_lan_hmc(struct i40e_hw *hw, u32 txq_num, ret_code = I40E_ERR_INVALID_HMC_OBJ_COUNT; hw_dbg(hw, "i40e_init_lan_hmc: FCoE filter: asks for 0x%x but max allowed is 0x%x, returns error %d\n", fcoe_filt_num, obj->max_cnt, ret_code); - goto init_lan_hmc_out; + goto free_hmc_out; } /* aggregate values into the full LAN object for later */ @@ -226,7 +203,7 @@ i40e_status i40e_init_lan_hmc(struct i40e_hw *hw, u32 txq_num, (sizeof(struct i40e_hmc_sd_entry) * hw->hmc.sd_table.sd_cnt)); if (ret_code) - goto init_lan_hmc_out; + goto free_hmc_out; hw->hmc.sd_table.sd_entry = (struct i40e_hmc_sd_entry *)hw->hmc.sd_table.addr.va; } @@ -234,6 +211,11 @@ i40e_status i40e_init_lan_hmc(struct i40e_hw *hw, u32 txq_num, full_obj->size = l2fpm_size; init_lan_hmc_out: + return ret_code; +free_hmc_out: + if (hw->hmc.hmc_obj_virt_mem.va) + i40e_free_virt_mem(hw, &hw->hmc.hmc_obj_virt_mem); + return ret_code; } @@ -255,9 +237,9 @@ static i40e_status i40e_remove_pd_page(struct i40e_hw *hw, struct i40e_hmc_info *hmc_info, u32 idx) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; - if (!i40e_prep_remove_pd_page(hmc_info, idx)) + if (i40e_prep_remove_pd_page(hmc_info, idx) == I40E_SUCCESS) ret_code = i40e_remove_pd_page_new(hw, hmc_info, idx, true); return ret_code; @@ -282,9 +264,9 @@ static i40e_status i40e_remove_sd_bp(struct i40e_hw *hw, struct i40e_hmc_info *hmc_info, u32 idx) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; - if (!i40e_prep_remove_sd_bp(hmc_info, idx)) + if (i40e_prep_remove_sd_bp(hmc_info, idx) == I40E_SUCCESS) ret_code = i40e_remove_sd_bp_new(hw, hmc_info, idx, true); return ret_code; @@ -301,7 +283,7 @@ static i40e_status i40e_remove_sd_bp(struct i40e_hw *hw, static i40e_status i40e_create_lan_hmc_object(struct i40e_hw *hw, struct i40e_hmc_lan_create_obj_info *info) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; struct i40e_hmc_sd_entry *sd_entry; u32 pd_idx1 = 0, pd_lmt1 = 0; u32 pd_idx = 0, pd_lmt = 0; @@ -371,7 +353,7 @@ static i40e_status i40e_create_lan_hmc_object(struct i40e_hw *hw, ret_code = i40e_add_sd_table_entry(hw, info->hmc_info, j, info->entry_type, sd_size); - if (ret_code) + if (I40E_SUCCESS != ret_code) goto exit_sd_error; sd_entry = &info->hmc_info->sd_table.sd_entry[j]; if (I40E_SD_TYPE_PAGED == sd_entry->entry_type) { @@ -388,7 +370,7 @@ static i40e_status i40e_create_lan_hmc_object(struct i40e_hw *hw, ret_code = i40e_add_pd_table_entry(hw, info->hmc_info, i, NULL); - if (ret_code) { + if (I40E_SUCCESS != ret_code) { pd_error = true; break; } @@ -461,9 +443,9 @@ i40e_status i40e_configure_lan_hmc(struct i40e_hw *hw, enum i40e_hmc_model model) { struct i40e_hmc_lan_create_obj_info info; - i40e_status ret_code = 0; u8 hmc_fn_id = hw->hmc.hmc_fn_id; struct i40e_hmc_obj_info *obj; + i40e_status ret_code = I40E_SUCCESS; /* Initialize part of the create object info struct */ info.hmc_info = &hw->hmc; @@ -479,9 +461,9 @@ i40e_status i40e_configure_lan_hmc(struct i40e_hw *hw, /* Make one big object, a single SD */ info.count = 1; ret_code = i40e_create_lan_hmc_object(hw, &info); - if (ret_code && (model == I40E_HMC_MODEL_DIRECT_PREFERRED)) + if ((ret_code != I40E_SUCCESS) && (model == I40E_HMC_MODEL_DIRECT_PREFERRED)) goto try_type_paged; - else if (ret_code) + else if (ret_code != I40E_SUCCESS) goto configure_lan_hmc_out; /* else clause falls through the break */ break; @@ -491,7 +473,7 @@ try_type_paged: /* Make one big object in the PD table */ info.count = 1; ret_code = i40e_create_lan_hmc_object(hw, &info); - if (ret_code) + if (ret_code != I40E_SUCCESS) goto configure_lan_hmc_out; break; default: @@ -545,7 +527,7 @@ configure_lan_hmc_out: static i40e_status i40e_delete_lan_hmc_object(struct i40e_hw *hw, struct i40e_hmc_lan_delete_obj_info *info) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; struct i40e_hmc_pd_table *pd_table; u32 pd_idx, pd_lmt, rel_pd_idx; u32 sd_idx, sd_lmt; @@ -610,7 +592,7 @@ static i40e_status i40e_delete_lan_hmc_object(struct i40e_hw *hw, &info->hmc_info->sd_table.sd_entry[sd_idx].u.pd_table; if (pd_table->pd_entry[rel_pd_idx].valid) { ret_code = i40e_remove_pd_bp(hw, info->hmc_info, j); - if (ret_code) + if (I40E_SUCCESS != ret_code) goto exit; } } @@ -631,12 +613,12 @@ static i40e_status i40e_delete_lan_hmc_object(struct i40e_hw *hw, switch (info->hmc_info->sd_table.sd_entry[i].entry_type) { case I40E_SD_TYPE_DIRECT: ret_code = i40e_remove_sd_bp(hw, info->hmc_info, i); - if (ret_code) + if (I40E_SUCCESS != ret_code) goto exit; break; case I40E_SD_TYPE_PAGED: ret_code = i40e_remove_pd_page(hw, info->hmc_info, i); - if (ret_code) + if (I40E_SUCCESS != ret_code) goto exit; break; default: @@ -681,7 +663,7 @@ i40e_status i40e_shutdown_lan_hmc(struct i40e_hw *hw) #define I40E_HMC_STORE(_struct, _ele) \ offsetof(struct _struct, _ele), \ - FIELD_SIZEOF(struct _struct, _ele) + sizeof_field(struct _struct, _ele) struct i40e_context_ele { u16 offset; @@ -774,13 +756,13 @@ static void i40e_write_byte(u8 *hmc_bits, /* get the current bits from the target bit string */ dest = hmc_bits + (ce_info->lsb / 8); - memcpy(&dest_byte, dest, sizeof(dest_byte)); + i40e_memcpy(&dest_byte, dest, sizeof(dest_byte), I40E_DMA_TO_NONDMA); dest_byte &= ~mask; /* get the bits not changing */ dest_byte |= src_byte; /* add in the new bits */ /* put it all back */ - memcpy(dest, &dest_byte, sizeof(dest_byte)); + i40e_memcpy(dest, &dest_byte, sizeof(dest_byte), I40E_NONDMA_TO_DMA); } /** @@ -818,13 +800,13 @@ static void i40e_write_word(u8 *hmc_bits, /* get the current bits from the target bit string */ dest = hmc_bits + (ce_info->lsb / 8); - memcpy(&dest_word, dest, sizeof(dest_word)); + i40e_memcpy(&dest_word, dest, sizeof(dest_word), I40E_DMA_TO_NONDMA); - dest_word &= ~(cpu_to_le16(mask)); /* get the bits not changing */ - dest_word |= cpu_to_le16(src_word); /* add in the new bits */ + dest_word &= ~(CPU_TO_LE16(mask)); /* get the bits not changing */ + dest_word |= CPU_TO_LE16(src_word); /* add in the new bits */ /* put it all back */ - memcpy(dest, &dest_word, sizeof(dest_word)); + i40e_memcpy(dest, &dest_word, sizeof(dest_word), I40E_NONDMA_TO_DMA); } /** @@ -870,13 +852,13 @@ static void i40e_write_dword(u8 *hmc_bits, /* get the current bits from the target bit string */ dest = hmc_bits + (ce_info->lsb / 8); - memcpy(&dest_dword, dest, sizeof(dest_dword)); + i40e_memcpy(&dest_dword, dest, sizeof(dest_dword), I40E_DMA_TO_NONDMA); - dest_dword &= ~(cpu_to_le32(mask)); /* get the bits not changing */ - dest_dword |= cpu_to_le32(src_dword); /* add in the new bits */ + dest_dword &= ~(CPU_TO_LE32(mask)); /* get the bits not changing */ + dest_dword |= CPU_TO_LE32(src_dword); /* add in the new bits */ /* put it all back */ - memcpy(dest, &dest_dword, sizeof(dest_dword)); + i40e_memcpy(dest, &dest_dword, sizeof(dest_dword), I40E_NONDMA_TO_DMA); } /** @@ -922,13 +904,13 @@ static void i40e_write_qword(u8 *hmc_bits, /* get the current bits from the target bit string */ dest = hmc_bits + (ce_info->lsb / 8); - memcpy(&dest_qword, dest, sizeof(dest_qword)); + i40e_memcpy(&dest_qword, dest, sizeof(dest_qword), I40E_DMA_TO_NONDMA); - dest_qword &= ~(cpu_to_le64(mask)); /* get the bits not changing */ - dest_qword |= cpu_to_le64(src_qword); /* add in the new bits */ + dest_qword &= ~(CPU_TO_LE64(mask)); /* get the bits not changing */ + dest_qword |= CPU_TO_LE64(src_qword); /* add in the new bits */ /* put it all back */ - memcpy(dest, &dest_qword, sizeof(dest_qword)); + i40e_memcpy(dest, &dest_qword, sizeof(dest_qword), I40E_NONDMA_TO_DMA); } /** @@ -942,9 +924,10 @@ static i40e_status i40e_clear_hmc_context(struct i40e_hw *hw, enum i40e_hmc_lan_rsrc_type hmc_type) { /* clean the bit array */ - memset(context_bytes, 0, (u32)hw->hmc.hmc_obj[hmc_type].size); + i40e_memset(context_bytes, 0, (u32)hw->hmc.hmc_obj[hmc_type].size, + I40E_DMA_MEM); - return 0; + return I40E_SUCCESS; } /** @@ -981,12 +964,12 @@ static i40e_status i40e_set_hmc_context(u8 *context_bytes, } } - return 0; + return I40E_SUCCESS; } /** * i40e_hmc_get_object_va - retrieves an object's virtual address - * @hmc_info: pointer to i40e_hmc_info struct + * @hw: pointer to the hw structure * @object_base: pointer to u64 to get the va * @rsrc_type: the hmc resource type * @obj_idx: hmc object index @@ -995,24 +978,20 @@ static i40e_status i40e_set_hmc_context(u8 *context_bytes, * base pointer. This function is used for LAN Queue contexts. **/ static -i40e_status i40e_hmc_get_object_va(struct i40e_hmc_info *hmc_info, +i40e_status i40e_hmc_get_object_va(struct i40e_hw *hw, u8 **object_base, enum i40e_hmc_lan_rsrc_type rsrc_type, u32 obj_idx) { u32 obj_offset_in_sd, obj_offset_in_pd; - i40e_status ret_code = 0; + struct i40e_hmc_info *hmc_info = &hw->hmc; struct i40e_hmc_sd_entry *sd_entry; struct i40e_hmc_pd_entry *pd_entry; u32 pd_idx, pd_lmt, rel_pd_idx; + i40e_status ret_code = I40E_SUCCESS; u64 obj_offset_in_fpm; u32 sd_idx, sd_lmt; - if (NULL == hmc_info) { - ret_code = I40E_ERR_BAD_PTR; - hw_dbg(hw, "i40e_hmc_get_object_va: bad hmc_info ptr\n"); - goto exit; - } if (NULL == hmc_info->hmc_obj) { ret_code = I40E_ERR_BAD_PTR; hw_dbg(hw, "i40e_hmc_get_object_va: bad hmc_info->hmc_obj ptr\n"); @@ -1070,8 +1049,7 @@ i40e_status i40e_clear_lan_tx_queue_context(struct i40e_hw *hw, i40e_status err; u8 *context_bytes; - err = i40e_hmc_get_object_va(&hw->hmc, &context_bytes, - I40E_HMC_LAN_TX, queue); + err = i40e_hmc_get_object_va(hw, &context_bytes, I40E_HMC_LAN_TX, queue); if (err < 0) return err; @@ -1091,8 +1069,7 @@ i40e_status i40e_set_lan_tx_queue_context(struct i40e_hw *hw, i40e_status err; u8 *context_bytes; - err = i40e_hmc_get_object_va(&hw->hmc, &context_bytes, - I40E_HMC_LAN_TX, queue); + err = i40e_hmc_get_object_va(hw, &context_bytes, I40E_HMC_LAN_TX, queue); if (err < 0) return err; @@ -1111,8 +1088,7 @@ i40e_status i40e_clear_lan_rx_queue_context(struct i40e_hw *hw, i40e_status err; u8 *context_bytes; - err = i40e_hmc_get_object_va(&hw->hmc, &context_bytes, - I40E_HMC_LAN_RX, queue); + err = i40e_hmc_get_object_va(hw, &context_bytes, I40E_HMC_LAN_RX, queue); if (err < 0) return err; @@ -1132,8 +1108,7 @@ i40e_status i40e_set_lan_rx_queue_context(struct i40e_hw *hw, i40e_status err; u8 *context_bytes; - err = i40e_hmc_get_object_va(&hw->hmc, &context_bytes, - I40E_HMC_LAN_RX, queue); + err = i40e_hmc_get_object_va(hw, &context_bytes, I40E_HMC_LAN_RX, queue); if (err < 0) return err; diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h index e74128db5..00b7d6de4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h +++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_LAN_HMC_H_ #define _I40E_LAN_HMC_H_ diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 78198072f..7ad12f421 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1,38 +1,29 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2017 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ -#include -#include -#include +#ifdef HAVE_XDP_SUPPORT #include - +#endif /* Local includes */ #include "i40e.h" +#include "i40e_helper.h" #include "i40e_diag.h" +#ifdef HAVE_VXLAN_RX_OFFLOAD +#if IS_ENABLED(CONFIG_VXLAN) +#include +#endif +#endif /* HAVE_VXLAN_RX_OFFLOAD */ +#ifdef HAVE_GRE_ENCAP_OFFLOAD +#include +#endif /* HAVE_GRE_ENCAP_OFFLOAD */ +#ifdef HAVE_GENEVE_RX_OFFLOAD +#if IS_ENABLED(CONFIG_GENEVE) +#include +#endif +#endif /* HAVE_GENEVE_RX_OFFLOAD */ +#ifdef HAVE_UDP_ENC_RX_OFFLOAD #include +#endif /* All i40e tracepoints are defined by the include below, which * must be included exactly once across the whole kernel with * CREATE_TRACE_POINTS defined @@ -40,20 +31,25 @@ #define CREATE_TRACE_POINTS #include "i40e_trace.h" -const char i40e_driver_name[] = "i40e"; +char i40e_driver_name[] = "i40e"; static const char i40e_driver_string[] = - "Intel(R) Ethernet Connection XL710 Network Driver"; + "Intel(R) 40-10 Gigabit Ethernet Connection Network Driver"; -#define DRV_KERN "-k" +#ifndef DRV_VERSION_LOCAL +#define DRV_VERSION_LOCAL +#endif /* DRV_VERSION_LOCAL */ + +#define DRV_VERSION_DESC "" #define DRV_VERSION_MAJOR 2 -#define DRV_VERSION_MINOR 1 -#define DRV_VERSION_BUILD 14 +#define DRV_VERSION_MINOR 11 +#define DRV_VERSION_BUILD 29 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ - __stringify(DRV_VERSION_MINOR) "." \ - __stringify(DRV_VERSION_BUILD) DRV_KERN + __stringify(DRV_VERSION_MINOR) "." \ + __stringify(DRV_VERSION_BUILD) \ + DRV_VERSION_DESC __stringify(DRV_VERSION_LOCAL) const char i40e_driver_version_str[] = DRV_VERSION; -static const char i40e_copyright[] = "Copyright (c) 2013 - 2014 Intel Corporation."; +static const char i40e_copyright[] = "Copyright(c) 2013 - 2020 Intel Corporation."; /* a bit of forward declarations */ static void i40e_vsi_reinit_locked(struct i40e_vsi *vsi); @@ -64,12 +60,20 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit); static int i40e_setup_misc_vector(struct i40e_pf *pf); static void i40e_determine_queue_usage(struct i40e_pf *pf); static int i40e_setup_pf_filter_control(struct i40e_pf *pf); +static void i40e_clear_rss_config_user(struct i40e_vsi *vsi); static void i40e_prep_for_reset(struct i40e_pf *pf, bool lock_acquired); static int i40e_reset(struct i40e_pf *pf); static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired); +static int i40e_setup_misc_vector_for_recovery_mode(struct i40e_pf *pf); +static int i40e_restore_interrupt_scheme(struct i40e_pf *pf); +static bool i40e_check_recovery_mode(struct i40e_pf *pf); +static int i40e_init_recovery_mode(struct i40e_pf *pf, struct i40e_hw *hw); +static i40e_status i40e_force_link_state(struct i40e_pf *pf, bool is_up); +static bool i40e_is_total_port_shutdown_enabled(struct i40e_pf *pf); static void i40e_fdir_sb_setup(struct i40e_pf *pf); static int i40e_veb_get_bw_info(struct i40e_veb *veb); - +static int i40e_get_capabilities(struct i40e_pf *pf, + enum i40e_admin_queue_opc list_type); /* i40e_pci_tbl - PCI Device ID Table * * Last entry must be all 0s @@ -85,16 +89,22 @@ static const struct pci_device_id i40e_pci_tbl[] = { {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_A), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_B), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_C), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_5G_BASE_T_BC), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T4), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_BC), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_SFP), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_B), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2_A), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_X710_N3000), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_XXV710_N3000), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_X722), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_X722), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X722), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_1G_BASE_T_X722), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_X722), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_I_X722), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2_A), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_25G_B), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_25G_SFP28), 0}, /* required last entry */ @@ -103,86 +113,38 @@ static const struct pci_device_id i40e_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, i40e_pci_tbl); #define I40E_MAX_VF_COUNT 128 +#define OPTION_UNSET -1 +#define I40E_PARAM_INIT { [0 ... I40E_MAX_NIC] = OPTION_UNSET} +#define I40E_MAX_NIC 64 +#if !defined(HAVE_SRIOV_CONFIGURE) && !defined(HAVE_RHEL6_SRIOV_CONFIGURE) +#ifdef CONFIG_PCI_IOV +static int max_vfs[I40E_MAX_NIC+1] = I40E_PARAM_INIT; +module_param_array_named(max_vfs, max_vfs, int, NULL, 0); +MODULE_PARM_DESC(max_vfs, + "Number of Virtual Functions: 0 = disable (default), 1-" + __stringify(I40E_MAX_VF_COUNT) " = enable " + "this many VFs"); +#endif /* CONFIG_PCI_IOV */ +#endif /* HAVE_SRIOV_CONFIGURE */ + static int debug = -1; -module_param(debug, uint, 0); -MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all), Debug mask (0x8XXXXXXX)"); +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); +static int l4mode = L4_MODE_DISABLED; +module_param(l4mode, int, 0000); +MODULE_PARM_DESC(l4mode, "L4 cloud filter mode: 0=UDP,1=TCP,2=Both,-1=Disabled(default)"); + MODULE_AUTHOR("Intel Corporation, "); -MODULE_DESCRIPTION("Intel(R) Ethernet Connection XL710 Network Driver"); +MODULE_DESCRIPTION("Intel(R) 40-10 Gigabit Ethernet Connection Network Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); static struct workqueue_struct *i40e_wq; -/** - * i40e_allocate_dma_mem_d - OS specific memory alloc for shared code - * @hw: pointer to the HW structure - * @mem: ptr to mem struct to fill out - * @size: size of memory requested - * @alignment: what to align the allocation to - **/ -int i40e_allocate_dma_mem_d(struct i40e_hw *hw, struct i40e_dma_mem *mem, - u64 size, u32 alignment) -{ - struct i40e_pf *pf = (struct i40e_pf *)hw->back; - - mem->size = ALIGN(size, alignment); - mem->va = dma_zalloc_coherent(&pf->pdev->dev, mem->size, - &mem->pa, GFP_KERNEL); - if (!mem->va) - return -ENOMEM; - - return 0; -} - -/** - * i40e_free_dma_mem_d - OS specific memory free for shared code - * @hw: pointer to the HW structure - * @mem: ptr to mem struct to free - **/ -int i40e_free_dma_mem_d(struct i40e_hw *hw, struct i40e_dma_mem *mem) -{ - struct i40e_pf *pf = (struct i40e_pf *)hw->back; - - dma_free_coherent(&pf->pdev->dev, mem->size, mem->va, mem->pa); - mem->va = NULL; - mem->pa = 0; - mem->size = 0; - - return 0; -} - -/** - * i40e_allocate_virt_mem_d - OS specific memory alloc for shared code - * @hw: pointer to the HW structure - * @mem: ptr to mem struct to fill out - * @size: size of memory requested - **/ -int i40e_allocate_virt_mem_d(struct i40e_hw *hw, struct i40e_virt_mem *mem, - u32 size) -{ - mem->size = size; - mem->va = kzalloc(size, GFP_KERNEL); - - if (!mem->va) - return -ENOMEM; - - return 0; -} - -/** - * i40e_free_virt_mem_d - OS specific memory free for shared code - * @hw: pointer to the HW structure - * @mem: ptr to mem struct to free - **/ -int i40e_free_virt_mem_d(struct i40e_hw *hw, struct i40e_virt_mem *mem) +bool i40e_is_l4mode_enabled(void) { - /* it's ok to kfree a NULL pointer */ - kfree(mem->va); - mem->va = NULL; - mem->size = 0; - - return 0; + return l4mode > L4_MODE_DISABLED; } /** @@ -206,8 +168,8 @@ static int i40e_get_lump(struct i40e_pf *pf, struct i40e_lump_tracking *pile, if (!pile || needed == 0 || id >= I40E_PILE_VALID_BIT) { dev_info(&pf->pdev->dev, - "param err: pile=%p needed=%d id=0x%04x\n", - pile, needed, id); + "param err: pile=%s needed=%d id=0x%04x\n", + pile ? "" : "", needed, id); return -EINVAL; } @@ -274,8 +236,8 @@ static int i40e_put_lump(struct i40e_lump_tracking *pile, u16 index, u16 id) /** * i40e_find_vsi_from_id - searches for the vsi with the given id - * @pf - the pf structure to search for the vsi - * @id - id of the vsi it is searching for + * @pf: the pf structure to search for the vsi + * @id: id of the vsi it is searching for **/ struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id) { @@ -288,6 +250,22 @@ struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id) return NULL; } +/** + * i40e_find_vsi_from_seid - searches for the vsi with the given seid + * @pf: the pf structure to search for the vsi + * @seid: seid of the vsi it is searching for + **/ +struct i40e_vsi *i40e_find_vsi_from_seid(struct i40e_pf *pf, u16 seid) +{ + int i; + + for (i = 0; i < pf->num_alloc_vsi; i++) + if (pf->vsi[i] && (pf->vsi[i]->seid == seid)) + return pf->vsi[i]; + + return NULL; +} + /** * i40e_service_event_schedule - Schedule the service task to wake up * @pf: board private structure @@ -296,20 +274,27 @@ struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id) **/ void i40e_service_event_schedule(struct i40e_pf *pf) { - if (!test_bit(__I40E_DOWN, pf->state) && - !test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) + if ((!test_bit(__I40E_DOWN, pf->state) && + !test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) || + test_bit(__I40E_RECOVERY_MODE, pf->state)) queue_work(i40e_wq, &pf->service_task); } /** * i40e_tx_timeout - Respond to a Tx Hang * @netdev: network interface device structure + * @txqueue: stuck queue * * If any port has noticed a Tx timeout, it is likely that the whole * device is munged, not just the one netdev port, so go for the full * reset. **/ +#ifdef HAVE_TX_TIMEOUT_TXQUEUE +static void +i40e_tx_timeout(struct net_device *netdev, __always_unused unsigned int txqueue) +#else static void i40e_tx_timeout(struct net_device *netdev) +#endif { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -350,12 +335,23 @@ static void i40e_tx_timeout(struct net_device *netdev) } } +#ifdef CONFIG_DEBUG_FS + if (vsi->block_tx_timeout) { + netdev_info(netdev, "tx_timeout recovery disabled\n"); + return; + } +#endif + if (time_after(jiffies, (pf->tx_timeout_last_recovery + HZ*20))) pf->tx_timeout_recovery_level = 1; /* reset after some time */ else if (time_before(jiffies, (pf->tx_timeout_last_recovery + netdev->watchdog_timeo))) return; /* don't do any new action before the next timeout */ + /* don't kick off another recovery if one is already pending */ + if (test_and_set_bit(__I40E_TIMEOUT_RECOVERY_PENDING, pf->state)) + return; + if (tx_ring) { head = i40e_get_head(tx_ring); /* Read interrupt register */ @@ -387,7 +383,9 @@ static void i40e_tx_timeout(struct net_device *netdev) set_bit(__I40E_GLOBAL_RESET_REQUESTED, pf->state); break; default: - netdev_err(netdev, "tx_timeout recovery unsuccessful\n"); + netdev_err(netdev, "tx_timeout recovery unsuccessful, device is in non-recoverable state.\n"); + set_bit(__I40E_DOWN_REQUESTED, pf->state); + set_bit(__I40E_VSI_DOWN_REQUESTED, vsi->state); break; } @@ -402,11 +400,23 @@ static void i40e_tx_timeout(struct net_device *netdev) * Returns the address of the device statistics structure. * The statistics are actually updated from the service task. **/ +#ifdef HAVE_NDO_GET_STATS64 struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi) { return &vsi->net_stats; } +#else +struct net_device_stats *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi) +{ + /* It is possible for a VSIs to not have a netdev */ + if (vsi->netdev) + return &vsi->netdev->stats; + else + return &vsi->net_stats; +} +#endif +#ifdef HAVE_NDO_GET_STATS64 /** * i40e_get_netdev_stats_struct_tx - populate stats from a Tx ring * @ring: Tx ring to get statistics from @@ -431,12 +441,19 @@ static void i40e_get_netdev_stats_struct_tx(struct i40e_ring *ring, /** * i40e_get_netdev_stats_struct - Get statistics for netdev interface * @netdev: network interface device structure + * @stats: data structure to store statistics * * Returns the address of the device statistics structure. * The statistics are actually updated from the service task. **/ +#ifdef HAVE_VOID_NDO_GET_STATS64 static void i40e_get_netdev_stats_struct(struct net_device *netdev, - struct rtnl_link_stats64 *stats) + struct rtnl_link_stats64 *stats) +#else +static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct( + struct net_device *netdev, + struct rtnl_link_stats64 *stats) +#endif /* HAVE_VOID_NDO_GET_STATS64 */ { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_ring *tx_ring, *rx_ring; @@ -445,21 +462,29 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev, int i; if (test_bit(__I40E_VSI_DOWN, vsi->state)) +#ifdef HAVE_VOID_NDO_GET_STATS64 return; +#else + return stats; +#endif /* HAVE_VOID_NDO_GET_STATS_64 */ if (!vsi->tx_rings) +#ifdef HAVE_VOID_NDO_GET_STATS64 return; +#else + return stats; +#endif /* HAVE_VOID_NDO_GET_STATS_64 */ rcu_read_lock(); for (i = 0; i < vsi->num_queue_pairs; i++) { u64 bytes, packets; unsigned int start; - tx_ring = ACCESS_ONCE(vsi->tx_rings[i]); + tx_ring = READ_ONCE(vsi->tx_rings[i]); if (!tx_ring) continue; - i40e_get_netdev_stats_struct_tx(tx_ring, stats); + i40e_get_netdev_stats_struct_tx(tx_ring, stats); rx_ring = &tx_ring[1]; do { @@ -484,7 +509,21 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev, stats->rx_dropped = vsi_stats->rx_dropped; stats->rx_crc_errors = vsi_stats->rx_crc_errors; stats->rx_length_errors = vsi_stats->rx_length_errors; +#ifndef HAVE_VOID_NDO_GET_STATS64 + + return stats; +#endif +} +#else +static struct net_device_stats *i40e_get_netdev_stats_struct( + struct net_device *netdev) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + + return i40e_get_vsi_stats_struct(vsi); } +#endif /* HAVE_NDO_GET_STATS64 */ /** * i40e_vsi_reset_stats - Resets all stats of the given vsi @@ -492,7 +531,11 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev, **/ void i40e_vsi_reset_stats(struct i40e_vsi *vsi) { +#ifdef HAVE_NDO_GET_STATS64 struct rtnl_link_stats64 *ns; +#else + struct net_device_stats *ns; +#endif int i; if (!vsi) @@ -509,6 +552,10 @@ void i40e_vsi_reset_stats(struct i40e_vsi *vsi) sizeof(vsi->rx_rings[i]->stats)); memset(&vsi->rx_rings[i]->rx_stats, 0, sizeof(vsi->rx_rings[i]->rx_stats)); +#ifdef HAVE_XDP_SUPPORT + memset(&vsi->rx_rings[i]->xdp_stats, 0, + sizeof(vsi->rx_rings[i]->xdp_stats)); +#endif memset(&vsi->tx_rings[i]->stats, 0, sizeof(vsi->tx_rings[i]->stats)); memset(&vsi->tx_rings[i]->tx_stats, 0, @@ -536,10 +583,30 @@ void i40e_pf_reset_stats(struct i40e_pf *pf) sizeof(pf->veb[i]->stats)); memset(&pf->veb[i]->stats_offsets, 0, sizeof(pf->veb[i]->stats_offsets)); + memset(&pf->veb[i]->tc_stats, 0, + sizeof(pf->veb[i]->tc_stats)); + memset(&pf->veb[i]->tc_stats_offsets, 0, + sizeof(pf->veb[i]->tc_stats_offsets)); pf->veb[i]->stat_offsets_loaded = false; } } pf->hw_csum_rx_error = 0; +#ifdef I40E_ADD_PROBES + pf->tcp_segs = 0; + pf->tx_tcp_cso = 0; + pf->tx_udp_cso = 0; + pf->tx_sctp_cso = 0; + pf->tx_ip4_cso = 0; + pf->rx_tcp_cso = 0; + pf->rx_udp_cso = 0; + pf->rx_sctp_cso = 0; + pf->rx_ip4_cso = 0; + pf->rx_tcp_cso_err = 0; + pf->rx_udp_cso_err = 0; + pf->rx_sctp_cso_err = 0; + pf->rx_ip4_cso_err = 0; + pf->hw_csum_rx_outer = 0; +#endif } /** @@ -599,6 +666,20 @@ static void i40e_stat_update32(struct i40e_hw *hw, u32 reg, *stat = (u32)((new_data + BIT_ULL(32)) - *offset); } +/** + * i40e_stat_update_and_clear32 - read and clear hw reg, update a 32 bit stat + * @hw: ptr to the hardware info + * @reg: the hw reg to read and clear + * @stat: ptr to the stat + **/ +static void i40e_stat_update_and_clear32(struct i40e_hw *hw, u32 reg, u64 *stat) +{ + u32 new_data = rd32(hw, reg); + + wr32(hw, reg, 1); /* must write a nonzero value to clear register */ + *stat += new_data; +} + /** * i40e_update_eth_stats - Update VSI-specific ethernet statistics counters. * @vsi: the VSI to be updated @@ -624,9 +705,6 @@ void i40e_update_eth_stats(struct i40e_vsi *vsi) i40e_stat_update32(hw, I40E_GLV_RUPP(stat_idx), vsi->stat_offsets_loaded, &oes->rx_unknown_protocol, &es->rx_unknown_protocol); - i40e_stat_update32(hw, I40E_GLV_TEPC(stat_idx), - vsi->stat_offsets_loaded, - &oes->tx_errors, &es->tx_errors); i40e_stat_update48(hw, I40E_GLV_GORCH(stat_idx), I40E_GLV_GORCL(stat_idx), @@ -668,7 +746,7 @@ void i40e_update_eth_stats(struct i40e_vsi *vsi) * i40e_update_veb_stats - Update Switch component statistics * @veb: the VEB being updated **/ -static void i40e_update_veb_stats(struct i40e_veb *veb) +void i40e_update_veb_stats(struct i40e_veb *veb) { struct i40e_pf *pf = veb->pf; struct i40e_hw *hw = &pf->hw; @@ -688,11 +766,10 @@ static void i40e_update_veb_stats(struct i40e_veb *veb) i40e_stat_update32(hw, I40E_GLSW_TDPC(idx), veb->stat_offsets_loaded, &oes->tx_discards, &es->tx_discards); - if (hw->revision_id > 0) - i40e_stat_update32(hw, I40E_GLSW_RUPP(idx), - veb->stat_offsets_loaded, - &oes->rx_unknown_protocol, - &es->rx_unknown_protocol); + i40e_stat_update32(hw, I40E_GLSW_RUPP(idx), + veb->stat_offsets_loaded, + &oes->rx_unknown_protocol, &es->rx_unknown_protocol); + i40e_stat_update48(hw, I40E_GLSW_GORCH(idx), I40E_GLSW_GORCL(idx), veb->stat_offsets_loaded, &oes->rx_bytes, &es->rx_bytes); @@ -756,15 +833,22 @@ static void i40e_update_veb_stats(struct i40e_veb *veb) static void i40e_update_vsi_stats(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; +#ifdef HAVE_NDO_GET_STATS64 struct rtnl_link_stats64 *ons; struct rtnl_link_stats64 *ns; /* netdev stats */ +#else + struct net_device_stats *ons; + struct net_device_stats *ns; /* netdev stats */ +#endif struct i40e_eth_stats *oes; struct i40e_eth_stats *es; /* device's eth stats */ u32 tx_restart, tx_busy; struct i40e_ring *p; u32 rx_page, rx_buf; u64 bytes, packets; +#ifdef HAVE_NDO_GET_STATS64 unsigned int start; +#endif u64 tx_linearize; u64 tx_force_wb; u64 rx_p, rx_b; @@ -791,13 +875,17 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) rcu_read_lock(); for (q = 0; q < vsi->num_queue_pairs; q++) { /* locate Tx ring */ - p = ACCESS_ONCE(vsi->tx_rings[q]); + p = READ_ONCE(vsi->tx_rings[q]); +#ifdef HAVE_NDO_GET_STATS64 do { start = u64_stats_fetch_begin_irq(&p->syncp); +#endif packets = p->stats.packets; bytes = p->stats.bytes; +#ifdef HAVE_NDO_GET_STATS64 } while (u64_stats_fetch_retry_irq(&p->syncp, start)); +#endif tx_b += bytes; tx_p += packets; tx_restart += p->tx_stats.restart_queue; @@ -807,11 +895,15 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) /* Rx queue is part of the same block as Tx queue */ p = &p[1]; +#ifdef HAVE_NDO_GET_STATS64 do { start = u64_stats_fetch_begin_irq(&p->syncp); +#endif packets = p->stats.packets; bytes = p->stats.bytes; +#ifdef HAVE_NDO_GET_STATS64 } while (u64_stats_fetch_retry_irq(&p->syncp, start)); +#endif rx_b += bytes; rx_p += packets; rx_buf += p->rx_stats.alloc_buff_failed; @@ -858,7 +950,7 @@ static void i40e_update_pf_stats(struct i40e_pf *pf) struct i40e_hw_port_stats *osd = &pf->stats_offsets; struct i40e_hw_port_stats *nsd = &pf->stats; struct i40e_hw *hw = &pf->hw; - u32 val; + int i; i40e_stat_update48(hw, I40E_GLPRT_GORCH(hw->port), @@ -1040,41 +1132,31 @@ static void i40e_update_pf_stats(struct i40e_pf *pf) &osd->rx_jabber, &nsd->rx_jabber); /* FDIR stats */ - i40e_stat_update32(hw, - I40E_GLQF_PCNT(I40E_FD_ATR_STAT_IDX(pf->hw.pf_id)), - pf->stat_offsets_loaded, - &osd->fd_atr_match, &nsd->fd_atr_match); - i40e_stat_update32(hw, - I40E_GLQF_PCNT(I40E_FD_SB_STAT_IDX(pf->hw.pf_id)), - pf->stat_offsets_loaded, - &osd->fd_sb_match, &nsd->fd_sb_match); - i40e_stat_update32(hw, - I40E_GLQF_PCNT(I40E_FD_ATR_TUNNEL_STAT_IDX(pf->hw.pf_id)), - pf->stat_offsets_loaded, - &osd->fd_atr_tunnel_match, &nsd->fd_atr_tunnel_match); - - val = rd32(hw, I40E_PRTPM_EEE_STAT); - nsd->tx_lpi_status = - (val & I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_MASK) >> - I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_SHIFT; - nsd->rx_lpi_status = - (val & I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_MASK) >> - I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_SHIFT; - i40e_stat_update32(hw, I40E_PRTPM_TLPIC, - pf->stat_offsets_loaded, - &osd->tx_lpi_count, &nsd->tx_lpi_count); - i40e_stat_update32(hw, I40E_PRTPM_RLPIC, - pf->stat_offsets_loaded, - &osd->rx_lpi_count, &nsd->rx_lpi_count); + i40e_stat_update_and_clear32(hw, + I40E_GLQF_PCNT(I40E_FD_ATR_STAT_IDX(hw->pf_id)), + &nsd->fd_atr_match); + i40e_stat_update_and_clear32(hw, + I40E_GLQF_PCNT(I40E_FD_SB_STAT_IDX(hw->pf_id)), + &nsd->fd_sb_match); + i40e_stat_update_and_clear32(hw, + I40E_GLQF_PCNT(I40E_FD_ATR_TUNNEL_STAT_IDX(hw->pf_id)), + &nsd->fd_atr_tunnel_match); + + i40e_get_phy_lpi_status(hw, nsd); + i40e_lpi_stat_update(hw, pf->stat_offsets_loaded, + &osd->tx_lpi_count, &nsd->tx_lpi_count, + &osd->rx_lpi_count, &nsd->rx_lpi_count); + i40e_get_lpi_duration(hw, nsd, + &nsd->tx_lpi_duration, &nsd->rx_lpi_duration); if (pf->flags & I40E_FLAG_FD_SB_ENABLED && - !(pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED)) + !test_bit(__I40E_FD_SB_AUTO_DISABLED, pf->state)) nsd->fd_sb_status = true; else nsd->fd_sb_status = false; if (pf->flags & I40E_FLAG_FD_ATR_ENABLED && - !(pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED)) + !test_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state)) nsd->fd_atr_status = true; else nsd->fd_atr_status = false; @@ -1098,6 +1180,25 @@ void i40e_update_stats(struct i40e_vsi *vsi) i40e_update_vsi_stats(vsi); } +/** + * i40e_count_filters - counts VSI mac filters + * @vsi: the VSI to be searched + * + * Returns count of mac filters + **/ +int i40e_count_filters(struct i40e_vsi *vsi) +{ + struct i40e_mac_filter *f; + struct hlist_node *h; + int bkt; + int cnt = 0; + + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) + ++cnt; + + return cnt; +} + /** * i40e_find_filter - Search VSI filter list for specific mac/vlan filter * @vsi: the VSI to be searched @@ -1106,8 +1207,8 @@ void i40e_update_stats(struct i40e_vsi *vsi) * * Returns ptr to the filter object or NULL **/ -static struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi, - const u8 *macaddr, s16 vlan) +struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi, + const u8 *macaddr, s16 vlan) { struct i40e_mac_filter *f; u64 key; @@ -1142,7 +1243,7 @@ struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, const u8 *macaddr) key = i40e_addr_to_hkey(macaddr); hash_for_each_possible(vsi->mac_filter_hash, f, hlist, key) { - if ((ether_addr_equal(macaddr, f->macaddr))) + if (ether_addr_equal(macaddr, f->macaddr)) return f; } return NULL; @@ -1172,11 +1273,11 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) * i40e_sync_filters_subtask. * * Thus, we can simply use a boolean value, has_vlan_filters which we - * will set to true when we add a VLAN filter in i40e_add_filter. Then + * will set to true when we add a vlan filter in i40e_add_filter. Then * we have to perform the full search after deleting filters in * i40e_sync_filters_subtask, but we already have to search * filters here and can perform the check at the same time. This - * results in avoiding embedding a loop for VLAN mode inside another + * results in avoiding embedding a loop for vlan mode inside another * loop over all the filters, and should maintain correctness as noted * above. */ @@ -1185,7 +1286,7 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) /** * i40e_correct_mac_vlan_filters - Correct non-VLAN filters if necessary - * @vsi: the VSI to configure + * @vsi: the vsi to configure * @tmp_add_list: list of filters ready to be added * @tmp_del_list: list of filters ready to be deleted * @vlan_filters: the number of active VLAN filters @@ -1250,7 +1351,7 @@ static int i40e_correct_mac_vlan_filters(struct i40e_vsi *vsi, /* Update the remaining active filters */ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { /* Combine the checks for whether a filter needs to be changed - * and then determine the new VLAN inside the if block, in + * and then determine the new vlan inside the if block, in * order to avoid duplicating code for adding the new filter * then deleting the old filter. */ @@ -1353,28 +1454,22 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, return NULL; /* Update the boolean indicating if we need to function in - * VLAN mode. + * vlan mode. */ if (vlan >= 0) vsi->has_vlan_filter = true; ether_addr_copy(f->macaddr, macaddr); f->vlan = vlan; - /* If we're in overflow promisc mode, set the state directly - * to failed, so we don't bother to try sending the filter - * to the hardware. - */ - if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state)) - f->state = I40E_FILTER_FAILED; - else - f->state = I40E_FILTER_NEW; + f->state = I40E_FILTER_NEW; + INIT_HLIST_NODE(&f->hlist); key = i40e_addr_to_hkey(macaddr); hash_add(vsi->mac_filter_hash, &f->hlist, key); vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; - vsi->back->flags |= I40E_FLAG_FILTER_SYNC; + set_bit(__I40E_MACVLAN_SYNC_PENDING, vsi->back->state); } /* If we're asked to add a filter that has been marked for removal, it @@ -1392,46 +1487,10 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, } /** - * __i40e_del_filter - Remove a specific filter from the VSI - * @vsi: VSI to remove from - * @f: the filter to remove from the list - * - * This function should be called instead of i40e_del_filter only if you know - * the exact filter you will remove already, such as via i40e_find_filter or - * i40e_find_mac. - * - * NOTE: This function is expected to be called with mac_filter_hash_lock - * being held. - * ANOTHER NOTE: This function MUST be called from within the context of - * the "safe" variants of any list iterators, e.g. list_for_each_entry_safe() - * instead of list_for_each_entry(). - **/ -void __i40e_del_filter(struct i40e_vsi *vsi, struct i40e_mac_filter *f) -{ - if (!f) - return; - - /* If the filter was never added to firmware then we can just delete it - * directly and we don't want to set the status to remove or else an - * admin queue command will unnecessarily fire. - */ - if ((f->state == I40E_FILTER_FAILED) || - (f->state == I40E_FILTER_NEW)) { - hash_del(&f->hlist); - kfree(f); - } else { - f->state = I40E_FILTER_REMOVE; - } - - vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; - vsi->back->flags |= I40E_FLAG_FILTER_SYNC; -} - -/** - * i40e_del_filter - Remove a MAC/VLAN filter from the VSI + * i40e_del_filter - Remove a mac/vlan filter from the VSI * @vsi: the VSI to be searched * @macaddr: the MAC address - * @vlan: the VLAN + * @vlan: the vlan * * NOTE: This function is expected to be called with mac_filter_hash_lock * being held. @@ -1504,8 +1563,6 @@ int i40e_del_mac_filter(struct i40e_vsi *vsi, const u8 *macaddr) bool found = false; int bkt; - WARN(!spin_is_locked(&vsi->mac_filter_hash_lock), - "Missing mac_filter_hash_lock\n"); hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { if (ether_addr_equal(macaddr, f->macaddr)) { __i40e_del_filter(vsi, f); @@ -1543,8 +1600,8 @@ static int i40e_set_mac(struct net_device *netdev, void *p) return 0; } - if (test_bit(__I40E_VSI_DOWN, vsi->back->state) || - test_bit(__I40E_RESET_RECOVERY_PENDING, vsi->back->state)) + if (test_bit(__I40E_DOWN, pf->state) || + test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) return -EADDRNOTAVAIL; if (ether_addr_equal(hw->mac.addr, addr->sa_data)) @@ -1553,16 +1610,22 @@ static int i40e_set_mac(struct net_device *netdev, void *p) else netdev_info(netdev, "set new mac address %pM\n", addr->sa_data); + /* Copy the address first, so that we avoid a possible race with + * .set_rx_mode(). If we copy after changing the address in the filter + * list, we might open ourselves to a narrow race window where + * .set_rx_mode could delete our dev_addr filter and prevent traffic + * from passing. + */ + ether_addr_copy(netdev->dev_addr, addr->sa_data); + spin_lock_bh(&vsi->mac_filter_hash_lock); i40e_del_mac_filter(vsi, netdev->dev_addr); i40e_add_mac_filter(vsi, addr->sa_data); spin_unlock_bh(&vsi->mac_filter_hash_lock); - ether_addr_copy(netdev->dev_addr, addr->sa_data); if (vsi->type == I40E_VSI_MAIN) { i40e_status ret; - ret = i40e_aq_mac_address_write(&vsi->back->hw, - I40E_AQC_WRITE_TYPE_LAA_WOL, + ret = i40e_aq_mac_address_write(hw, I40E_AQC_WRITE_TYPE_LAA_WOL, addr->sa_data, NULL); if (ret) netdev_info(netdev, "Ignoring error from firmware on LAA update, status %s, AQ ret %s\n", @@ -1573,77 +1636,250 @@ static int i40e_set_mac(struct net_device *netdev, void *p) /* schedule our worker thread which will take care of * applying the new filter changes */ - i40e_service_event_schedule(vsi->back); + i40e_service_event_schedule(pf); return 0; } /** - * i40e_vsi_setup_queue_map - Setup a VSI queue map based on enabled_tc - * @vsi: the VSI being setup - * @ctxt: VSI context structure - * @enabled_tc: Enabled TCs bitmap - * @is_add: True if called before Add VSI - * - * Setup VSI queue mapping for enabled traffic classes. + * i40e_config_rss_aq - Prepare for RSS using AQ commands + * @vsi: vsi structure + * @seed: RSS hash seed + * @lut: Buffer to store the lookup table entries + * @lut_size: Size of buffer to store the lookup table entries **/ -static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, - struct i40e_vsi_context *ctxt, - u8 enabled_tc, - bool is_add) +static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed, + u8 *lut, u16 lut_size) { struct i40e_pf *pf = vsi->back; - u16 sections = 0; - u8 netdev_tc = 0; - u16 numtc = 0; - u16 qcount; - u8 offset; - u16 qmap; - int i; - u16 num_tc_qps = 0; - - sections = I40E_AQ_VSI_PROP_QUEUE_MAP_VALID; - offset = 0; + struct i40e_hw *hw = &pf->hw; + int ret = 0; - if (enabled_tc && (vsi->back->flags & I40E_FLAG_DCB_ENABLED)) { - /* Find numtc from enabled TC bitmap */ - for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - if (enabled_tc & BIT(i)) /* TC is enabled */ - numtc++; - } - if (!numtc) { - dev_warn(&pf->pdev->dev, "DCB is enabled but no TC enabled, forcing TC0\n"); - numtc = 1; + if (seed) { + struct i40e_aqc_get_set_rss_key_data *seed_dw = + (struct i40e_aqc_get_set_rss_key_data *)seed; + ret = i40e_aq_set_rss_key(hw, vsi->id, seed_dw); + if (ret) { + dev_info(&pf->pdev->dev, + "Cannot set RSS key, err %s aq_err %s\n", + i40e_stat_str(hw, ret), + i40e_aq_str(hw, hw->aq.asq_last_status)); + return ret; } - } else { - /* At least TC0 is enabled in case of non-DCB case */ - numtc = 1; } + if (lut) { + bool pf_lut = vsi->type == I40E_VSI_MAIN ? true : false; - vsi->tc_config.numtc = numtc; - vsi->tc_config.enabled_tc = enabled_tc ? enabled_tc : 1; - /* Number of queues per enabled TC */ - qcount = vsi->alloc_queue_pairs; + ret = i40e_aq_set_rss_lut(hw, vsi->id, pf_lut, lut, lut_size); + if (ret) { + dev_info(&pf->pdev->dev, + "Cannot set RSS lut, err %s aq_err %s\n", + i40e_stat_str(hw, ret), + i40e_aq_str(hw, hw->aq.asq_last_status)); + return ret; + } + } + return ret; +} - num_tc_qps = qcount / numtc; - num_tc_qps = min_t(int, num_tc_qps, i40e_pf_get_max_q_per_tc(pf)); +/** + * i40e_vsi_config_rss - Prepare for VSI(VMDq) RSS if used + * @vsi: VSI structure + **/ +static int i40e_vsi_config_rss(struct i40e_vsi *vsi) +{ + struct i40e_pf *pf = vsi->back; + u8 seed[I40E_HKEY_ARRAY_SIZE]; + u8 *lut; + int ret; - /* Setup queue offset/count for all TCs for given VSI */ - for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - /* See if the given TC is enabled for the given VSI */ - if (vsi->tc_config.enabled_tc & BIT(i)) { - /* TC is enabled */ - int pow, num_qps; + if (!(pf->hw_features & I40E_HW_RSS_AQ_CAPABLE)) + return 0; + if (!vsi->rss_size) + vsi->rss_size = min_t(int, pf->alloc_rss_size, + vsi->num_queue_pairs); + if (!vsi->rss_size) + return -EINVAL; + lut = kzalloc(vsi->rss_table_size, GFP_KERNEL); + if (!lut) + return -ENOMEM; - switch (vsi->type) { - case I40E_VSI_MAIN: - qcount = min_t(int, pf->alloc_rss_size, - num_tc_qps); - break; - case I40E_VSI_FDIR: - case I40E_VSI_SRIOV: - case I40E_VSI_VMDQ2: - default: - qcount = num_tc_qps; + /* Use the user configured hash keys and lookup table if there is one, + * otherwise use default + */ + if (vsi->rss_lut_user) + memcpy(lut, vsi->rss_lut_user, vsi->rss_table_size); + else + i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size); + if (vsi->rss_hkey_user) + memcpy(seed, vsi->rss_hkey_user, I40E_HKEY_ARRAY_SIZE); + else + netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE); + ret = i40e_config_rss_aq(vsi, seed, lut, vsi->rss_table_size); + kfree(lut); + return ret; +} + +#ifdef __TC_MQPRIO_MODE_MAX +/** + * i40e_vsi_setup_queue_map_mqprio - Prepares mqprio based tc_config + * @vsi: the VSI being configured, + * @ctxt: VSI context structure + * @enabled_tc: number of traffic classes to enable + * + * Prepares VSI tc_config to have queue configurations based on MQPRIO options. + **/ +static int i40e_vsi_setup_queue_map_mqprio(struct i40e_vsi *vsi, + struct i40e_vsi_context *ctxt, + u8 enabled_tc) +{ + u16 qcount = 0, max_qcount, qmap, sections = 0; + int i, override_q, pow, num_qps, ret; + u8 netdev_tc = 0, offset = 0; + + if (vsi->type != I40E_VSI_MAIN) + return -EINVAL; + sections = I40E_AQ_VSI_PROP_QUEUE_MAP_VALID; + sections |= I40E_AQ_VSI_PROP_SCHED_VALID; + vsi->tc_config.numtc = vsi->mqprio_qopt.qopt.num_tc; + vsi->tc_config.enabled_tc = enabled_tc ? enabled_tc : 1; + num_qps = vsi->mqprio_qopt.qopt.count[0]; + + /* find the next higher power-of-2 of num queue pairs */ + pow = ilog2(num_qps); + if (!is_power_of_2(num_qps)) + pow++; + qmap = (offset << I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT) | + (pow << I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT); + + /* Setup queue offset/count for all TCs for given VSI */ + max_qcount = vsi->mqprio_qopt.qopt.count[0]; + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { + /* See if the given TC is enabled for the given VSI */ + if (vsi->tc_config.enabled_tc & BIT(i)) { + offset = vsi->mqprio_qopt.qopt.offset[i]; + qcount = vsi->mqprio_qopt.qopt.count[i]; + if (qcount > max_qcount) + max_qcount = qcount; + vsi->tc_config.tc_info[i].qoffset = offset; + vsi->tc_config.tc_info[i].qcount = qcount; + vsi->tc_config.tc_info[i].netdev_tc = netdev_tc++; + } else { + /* TC is not enabled so set the offset to + * default queue and allocate one queue + * for the given TC. + */ + vsi->tc_config.tc_info[i].qoffset = 0; + vsi->tc_config.tc_info[i].qcount = 1; + vsi->tc_config.tc_info[i].netdev_tc = 0; + } + } + + /* Set actual Tx/Rx queue pairs */ + vsi->num_queue_pairs = offset + qcount; + + /* Setup queue TC[0].qmap for given VSI context */ + ctxt->info.tc_mapping[0] = cpu_to_le16(qmap); + ctxt->info.mapping_flags |= cpu_to_le16(I40E_AQ_VSI_QUE_MAP_CONTIG); + ctxt->info.queue_mapping[0] = cpu_to_le16(vsi->base_queue); + ctxt->info.valid_sections |= cpu_to_le16(sections); + + /* Reconfigure RSS for main VSI with max queue count */ + vsi->rss_size = max_qcount; + ret = i40e_vsi_config_rss(vsi); + if (ret) { + dev_info(&vsi->back->pdev->dev, + "Failed to reconfig rss for num_queues (%u)\n", + max_qcount); + return ret; + } + vsi->reconfig_rss = true; + dev_dbg(&vsi->back->pdev->dev, + "Reconfigured rss with num_queues (%u)\n", max_qcount); + + /* Find queue count available for channel VSIs and starting offset + * for channel VSIs + */ + override_q = vsi->mqprio_qopt.qopt.count[0]; + if (override_q && override_q < vsi->num_queue_pairs) { + vsi->cnt_q_avail = vsi->num_queue_pairs - override_q; + vsi->next_base_queue = override_q; + } + return 0; +} +#endif + +/** + * i40e_vsi_setup_queue_map - Setup a VSI queue map based on enabled_tc + * @vsi: the VSI being setup + * @ctxt: VSI context structure + * @enabled_tc: Enabled TCs bitmap + * @is_add: True if called before Add VSI + * + * Setup VSI queue mapping for enabled traffic classes. + **/ +static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, + struct i40e_vsi_context *ctxt, + u8 enabled_tc, + bool is_add) +{ + struct i40e_pf *pf = vsi->back; + u16 sections = 0; + u8 netdev_tc = 0; + u16 qcount = 0; + u16 numtc = 1; + u8 offset; + u16 qmap; + int i; + u16 num_tc_qps = 0; + + sections = I40E_AQ_VSI_PROP_QUEUE_MAP_VALID; + offset = 0; + + /* Number of queues per enabled TC */ + num_tc_qps = vsi->alloc_queue_pairs; + if (enabled_tc && (vsi->back->flags & I40E_FLAG_DCB_ENABLED)) { + /* Find numtc from enabled TC bitmap */ + for (i = 0, numtc = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { + if (enabled_tc & BIT(i)) /* TC is enabled */ + numtc++; + } + if (!numtc) { + dev_warn(&pf->pdev->dev, "DCB is enabled but no TC enabled, forcing TC0\n"); + numtc = 1; + } + num_tc_qps = num_tc_qps / numtc; + num_tc_qps = min_t(int, num_tc_qps, + i40e_pf_get_max_q_per_tc(pf)); + } + + vsi->tc_config.numtc = numtc; + vsi->tc_config.enabled_tc = enabled_tc ? enabled_tc : 1; + + /* Do not allow use more TC queue pairs than MSI-X vectors exist */ + if (pf->flags & I40E_FLAG_MSIX_ENABLED) + num_tc_qps = min_t(int, num_tc_qps, pf->num_lan_msix); + + /* Setup queue offset/count for all TCs for given VSI */ + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { + /* See if the given TC is enabled for the given VSI */ + if (vsi->tc_config.enabled_tc & BIT(i)) { + int pow, num_qps; + + switch (vsi->type) { + case I40E_VSI_MAIN: + if (!(pf->flags & (I40E_FLAG_FD_SB_ENABLED | + I40E_FLAG_FD_ATR_ENABLED)) || + vsi->tc_config.enabled_tc != 1) { + qcount = min_t(int, pf->alloc_rss_size, + num_tc_qps); + break; + } + /* fall through */ + case I40E_VSI_FDIR: + case I40E_VSI_SRIOV: + case I40E_VSI_VMDQ2: + default: + qcount = num_tc_qps; WARN_ON(i != 0); break; } @@ -1681,6 +1917,10 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, /* Set actual Tx/Rx queue pairs */ vsi->num_queue_pairs = offset; if ((vsi->type == I40E_VSI_MAIN) && (numtc == 1)) { + /* This code helps add more queue to the VSI if we have + * more cores than RSS can support, the higher cores will + * be served by ATR or other filters. + */ if (vsi->req_queue_pairs > 0) vsi->num_queue_pairs = vsi->req_queue_pairs; else if (pf->flags & I40E_FLAG_MSIX_ENABLED) @@ -1739,6 +1979,14 @@ static int i40e_addr_unsync(struct net_device *netdev, const u8 *addr) struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; + /* Under some circumstances, we might receive a request to delete + * our own device address from our uc list. Because we store the + * device address in the VSI's MAC/VLAN filter list, we need to ignore + * such requests and not delete our device address from this list. + */ + if (ether_addr_equal(addr, netdev->dev_addr)) + return 0; + i40e_del_mac_filter(vsi, addr); return 0; @@ -1763,7 +2011,7 @@ static void i40e_set_rx_mode(struct net_device *netdev) /* check for other flag changes */ if (vsi->current_netdev_flags != vsi->netdev->flags) { vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; - vsi->back->flags |= I40E_FLAG_FILTER_SYNC; + set_bit(__I40E_MACVLAN_SYNC_PENDING, vsi->back->state); } /* schedule our worker thread which will take care of @@ -1774,7 +2022,7 @@ static void i40e_set_rx_mode(struct net_device *netdev) /** * i40e_undo_del_filter_entries - Undo the changes made to MAC filter entries - * @vsi: Pointer to VSI struct + * @vsi: Pointer to vsi struct * @from: Pointer to list which contains MAC filter entries - changes to * those entries needs to be undone. * @@ -1840,7 +2088,7 @@ struct i40e_new_mac_filter *i40e_next_filter(struct i40e_new_mac_filter *next) * from firmware * @count: Number of filters added * @add_list: return data from fw - * @head: pointer to first filter in current batch + * @add_head: pointer to first filter in current batch * * MAC filter entries from list were slated to be added to device. Returns * number of successful filters. Note that 0 does NOT mean success! @@ -1917,17 +2165,16 @@ void i40e_aqc_del_filters(struct i40e_vsi *vsi, const char *vsi_name, * @list: the list of filters to send to firmware * @add_head: Position in the add hlist * @num_add: the number of filters to add - * @promisc_change: set to true on exit if promiscuous mode was forced on * * Send a request to firmware via AdminQ to add a chunk of filters. Will set - * promisc_changed to true if the firmware has run out of space for more - * filters. + * __I40E_VSI_OVERFLOW_PROMISC bit in vsi->state if the firmware has run out of + * space for more filters. */ static void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name, struct i40e_aqc_add_macvlan_element_data *list, struct i40e_new_mac_filter *add_head, - int num_add, bool *promisc_changed) + int num_add) { struct i40e_hw *hw = &vsi->back->hw; int aq_err, fcnt; @@ -1937,18 +2184,29 @@ void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name, fcnt = i40e_update_filter_state(num_add, list, add_head); if (fcnt != num_add) { - *promisc_changed = true; - set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); - dev_warn(&vsi->back->pdev->dev, - "Error %s adding RX filters on %s, promiscuous mode forced on\n", - i40e_aq_str(hw, aq_err), - vsi_name); + if (vsi->type == I40E_VSI_MAIN) { + set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); + dev_warn(&vsi->back->pdev->dev, + "Error %s adding RX filters on %s, promiscuous mode forced on\n", + i40e_aq_str(hw, aq_err), vsi_name); + } else if (vsi->type == I40E_VSI_SRIOV || + vsi->type == I40E_VSI_VMDQ1 || + vsi->type == I40E_VSI_VMDQ2) { + dev_warn(&vsi->back->pdev->dev, + "Error %s adding RX filters on %s, please set promiscuous on manually for %s\n", + i40e_aq_str(hw, aq_err), vsi_name, vsi_name); + } else { + dev_warn(&vsi->back->pdev->dev, + "Error %s adding RX filters on %s, incorrect VSI type: %i.\n", + i40e_aq_str(hw, aq_err), vsi_name, vsi->type); + } } } /** * i40e_aqc_broadcast_filter - Set promiscuous broadcast flags * @vsi: pointer to the VSI + * @vsi_name: the VSI name * @f: filter data * * This function sets or clears the promiscuous broadcast flags for VLAN @@ -1978,11 +2236,13 @@ i40e_aqc_broadcast_filter(struct i40e_vsi *vsi, const char *vsi_name, NULL); } - if (aq_ret) + if (aq_ret) { + set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); dev_warn(&vsi->back->pdev->dev, - "Error %s setting broadcast promiscuous mode on %s\n", + "Error %s, forcing overflow promiscuous on %s\n", i40e_aq_str(hw, hw->aq.asq_last_status), vsi_name); + } return aq_ret; } @@ -1994,7 +2254,7 @@ i40e_aqc_broadcast_filter(struct i40e_vsi *vsi, const char *vsi_name, * * There are different ways of setting promiscuous mode on a PF depending on * what state/environment we're in. This identifies and sets it appropriately. - * Returns 0 on success. + * Returns I40E_SUCCESS on success. **/ static int i40e_set_promiscuous(struct i40e_pf *pf, bool promisc) { @@ -2054,6 +2314,56 @@ static int i40e_set_promiscuous(struct i40e_pf *pf, bool promisc) return aq_ret; } + /** + * i40e_set_switch_mode - sets up switch mode correctly + * @pf: working PF + * @l4type: TCP, UDP, or both + * + * Sets up switch mode correctly + **/ +static void i40e_set_switch_mode(struct i40e_pf *pf, u8 l4type) +{ + struct i40e_hw *hw; + u8 mode; + int ret; + + if (!pf) + return; + + hw = &pf->hw; + + /* Set Bit 7 to be valid */ + mode = I40E_AQ_SET_SWITCH_BIT7_VALID; + + /* We only support destination port filters, so don't set the + * source port bit here. + */ + if (l4type > I40E_AQ_SET_SWITCH_L4_TYPE_BOTH || + l4type < I40E_AQ_SET_SWITCH_L4_TYPE_TCP) { + dev_warn(&pf->pdev->dev, + "invalid L4 type 0x%x, unable to set switch mode\n", + l4type); + return; + } + + mode |= l4type; + + /* Set cloud filter mode */ + mode |= I40E_AQ_SET_SWITCH_MODE_L4_PORT; + + dev_dbg(&pf->pdev->dev, "setting switch mode to 0x%x\n", mode); + /* Prep mode field for set_switch_config */ + ret = i40e_aq_set_switch_config(hw, 0, 0, mode, NULL); + /* If the driver is reloaded, the AQ call will fail. So don't make a + * big deal about it. + */ + if (ret && hw->aq.asq_last_status != I40E_AQ_RC_ESRCH) + dev_dbg(&pf->pdev->dev, + "couldn't set switch config bits, err %s aq_err %s\n", + i40e_stat_str(hw, ret), + i40e_aq_str(hw, hw->aq.asq_last_status)); +} + /** * i40e_sync_vsi_filters - Update the VSI filter list to the HW * @vsi: ptr to the VSI @@ -2068,9 +2378,9 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) struct i40e_mac_filter *f; struct i40e_new_mac_filter *new, *add_head = NULL; struct i40e_hw *hw = &vsi->back->hw; + bool old_overflow, new_overflow; unsigned int failed_filters = 0; unsigned int vlan_filters = 0; - bool promisc_changed = false; char vsi_name[16] = "PF"; int filter_list_len = 0; i40e_status aq_ret = 0; @@ -2092,6 +2402,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) usleep_range(1000, 2000); pf = vsi->back; + old_overflow = test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); + if (vsi->netdev) { changed_flags = vsi->current_netdev_flags ^ vsi->netdev->flags; vsi->current_netdev_flags = vsi->netdev->flags; @@ -2182,7 +2494,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) cmd_flags |= I40E_AQC_MACVLAN_DEL_IGNORE_VLAN; } else { del_list[num_del].vlan_tag = - cpu_to_le16((u16)(f->vlan)); + CPU_TO_LE16((u16)(f->vlan)); } cmd_flags |= I40E_AQC_MACVLAN_DEL_PERFECT_MATCH; @@ -2224,12 +2536,6 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) num_add = 0; hlist_for_each_entry_safe(new, h, &tmp_add_list, hlist) { - if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, - vsi->state)) { - new->state = I40E_FILTER_FAILED; - continue; - } - /* handle broadcast filters by updating the broadcast * promiscuous flag instead of adding a MAC filter. */ @@ -2253,27 +2559,26 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) cmd_flags |= I40E_AQC_MACVLAN_ADD_IGNORE_VLAN; } else { add_list[num_add].vlan_tag = - cpu_to_le16((u16)(new->f->vlan)); + CPU_TO_LE16((u16)(new->f->vlan)); } add_list[num_add].queue_number = 0; /* set invalid match method for later detection */ add_list[num_add].match_method = I40E_AQC_MM_ERR_NO_RES; cmd_flags |= I40E_AQC_MACVLAN_ADD_PERFECT_MATCH; - add_list[num_add].flags = cpu_to_le16(cmd_flags); + add_list[num_add].flags = CPU_TO_LE16(cmd_flags); num_add++; /* flush a full buffer */ if (num_add == filter_list_len) { i40e_aqc_add_filters(vsi, vsi_name, add_list, - add_head, num_add, - &promisc_changed); + add_head, num_add); memset(add_list, 0, list_size); num_add = 0; } } if (num_add) { i40e_aqc_add_filters(vsi, vsi_name, add_list, add_head, - num_add, &promisc_changed); + num_add); } /* Now move all of the filters from the temp add list back to * the VSI's list. @@ -2302,33 +2607,32 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) } spin_unlock_bh(&vsi->mac_filter_hash_lock); - /* If promiscuous mode has changed, we need to calculate a new - * threshold for when we are safe to exit - */ - if (promisc_changed) - vsi->promisc_threshold = (vsi->active_filters * 3) / 4; - /* Check if we are able to exit overflow promiscuous mode. We can * safely exit if we didn't just enter, we no longer have any failed * filters, and we have reduced filters below the threshold value. */ - if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state) && - !promisc_changed && !failed_filters && - (vsi->active_filters < vsi->promisc_threshold)) { + if (old_overflow && !failed_filters && + vsi->active_filters < vsi->promisc_threshold) { dev_info(&pf->pdev->dev, "filter logjam cleared on %s, leaving overflow promiscuous mode\n", vsi_name); clear_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); - promisc_changed = true; vsi->promisc_threshold = 0; } - /* if the VF is not trusted do not do promisc */ if ((vsi->type == I40E_VSI_SRIOV) && !pf->vf[vsi->vf_id].trusted) { clear_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); goto out; } + new_overflow = test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); + + /* If we are entering overflow promiscuous, we need to calculate a new + * threshold for when we are safe to exit + */ + if (!old_overflow && new_overflow) + vsi->promisc_threshold = (vsi->active_filters * 3) / 4; + /* check for changes in promiscuous modes */ if (changed_flags & IFF_ALLMULTI) { bool cur_multipromisc; @@ -2346,15 +2650,18 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) vsi_name, i40e_stat_str(hw, aq_ret), i40e_aq_str(hw, hw->aq.asq_last_status)); + } else { + dev_info(&pf->pdev->dev, "%s is %s allmulti mode.\n", + vsi->netdev->name, + cur_multipromisc ? "entering" : "leaving"); } } - if ((changed_flags & IFF_PROMISC) || promisc_changed) { + if ((changed_flags & IFF_PROMISC) || old_overflow != new_overflow) { bool cur_promisc; cur_promisc = (!!(vsi->current_netdev_flags & IFF_PROMISC) || - test_bit(__I40E_VSI_OVERFLOW_PROMISC, - vsi->state)); + new_overflow); aq_ret = i40e_set_promiscuous(pf, cur_promisc); if (aq_ret) { retval = i40e_aq_rc_to_posix(aq_ret, @@ -2366,6 +2673,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) i40e_stat_str(hw, aq_ret), i40e_aq_str(hw, hw->aq.asq_last_status)); } + } out: /* if something went wrong then set the changed flag so we try again */ @@ -2396,18 +2704,21 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf) { int v; - if (!pf || !(pf->flags & I40E_FLAG_FILTER_SYNC)) + if (!pf) + return; + + if (!test_and_clear_bit(__I40E_MACVLAN_SYNC_PENDING, pf->state)) return; - pf->flags &= ~I40E_FLAG_FILTER_SYNC; for (v = 0; v < pf->num_alloc_vsi; v++) { if (pf->vsi[v] && - (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED)) { + (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED) && + !test_bit(__I40E_VSI_RELEASING, pf->vsi[v]->state)) { int ret = i40e_sync_vsi_filters(pf->vsi[v]); - if (ret) { /* come back and try again later */ - pf->flags |= I40E_FLAG_FILTER_SYNC; + set_bit(__I40E_MACVLAN_SYNC_PENDING, + pf->state); break; } } @@ -2436,26 +2747,52 @@ static int i40e_max_xdp_frame_size(struct i40e_vsi *vsi) static int i40e_change_mtu(struct net_device *netdev, int new_mtu) { struct i40e_netdev_priv *np = netdev_priv(netdev); + int max_frame = new_mtu + I40E_PACKET_HDR_PAD; struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - if (i40e_enabled_xdp_vsi(vsi)) { - int frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + /* MTU < 68 is an error and causes problems on some kernels */ + if ((new_mtu < 68) || (max_frame > I40E_MAX_RXBUFFER)) + return -EINVAL; - if (frame_size > i40e_max_xdp_frame_size(vsi)) + if (i40e_enabled_xdp_vsi(vsi)) { + if (max_frame > i40e_max_xdp_frame_size(vsi)) return -EINVAL; } +#ifndef HAVE_NDO_FEATURES_CHECK + + /* MTU < 576 causes problems with TSO */ + if (new_mtu < 576) { + netdev->features &= ~NETIF_F_TSO; + netdev->features &= ~NETIF_F_TSO6; +#ifdef HAVE_NDO_SET_FEATURES + } else { +#ifndef HAVE_RHEL6_NET_DEVICE_OPS_EXT + if (netdev->wanted_features & NETIF_F_TSO) + netdev->features |= NETIF_F_TSO; + if (netdev->wanted_features & NETIF_F_TSO6) + netdev->features |= NETIF_F_TSO6; +#else + if (netdev_extended(netdev)->wanted_features & NETIF_F_TSO) + netdev->features |= NETIF_F_TSO; + if (netdev_extended(netdev)->wanted_features & NETIF_F_TSO6) + netdev->features |= NETIF_F_TSO6; +#endif /* HAVE_RHEL6_NET_DEVICE_OPS_EXT */ +#endif /* HAVE_NDO_SET_FEATURES */ + } +#endif /* ! HAVE_NDO_FEATURES_CHECK */ netdev_info(netdev, "changing MTU from %d to %d\n", netdev->mtu, new_mtu); netdev->mtu = new_mtu; if (netif_running(netdev)) i40e_vsi_reinit_locked(vsi); - pf->flags |= (I40E_FLAG_SERVICE_CLIENT_REQUESTED | - I40E_FLAG_CLIENT_L2_CHANGE); + set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state); + set_bit(__I40E_CLIENT_L2_CHANGE, pf->state); return 0; } +#if defined(HAVE_PTP_1588_CLOCK) || defined(HAVE_I40E_INTELCIM_IOCTL) /** * i40e_ioctl - Access the hwtstamp interface * @netdev: network interface device structure @@ -2464,19 +2801,34 @@ static int i40e_change_mtu(struct net_device *netdev, int new_mtu) **/ int i40e_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { +#ifdef HAVE_PTP_1588_CLOCK struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; +#endif /* HAVE_PTP_1588_CLOCK */ switch (cmd) { +#ifdef HAVE_PTP_1588_CLOCK +#ifdef SIOCGHWTSTAMP case SIOCGHWTSTAMP: return i40e_ptp_get_ts_config(pf, ifr); +#endif case SIOCSHWTSTAMP: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; return i40e_ptp_set_ts_config(pf, ifr); + case SIOCSPINS: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + return i40e_ptp_set_pins_ioctl(pf, ifr); + case SIOCGPINS: + return i40e_ptp_get_pins(pf, ifr); +#endif /* HAVE_PTP_1588_CLOCK */ default: return -EOPNOTSUPP; } } +#endif /** * i40e_vlan_stripping_enable - Turn on vlan stripping for the VSI * @vsi: the vsi being adjusted @@ -2486,6 +2838,10 @@ void i40e_vlan_stripping_enable(struct i40e_vsi *vsi) struct i40e_vsi_context ctxt; i40e_status ret; + /* Don't modify stripping options if a port vlan is active */ + if (vsi->info.pvid) + return; + if ((vsi->info.valid_sections & cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID)) && ((vsi->info.port_vlan_flags & I40E_AQ_VSI_PVLAN_MODE_MASK) == 0)) @@ -2516,6 +2872,10 @@ void i40e_vlan_stripping_disable(struct i40e_vsi *vsi) struct i40e_vsi_context ctxt; i40e_status ret; + /* Don't modify stripping options if a port vlan is active */ + if (vsi->info.pvid) + return; + if ((vsi->info.valid_sections & cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID)) && ((vsi->info.port_vlan_flags & I40E_AQ_VSI_PVLAN_EMOD_MASK) == @@ -2534,25 +2894,31 @@ void i40e_vlan_stripping_disable(struct i40e_vsi *vsi) "update vlan stripping failed, err %s aq_err %s\n", i40e_stat_str(&vsi->back->hw, ret), i40e_aq_str(&vsi->back->hw, - vsi->back->hw.aq.asq_last_status)); + vsi->back->hw.aq.asq_last_status)); } } +#ifdef HAVE_VLAN_RX_REGISTER /** * i40e_vlan_rx_register - Setup or shutdown vlan offload * @netdev: network interface to be adjusted - * @features: netdev features to test if VLAN offload is enabled or not + * @grp: new vlan group list, NULL if disabling **/ -static void i40e_vlan_rx_register(struct net_device *netdev, u32 features) +static void i40e_vlan_rx_register(struct net_device *netdev, + struct vlan_group *grp) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; + bool enable; - if (features & NETIF_F_HW_VLAN_CTAG_RX) + vsi->vlgrp = grp; + enable = (grp || (vsi->back->flags & I40E_FLAG_DCB_ENABLED)); + if (enable) i40e_vlan_stripping_enable(vsi); else i40e_vlan_stripping_disable(vsi); } +#endif /* HAVE_VLAN_RX_REGISTER */ /** * i40e_add_vlan_all_mac - Add a MAC/VLAN filter for each existing MAC address @@ -2562,7 +2928,7 @@ static void i40e_vlan_rx_register(struct net_device *netdev, u32 features) * This is a helper function for adding a new MAC/VLAN filter with the * specified VLAN for each existing MAC address already in the hash table. * This function does *not* perform any accounting to update filters based on - * VLAN mode. + * vlan mode. * * NOTE: this function expects to be called while under the * mac_filter_hash_lock @@ -2589,17 +2955,14 @@ int i40e_add_vlan_all_mac(struct i40e_vsi *vsi, s16 vid) } /** - * i40e_vsi_add_vlan - Add VSI membership for given VLAN - * @vsi: the VSI being configured - * @vid: VLAN id to be added + * i40e_vsi_add_vlan - Add vsi membership for given vlan + * @vsi: the vsi being configured + * @vid: vlan id to be added **/ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, u16 vid) { int err; - if (vsi->info.pvid) - return -EINVAL; - /* The network stack will attempt to add VID=0, with the intention to * receive priority tagged packets with a VLAN of 0. Our HW receives * these packets by default when configured to receive untagged @@ -2651,9 +3014,9 @@ void i40e_rm_vlan_all_mac(struct i40e_vsi *vsi, s16 vid) } /** - * i40e_vsi_kill_vlan - Remove VSI membership for given VLAN - * @vsi: the VSI being configured - * @vid: VLAN id to be removed + * i40e_vsi_kill_vlan - Remove vsi membership for given vlan + * @vsi: the vsi being configured + * @vid: vlan id to be removed **/ void i40e_vsi_kill_vlan(struct i40e_vsi *vsi, u16 vid) { @@ -2673,36 +3036,126 @@ void i40e_vsi_kill_vlan(struct i40e_vsi *vsi, u16 vid) /** * i40e_vlan_rx_add_vid - Add a vlan id filter to HW offload * @netdev: network interface to be adjusted + * @proto: unused protocol value * @vid: vlan id to be added * * net_device_ops implementation for adding vlan ids **/ +#ifdef HAVE_INT_NDO_VLAN_RX_ADD_VID +#ifdef NETIF_F_HW_VLAN_CTAG_RX static int i40e_vlan_rx_add_vid(struct net_device *netdev, __always_unused __be16 proto, u16 vid) +#else +static int i40e_vlan_rx_add_vid(struct net_device *netdev, u16 vid) +#endif +#else +static void i40e_vlan_rx_add_vid(struct net_device *netdev, u16 vid) +#endif { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; int ret = 0; if (vid >= VLAN_N_VID) +#ifdef HAVE_INT_NDO_VLAN_RX_ADD_VID return -EINVAL; +#else + return; +#endif ret = i40e_vsi_add_vlan(vsi, vid); +#ifndef HAVE_VLAN_RX_REGISTER if (!ret) set_bit(vid, vsi->active_vlans); +#endif /* !HAVE_VLAN_RX_REGISTER */ +#ifndef HAVE_NETDEV_VLAN_FEATURES + + /* Copy feature flags from netdev to the vlan netdev for this vid. + * This allows things like TSO to bubble down to our vlan device. + * Some vlans, such as VLAN 0 for DCB will not have a v_netdev so + * we will not have a netdev that needs updating. + */ + if (vsi->vlgrp) { + struct vlan_group *vlgrp = vsi->vlgrp; + struct net_device *v_netdev = vlan_group_get_device(vlgrp, vid); + if (v_netdev) { + v_netdev->features |= netdev->features; +#ifdef HAVE_ENCAP_CSUM_OFFLOAD + v_netdev->enc_features |= netdev->enc_features; +#endif + vlan_group_set_device(vlgrp, vid, v_netdev); + } + } +#endif /* HAVE_NETDEV_VLAN_FEATURES */ +#ifdef HAVE_INT_NDO_VLAN_RX_ADD_VID return ret; +#endif +} + +/** + * i40e_vlan_rx_add_vid_up - Add a vlan id filter to HW offload in UP path + * @netdev: network interface to be adjusted + * @proto: unused protocol value + * @vid: vlan id to be added + **/ +#ifdef NETIF_F_HW_VLAN_CTAG_RX +static void i40e_vlan_rx_add_vid_up(struct net_device *netdev, + __always_unused __be16 proto, u16 vid) +#else +static void i40e_vlan_rx_add_vid_up(struct net_device *netdev, u16 vid) +#endif +{ +#if (!defined(HAVE_NETDEV_VLAN_FEATURES) || !defined(HAVE_VLAN_RX_REGISTER)) + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; +#endif + + if (vid >= VLAN_N_VID) + return; +#ifndef HAVE_VLAN_RX_REGISTER + set_bit(vid, vsi->active_vlans); +#endif /* !HAVE_VLAN_RX_REGISTER */ +#ifndef HAVE_NETDEV_VLAN_FEATURES + + /* Copy feature flags from netdev to the vlan netdev for this vid. + * This allows things like TSO to bubble down to our vlan device. + * Some vlans, such as VLAN 0 for DCB will not have a v_netdev so + * we will not have a netdev that needs updating. + */ + if (vsi->vlgrp) { + struct vlan_group *vlgrp = vsi->vlgrp; + struct net_device *v_netdev = vlan_group_get_device(vlgrp, vid); + + if (v_netdev) { + v_netdev->features |= netdev->features; +#ifdef HAVE_ENCAP_CSUM_OFFLOAD + v_netdev->enc_features |= netdev->enc_features; +#endif + vlan_group_set_device(vlgrp, vid, v_netdev); + } + } +#endif /* HAVE_NETDEV_VLAN_FEATURES */ } /** * i40e_vlan_rx_kill_vid - Remove a vlan id filter from HW offload * @netdev: network interface to be adjusted + * @proto: unused protocol value * @vid: vlan id to be removed * * net_device_ops implementation for removing vlan ids **/ +#ifdef HAVE_INT_NDO_VLAN_RX_ADD_VID +#ifdef NETIF_F_HW_VLAN_CTAG_RX static int i40e_vlan_rx_kill_vid(struct net_device *netdev, __always_unused __be16 proto, u16 vid) +#else +static int i40e_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) +#endif +#else +static void i40e_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) +#endif { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -2712,10 +3165,14 @@ static int i40e_vlan_rx_kill_vid(struct net_device *netdev, * already printed from the other function */ i40e_vsi_kill_vlan(vsi, vid); +#ifndef HAVE_VLAN_RX_REGISTER clear_bit(vid, vsi->active_vlans); +#endif /* HAVE_VLAN_RX_REGISTER */ +#ifdef HAVE_INT_NDO_VLAN_RX_ADD_VID return 0; +#endif } /** @@ -2729,11 +3186,40 @@ static void i40e_restore_vlan(struct i40e_vsi *vsi) if (!vsi->netdev) return; - i40e_vlan_rx_register(vsi->netdev, vsi->netdev->features); +#ifdef HAVE_VLAN_RX_REGISTER + i40e_vlan_rx_register(vsi->netdev, vsi->vlgrp); + + if (vsi->vlgrp) { + for (vid = 0; vid < VLAN_N_VID; vid++) { + if (!vlan_group_get_device(vsi->vlgrp, vid)) + continue; +#ifdef NETIF_F_HW_VLAN_CTAG_RX + i40e_vlan_rx_add_vid_up(vsi->netdev, htons(ETH_P_8021Q), + vid); +#else + i40e_vlan_rx_add_vid_up(vsi->netdev, vid); +#endif + } + } +#else /* HAVE_VLAN_RX_REGISTER */ +#ifdef NETIF_F_HW_VLAN_CTAG_RX + if (vsi->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) + i40e_vlan_stripping_enable(vsi); +#else + if (vsi->netdev->features & NETIF_F_HW_VLAN_RX) + i40e_vlan_stripping_enable(vsi); +#endif + else + i40e_vlan_stripping_disable(vsi); for_each_set_bit(vid, vsi->active_vlans, VLAN_N_VID) - i40e_vlan_rx_add_vid(vsi->netdev, htons(ETH_P_8021Q), - vid); +#ifdef NETIF_F_HW_VLAN_CTAG_RX + i40e_vlan_rx_add_vid_up(vsi->netdev, htons(ETH_P_8021Q), + vid); +#else + i40e_vlan_rx_add_vid_up(vsi->netdev, vid); +#endif +#endif } /** @@ -2760,7 +3246,7 @@ int i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid) "add pvid failed, err %s aq_err %s\n", i40e_stat_str(&vsi->back->hw, ret), i40e_aq_str(&vsi->back->hw, - vsi->back->hw.aq.asq_last_status)); + vsi->back->hw.aq.asq_last_status)); return -ENOENT; } @@ -2775,9 +3261,47 @@ int i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid) **/ void i40e_vsi_remove_pvid(struct i40e_vsi *vsi) { + vsi->info.pvid = 0; + i40e_vlan_stripping_disable(vsi); +} +/** + * i40e_get_cloud_filter_type - Get cloud filter type + * @flags: set of enabled fields + * @type: location to return type + * + * Given the set of flags indicating which fields are active, look up the type + * number for programming the cloud filter in firmware. If the flags are + * invalid, return I40E_ERR_CONFIG. @type may be NULL, in which case the + * function may be used to verify that the flags would produce a valid type. + **/ +int i40e_get_cloud_filter_type(u8 flags, u16 *type) +{ + static const u16 table[128] = { + [I40E_CLOUD_FILTER_FLAGS_OMAC] = + I40E_AQC_ADD_CLOUD_FILTER_OMAC, + [I40E_CLOUD_FILTER_FLAGS_IMAC] = + I40E_AQC_ADD_CLOUD_FILTER_IMAC, + [I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN] = + I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN, + [I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID] = + I40E_AQC_ADD_CLOUD_FILTER_IMAC_TEN_ID, + [I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC] = + I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC, + [I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID] = + I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN_TEN_ID, + [I40E_CLOUD_FILTER_FLAGS_IIP] = + I40E_AQC_ADD_CLOUD_FILTER_IIP, + }; + + if (flags >= ARRAY_SIZE(table) || table[flags] == 0) + return I40E_ERR_CONFIG; + + /* Return type if we're given space to do so */ + if (type) + *type = table[flags]; - vsi->info.pvid = 0; + return 0; } /** @@ -2790,7 +3314,7 @@ void i40e_vsi_remove_pvid(struct i40e_vsi *vsi) * * Return 0 on success, negative on failure **/ -static int i40e_vsi_setup_tx_resources(struct i40e_vsi *vsi) +int i40e_vsi_setup_tx_resources(struct i40e_vsi *vsi) { int i, err = 0; @@ -2802,7 +3326,6 @@ static int i40e_vsi_setup_tx_resources(struct i40e_vsi *vsi) for (i = 0; i < vsi->num_queue_pairs && !err; i++) err = i40e_setup_tx_descriptors(vsi->xdp_rings[i]); - return err; } @@ -2839,7 +3362,7 @@ static void i40e_vsi_free_tx_resources(struct i40e_vsi *vsi) * * Return 0 on success, negative on failure **/ -static int i40e_vsi_setup_rx_resources(struct i40e_vsi *vsi) +int i40e_vsi_setup_rx_resources(struct i40e_vsi *vsi) { int i, err = 0; @@ -2875,22 +3398,53 @@ static void i40e_vsi_free_rx_resources(struct i40e_vsi *vsi) **/ static void i40e_config_xps_tx_ring(struct i40e_ring *ring) { +#ifndef HAVE_XPS_QOS_SUPPORT struct i40e_vsi *vsi = ring->vsi; +#endif + int cpu; - if (!ring->q_vector || !ring->netdev) + if (!ring->q_vector || !ring->netdev || ring->ch) return; - if ((vsi->tc_config.numtc <= 1) && - !test_and_set_bit(__I40E_TX_XPS_INIT_DONE, &ring->state)) { - netif_set_xps_queue(ring->netdev, - get_cpu_mask(ring->q_vector->v_idx), - ring->queue_index); - } +#ifndef HAVE_XPS_QOS_SUPPORT + /* Some older kernels do not support XPS with QoS */ + if (vsi->tc_config.numtc > 1) { +#ifndef HAVE_NETDEV_TC_RESETS_XPS + /* Additionally, some kernels do not properly clear the XPS + * mapping when the number of traffic classes is changed. In + * order to support these kernels we work around this by + * setting the XPS mapping to the empty cpu set. + */ + cpumask_var_t mask; - /* schedule our worker thread which will take care of - * applying the new filter changes - */ - i40e_service_event_schedule(vsi->back); + /* Only clear the settings if we initialized XPS */ + if (!test_and_clear_bit(__I40E_TX_XPS_INIT_DONE, ring->state)) + return; + + if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) + return; + + netif_set_xps_queue(ring->netdev, mask, ring->queue_index); + free_cpumask_var(mask); +#endif /* !HAVE_NETDEV_TC_RESETS_XPS */ + return; + } + +#endif /* !HAVE_XPS_QOS_SUPPORT */ + /* We only initialize XPS once, so as not to overwrite user settings */ + if (test_and_set_bit(__I40E_TX_XPS_INIT_DONE, ring->state)) + return; + + cpu = cpumask_local_spread(ring->q_vector->v_idx, -1); +#ifndef HAVE_NETIF_SET_XPS_QUEUE_CONST_MASK + /* In kernels before 3.12 the second parameter has no const qualifier. + * It is generating warning in older kernels. + */ + netif_set_xps_queue(ring->netdev, (struct cpumask *)get_cpu_mask(cpu), + ring->queue_index); +#else /* !HAVE_NETIF_SET_XPS_QUEUE_CONST_MASK */ + netif_set_xps_queue(ring->netdev, get_cpu_mask(cpu), ring->queue_index); +#endif /* !HAVE_NETIF_SET_XPS_QUEUE_CONST_MASK */ } /** @@ -2915,7 +3469,6 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) } else { ring->atr_sample_rate = 0; } - /* configure XPS */ i40e_config_xps_tx_ring(ring); @@ -2927,7 +3480,9 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) tx_ctx.qlen = ring->count; tx_ctx.fd_ena = !!(vsi->back->flags & (I40E_FLAG_FD_SB_ENABLED | I40E_FLAG_FD_ATR_ENABLED)); +#ifdef HAVE_PTP_1588_CLOCK tx_ctx.timesync_ena = !!(vsi->back->flags & I40E_FLAG_PTP); +#endif /* HAVE_PTP_1588_CLOCK */ /* FDIR VSI tx ring can still use RS bit and writebacks */ if (vsi->type != I40E_VSI_FDIR) tx_ctx.head_wb_ena = 1; @@ -2944,7 +3499,14 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) * initialization. This has to be done regardless of * DCB as by default everything is mapped to TC0. */ - tx_ctx.rdylist = le16_to_cpu(vsi->info.qs_handle[ring->dcb_tc]); + + if (ring->ch) + tx_ctx.rdylist = + le16_to_cpu(ring->ch->info.qs_handle[ring->dcb_tc]); + + else + tx_ctx.rdylist = le16_to_cpu(vsi->info.qs_handle[ring->dcb_tc]); + tx_ctx.rdylist_act = 0; /* clear the context in the HMC */ @@ -2966,12 +3528,23 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) } /* Now associate this queue with this PCI function */ - if (vsi->type == I40E_VSI_VMDQ2) { - qtx_ctl = I40E_QTX_CTL_VM_QUEUE; - qtx_ctl |= ((vsi->id) << I40E_QTX_CTL_VFVM_INDX_SHIFT) & - I40E_QTX_CTL_VFVM_INDX_MASK; + if (ring->ch) { + if (ring->ch->type == I40E_VSI_VMDQ2) + qtx_ctl = I40E_QTX_CTL_VM_QUEUE; + else + return -EINVAL; + + qtx_ctl |= (ring->ch->vsi_number << + I40E_QTX_CTL_VFVM_INDX_SHIFT) & + I40E_QTX_CTL_VFVM_INDX_MASK; } else { - qtx_ctl = I40E_QTX_CTL_PF_QUEUE; + if (vsi->type == I40E_VSI_VMDQ2) { + qtx_ctl = I40E_QTX_CTL_VM_QUEUE; + qtx_ctl |= ((vsi->id) << I40E_QTX_CTL_VFVM_INDX_SHIFT) & + I40E_QTX_CTL_VFVM_INDX_MASK; + } else { + qtx_ctl = I40E_QTX_CTL_PF_QUEUE; + } } qtx_ctl |= ((hw->pf_id << I40E_QTX_CTL_PF_INDX_SHIFT) & @@ -3000,7 +3573,7 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) struct i40e_hmc_obj_rxq rx_ctx; i40e_status err = 0; - ring->state = 0; + bitmap_zero(ring->state, __I40E_RING_STATE_NBITS); /* clear the context structure first */ memset(&rx_ctx, 0, sizeof(rx_ctx)); @@ -3014,7 +3587,12 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) rx_ctx.qlen = ring->count; /* use 32 byte descriptors */ +#ifdef I40E_32BYTE_RX rx_ctx.dsize = 1; +#else + /* use 16 byte descriptors */ + rx_ctx.dsize = 0; +#endif /* descriptor type is always zero * rx_ctx.dtype = 0; @@ -3022,10 +3600,7 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) rx_ctx.hsplit_0 = 0; rx_ctx.rxmax = min_t(u16, vsi->max_frame, chain_len * ring->rx_buf_len); - if (hw->revision_id == 0) - rx_ctx.lrxqthresh = 0; - else - rx_ctx.lrxqthresh = 2; + rx_ctx.lrxqthresh = 1; rx_ctx.crcstrip = 1; rx_ctx.l2tsel = 1; /* this controls whether VLAN is stripped from inner headers */ @@ -3099,6 +3674,17 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi) { int err = 0; u16 i; +#ifdef CONFIG_I40E_DISABLE_PACKET_SPLIT + u16 max_frame = I40E_RXBUFFER_1536 - NET_IP_ALIGN; + + if (!vsi->netdev) + max_frame = I40E_RXBUFFER_2048; + else if (vsi->netdev->mtu + I40E_PACKET_HDR_PAD > max_frame) + max_frame = vsi->netdev->mtu + I40E_PACKET_HDR_PAD; + + vsi->max_frame = max_frame; + vsi->rx_buf_len = max_frame; +#else /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ if (!vsi->netdev || (vsi->back->flags & I40E_FLAG_LEGACY_RX)) { vsi->max_frame = I40E_MAX_RXBUFFER; @@ -3114,6 +3700,7 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi) vsi->rx_buf_len = (PAGE_SIZE < 8192) ? I40E_RXBUFFER_3072 : I40E_RXBUFFER_2048; } +#endif /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ /* set up individual rings */ for (i = 0; i < vsi->num_queue_pairs && !err; i++) @@ -3140,6 +3727,7 @@ static void i40e_vsi_config_dcb_rings(struct i40e_vsi *vsi) rx_ring->dcb_tc = 0; tx_ring->dcb_tc = 0; } + return; } for (n = 0; n < I40E_MAX_TRAFFIC_CLASS; n++) { @@ -3165,6 +3753,7 @@ static void i40e_set_vsi_rx_mode(struct i40e_vsi *vsi) { if (vsi->netdev) i40e_set_rx_mode(vsi->netdev); + } /** @@ -3183,7 +3772,7 @@ static void i40e_fdir_filter_restore(struct i40e_vsi *vsi) if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED)) return; - /* Reset FDir counters as we're replaying all existing filters */ + /* reset FDIR counters as we're replaying all existing filters */ pf->fd_tcp4_filter_cnt = 0; pf->fd_udp4_filter_cnt = 0; pf->fd_sctp4_filter_cnt = 0; @@ -3195,6 +3784,209 @@ static void i40e_fdir_filter_restore(struct i40e_vsi *vsi) } } +/** + * i40e_cloud_filter_restore - Restore the switch's cloud filters + * @pf: Pointer to the targeted VSI + * + * This function replays the cloud filter hlist into the hw switch + **/ +static void i40e_cloud_filter_restore(struct i40e_pf *pf) +{ + struct i40e_cloud_filter *filter; + struct hlist_node *node; + + hlist_for_each_entry_safe(filter, node, + &pf->cloud_filter_list, cloud_node) { + i40e_add_del_cloud_filter_ex(pf, filter, true); + } +} + +/** + * i40e_set_cld_element - sets cloud filter element data + * @filter: cloud filter rule + * @cld: ptr to cloud filter element data + * + * This is helper function to copy data into cloud filter element + **/ +static inline void +i40e_set_cld_element(struct i40e_cloud_filter *filter, + struct i40e_aqc_cloud_filters_element_data *cld) +{ + int i, j; + u32 ipa; + + memset(cld, 0, sizeof(*cld)); + ether_addr_copy(cld->outer_mac, filter->dst_mac); + ether_addr_copy(cld->inner_mac, filter->src_mac); + + if (filter->n_proto != ETH_P_IP && filter->n_proto != ETH_P_IPV6) { + /* copy parameters from filter to cloud filters element + * which are not specific to IP protos + */ + ether_addr_copy(cld->outer_mac, filter->outer_mac); + ether_addr_copy(cld->inner_mac, filter->inner_mac); + cld->inner_vlan = cpu_to_le16(ntohs(filter->inner_vlan)); + cld->tenant_id = cpu_to_le32(filter->tenant_id); + return; + } + + if (filter->n_proto == ETH_P_IPV6) { +#define IPV6_MAX_INDEX (ARRAY_SIZE(filter->dst_ipv6) - 1) + for (i = 0, j = 0; i < ARRAY_SIZE(filter->dst_ipv6); + i++, j += 2) { + ipa = be32_to_cpu(filter->dst_ipv6[IPV6_MAX_INDEX - i]); + memcpy(&cld->ipaddr.raw_v6.data[j], &ipa, sizeof(ipa)); + } + } else { + ipa = be32_to_cpu(filter->dst_ipv4); + memcpy(&cld->ipaddr.v4.data, &ipa, sizeof(ipa)); + } + + cld->inner_vlan = cpu_to_le16(ntohs(filter->vlan_id)); + + /* tenant_id is not supported by FW now, once the support is enabled + * fill the cld->tenant_id with cpu_to_le32(filter->tenant_id) + */ + if (filter->tenant_id) + return; +} + +/* i40e_add_del_cloud_filter_ex - Add/del cloud filter using big_buf + * @pf: working PF + * @filter: cloud filter rule + * @add: if true, add, if false, delete + * + * Add or delete a cloud filter for a specific flow spec using big buffer. + * Returns 0 if the filter were successfully added. + **/ +int i40e_add_del_cloud_filter_ex(struct i40e_pf *pf, + struct i40e_cloud_filter *filter, + bool add) +{ + struct i40e_aqc_cloud_filters_element_bb cld_filter; + int ret; + + if (!i40e_is_l4mode_enabled()) { + struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; + + return i40e_add_del_cloud_filter(vsi, filter, add); + } + + /* Make sure port is specified, otherwise bail out, for channel + * specific cloud filter needs 'L4 port' to be non-zero + */ + if (!filter->dst_port) + return -EINVAL; + + memset(&cld_filter, 0, sizeof(cld_filter)); + + /* Switch is in Mode 1, so this is an L4 port filter */ + cld_filter.element.flags = + cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_IP_PORT); + + /* Now copy L4 port in Byte 6..7 in general fields */ + cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0] = + be16_to_cpu(filter->dst_port); + + if (add) + ret = i40e_aq_add_cloud_filters_bb(&pf->hw, + filter->seid, + &cld_filter, 1); + else + ret = i40e_aq_rem_cloud_filters_bb(&pf->hw, + filter->seid, + &cld_filter, 1); + + if (ret) + dev_err(&pf->pdev->dev, + "fail to %s cloud filter err %d aq_err %d\n", + add ? "add" : "delete", + ret, pf->hw.aq.asq_last_status); + + dev_info(&pf->pdev->dev, + "%s cloud filter for VSI: %d, L4 port: %d\n", + add ? "add" : "delete", + filter->seid, be16_to_cpu(filter->dst_port)); + + return ret; +} + +/** + * i40e_add_del_cloud_filter - Add/del cloud filter + * @vsi: pointer to VSI + * @filter: cloud filter rule + * @add: if true, add, if false, delete + * + * Add or delete a cloud filter for a specific flow spec. + * Returns 0 if the filter were successfully added. + **/ +int i40e_add_del_cloud_filter(struct i40e_vsi *vsi, + struct i40e_cloud_filter *filter, bool add) +{ + struct i40e_aqc_cloud_filters_element_data cld_filter; + struct i40e_pf *pf = vsi->back; + int ret; + static const u16 flag_table[128] = { + [I40E_CLOUD_FILTER_FLAGS_OMAC] = + I40E_AQC_ADD_CLOUD_FILTER_OMAC, + [I40E_CLOUD_FILTER_FLAGS_IMAC] = + I40E_AQC_ADD_CLOUD_FILTER_IMAC, + [I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN] = + I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN, + [I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID] = + I40E_AQC_ADD_CLOUD_FILTER_IMAC_TEN_ID, + [I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC] = + I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC, + [I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID] = + I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN_TEN_ID, + [I40E_CLOUD_FILTER_FLAGS_IIP] = + I40E_AQC_ADD_CLOUD_FILTER_IIP, + }; + + if (filter->flags >= ARRAY_SIZE(flag_table)) + return I40E_ERR_CONFIG; + + /* copy element needed to add cloud filter from filter */ + i40e_set_cld_element(filter, &cld_filter); + + if (filter->tunnel_type != I40E_CLOUD_TNL_TYPE_NONE) + cld_filter.flags = cpu_to_le16(filter->tunnel_type << + I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT); + + /* copy queue number from filter to pass to cloud filter engine + * with flags for targeting traffic to specific queue + */ + if (filter->flags != I40E_CLOUD_FILTER_FLAGS_OMAC) { + cld_filter.flags |= + cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_TO_QUEUE); + cld_filter.queue_number = cpu_to_le16(filter->queue_id); + } + + if (filter->n_proto == ETH_P_IPV6) + cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] | + I40E_AQC_ADD_CLOUD_FLAGS_IPV6); + else + cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] | + I40E_AQC_ADD_CLOUD_FLAGS_IPV4); + + if (add) + ret = i40e_aq_add_cloud_filters(&pf->hw, filter->seid, + &cld_filter, 1); + else + ret = i40e_aq_rem_cloud_filters(&pf->hw, filter->seid, + &cld_filter, 1); + if (ret) + dev_dbg(&pf->pdev->dev, + "Failed to %s cloud filter using l4 port %u, err %d aq_err %d\n", + add ? "add" : "delete", filter->dst_port, ret, + pf->hw.aq.asq_last_status); + else + dev_info(&pf->pdev->dev, + "%s cloud filter for VSI: %d\n", + add ? "Added" : "Deleted", filter->seid); + return ret; +} + /** * i40e_vsi_configure - Set up the VSI for action * @vsi: the VSI being configured @@ -3235,55 +4027,46 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) for (i = 0; i < vsi->num_q_vectors; i++, vector++) { struct i40e_q_vector *q_vector = vsi->q_vectors[i]; - q_vector->itr_countdown = ITR_COUNTDOWN_START; - q_vector->rx.itr = ITR_TO_REG(vsi->rx_rings[i]->rx_itr_setting); - q_vector->rx.latency_range = I40E_LOW_LATENCY; + q_vector->rx.next_update = jiffies + 1; + q_vector->rx.target_itr = + ITR_TO_REG(vsi->rx_rings[i]->itr_setting); wr32(hw, I40E_PFINT_ITRN(I40E_RX_ITR, vector - 1), - q_vector->rx.itr); - q_vector->tx.itr = ITR_TO_REG(vsi->tx_rings[i]->tx_itr_setting); - q_vector->tx.latency_range = I40E_LOW_LATENCY; + q_vector->rx.target_itr >> 1); + q_vector->rx.current_itr = q_vector->rx.target_itr; + + q_vector->tx.next_update = jiffies + 1; + q_vector->tx.target_itr = + ITR_TO_REG(vsi->tx_rings[i]->itr_setting); wr32(hw, I40E_PFINT_ITRN(I40E_TX_ITR, vector - 1), - q_vector->tx.itr); + q_vector->tx.target_itr >> 1); + q_vector->tx.current_itr = q_vector->tx.target_itr; + wr32(hw, I40E_PFINT_RATEN(vector - 1), i40e_intrl_usec_to_reg(vsi->int_rate_limit)); - /* Linked list for the queuepairs assigned to this vector */ + /* begin of linked list for RX queue assigned to this vector */ wr32(hw, I40E_PFINT_LNKLSTN(vector - 1), qp); for (q = 0; q < q_vector->num_ringpairs; q++) { u32 nextqp = has_xdp ? qp + vsi->alloc_queue_pairs : qp; u32 val; - val = I40E_QINT_RQCTL_CAUSE_ENA_MASK | - (I40E_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT) | - (vector << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) | - (nextqp << I40E_QINT_RQCTL_NEXTQ_INDX_SHIFT) | - (I40E_QUEUE_TYPE_TX << - I40E_QINT_RQCTL_NEXTQ_TYPE_SHIFT); - + /* RX queue in linked list with next queue set to TX */ + val = I40E_QINT_RQCTL_VAL(nextqp, vector, TX); wr32(hw, I40E_QINT_RQCTL(qp), val); if (has_xdp) { - val = I40E_QINT_TQCTL_CAUSE_ENA_MASK | - (I40E_TX_ITR << I40E_QINT_TQCTL_ITR_INDX_SHIFT) | - (vector << I40E_QINT_TQCTL_MSIX_INDX_SHIFT) | - (qp << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT) | - (I40E_QUEUE_TYPE_TX << - I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT); - + /* TX queue with next queue set to TX */ + val = I40E_QINT_TQCTL_VAL(qp, vector, TX); wr32(hw, I40E_QINT_TQCTL(nextqp), val); } - val = I40E_QINT_TQCTL_CAUSE_ENA_MASK | - (I40E_TX_ITR << I40E_QINT_TQCTL_ITR_INDX_SHIFT) | - (vector << I40E_QINT_TQCTL_MSIX_INDX_SHIFT) | - ((qp + 1) << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT) | - (I40E_QUEUE_TYPE_RX << - I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT); + /* TX queue with next RX or end of linked list */ + val = I40E_QINT_TQCTL_VAL((qp + 1), vector, RX); /* Terminate the linked list */ if (q == (q_vector->num_ringpairs - 1)) - val |= (I40E_QUEUE_END_OF_LIST << - I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT); + val |= (I40E_QUEUE_END_OF_LIST + << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT); wr32(hw, I40E_QINT_TQCTL(qp), val); qp++; @@ -3295,7 +4078,7 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) /** * i40e_enable_misc_int_causes - enable the non-queue interrupts - * @hw: ptr to the hardware info + * @pf: pointer to private device data structure **/ static void i40e_enable_misc_int_causes(struct i40e_pf *pf) { @@ -3314,12 +4097,13 @@ static void i40e_enable_misc_int_causes(struct i40e_pf *pf) I40E_PFINT_ICR0_ENA_HMC_ERR_MASK | I40E_PFINT_ICR0_ENA_VFLR_MASK | I40E_PFINT_ICR0_ENA_ADMINQ_MASK; - if (pf->flags & I40E_FLAG_IWARP_ENABLED) val |= I40E_PFINT_ICR0_ENA_PE_CRITERR_MASK; +#ifdef HAVE_PTP_1588_CLOCK if (pf->flags & I40E_FLAG_PTP) val |= I40E_PFINT_ICR0_ENA_TIMESYNC_MASK; +#endif /* HAVE_PTP_1588_CLOCK */ wr32(hw, I40E_PFINT_ICR0_ENA, val); @@ -3341,44 +4125,36 @@ static void i40e_configure_msi_and_legacy(struct i40e_vsi *vsi) struct i40e_q_vector *q_vector = vsi->q_vectors[0]; struct i40e_pf *pf = vsi->back; struct i40e_hw *hw = &pf->hw; - u32 val; /* set the ITR configuration */ - q_vector->itr_countdown = ITR_COUNTDOWN_START; - q_vector->rx.itr = ITR_TO_REG(vsi->rx_rings[0]->rx_itr_setting); - q_vector->rx.latency_range = I40E_LOW_LATENCY; - wr32(hw, I40E_PFINT_ITR0(I40E_RX_ITR), q_vector->rx.itr); - q_vector->tx.itr = ITR_TO_REG(vsi->tx_rings[0]->tx_itr_setting); - q_vector->tx.latency_range = I40E_LOW_LATENCY; - wr32(hw, I40E_PFINT_ITR0(I40E_TX_ITR), q_vector->tx.itr); + q_vector->rx.next_update = jiffies + 1; + q_vector->rx.target_itr = ITR_TO_REG(vsi->rx_rings[0]->itr_setting); + wr32(hw, I40E_PFINT_ITR0(I40E_RX_ITR), q_vector->rx.target_itr >> 1); + q_vector->rx.current_itr = q_vector->rx.target_itr; + q_vector->tx.next_update = jiffies + 1; + q_vector->tx.target_itr = ITR_TO_REG(vsi->tx_rings[0]->itr_setting); + wr32(hw, I40E_PFINT_ITR0(I40E_TX_ITR), q_vector->tx.target_itr >> 1); + q_vector->tx.current_itr = q_vector->tx.target_itr; i40e_enable_misc_int_causes(pf); /* FIRSTQ_INDX = 0, FIRSTQ_TYPE = 0 (rx) */ wr32(hw, I40E_PFINT_LNKLST0, 0); - /* Associate the queue pair to the vector and enable the queue int */ - val = I40E_QINT_RQCTL_CAUSE_ENA_MASK | - (I40E_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT) | - (nextqp << I40E_QINT_RQCTL_NEXTQ_INDX_SHIFT)| - (I40E_QUEUE_TYPE_TX << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT); - - wr32(hw, I40E_QINT_RQCTL(0), val); - - if (i40e_enabled_xdp_vsi(vsi)) { - val = I40E_QINT_TQCTL_CAUSE_ENA_MASK | - (I40E_TX_ITR << I40E_QINT_TQCTL_ITR_INDX_SHIFT)| - (I40E_QUEUE_TYPE_TX - << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT); + /* Associate the queue pair to the vector and enable the queue + * interrupt RX queue in linked list with next queue set to TX + */ + wr32(hw, I40E_QINT_RQCTL(0), I40E_QINT_RQCTL_VAL(nextqp, 0, TX)); - wr32(hw, I40E_QINT_TQCTL(nextqp), val); + if (nextqp) { + /* TX queue in linked list with next queue set to TX */ + wr32(hw, I40E_QINT_TQCTL(nextqp), + I40E_QINT_TQCTL_VAL(nextqp, 0, TX)); } - val = I40E_QINT_TQCTL_CAUSE_ENA_MASK | - (I40E_TX_ITR << I40E_QINT_TQCTL_ITR_INDX_SHIFT) | - (I40E_QUEUE_END_OF_LIST << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT); - - wr32(hw, I40E_QINT_TQCTL(0), val); + /* last TX queue so the next RX queue doesn't matter */ + wr32(hw, I40E_QINT_TQCTL(0), + I40E_QINT_TQCTL_VAL(I40E_QUEUE_END_OF_LIST, 0, RX)); i40e_flush(hw); } @@ -3398,15 +4174,14 @@ void i40e_irq_dynamic_disable_icr0(struct i40e_pf *pf) /** * i40e_irq_dynamic_enable_icr0 - Enable default interrupt generation for icr0 * @pf: board private structure - * @clearpba: true when all pending interrupt events should be cleared **/ -void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf, bool clearpba) +void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; u32 val; val = I40E_PFINT_DYN_CTL0_INTENA_MASK | - (clearpba ? I40E_PFINT_DYN_CTL0_CLEARPBA_MASK : 0) | + I40E_PFINT_DYN_CTL0_CLEARPBA_MASK | (I40E_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT); wr32(hw, I40E_PFINT_DYN_CTL0, val); @@ -3430,6 +4205,7 @@ static irqreturn_t i40e_msix_clean_rings(int irq, void *data) return IRQ_HANDLED; } +#ifdef HAVE_IRQ_AFFINITY_NOTIFY /** * i40e_irq_affinity_notify - Callback for affinity changes * @notify: context as to what irq was changed @@ -3456,6 +4232,7 @@ static void i40e_irq_affinity_notify(struct irq_affinity_notify *notify, * receive notifications. **/ static void i40e_irq_affinity_release(struct kref *ref) {} +#endif /* HAVE_IRQ_AFFINITY_NOTIFY */ /** * i40e_vsi_request_irq_msix - Initialize MSI-X interrupts @@ -3464,7 +4241,7 @@ static void i40e_irq_affinity_release(struct kref *ref) {} * * Allocates MSI-X vectors and requests interrupts from the kernel. **/ -static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) +int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) { int q_vectors = vsi->num_q_vectors; struct i40e_pf *pf = vsi->back; @@ -3473,6 +4250,9 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) int tx_int_idx = 0; int vector, err; int irq_num; +#ifdef HAVE_IRQ_AFFINITY_HINT + int cpu; +#endif for (vector = 0; vector < q_vectors; vector++) { struct i40e_q_vector *q_vector = vsi->q_vectors[vector]; @@ -3504,14 +4284,22 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) goto free_queue_irqs; } +#ifdef HAVE_IRQ_AFFINITY_NOTIFY /* register for affinity change notifications */ q_vector->affinity_notify.notify = i40e_irq_affinity_notify; q_vector->affinity_notify.release = i40e_irq_affinity_release; irq_set_affinity_notifier(irq_num, &q_vector->affinity_notify); - /* get_cpu_mask returns a static constant mask with - * a permanent lifetime so it's ok to use here. +#endif +#ifdef HAVE_IRQ_AFFINITY_HINT + /* Spread affinity hints out across online CPUs. + * + * get_cpu_mask returns a static constant mask with + * a permanent lifetime so it's ok to pass to + * irq_set_affinity_hint without making a copy. */ - irq_set_affinity_hint(irq_num, get_cpu_mask(q_vector->v_idx)); + cpu = cpumask_local_spread(q_vector->v_idx, -1); + irq_set_affinity_hint(irq_num, get_cpu_mask(cpu)); +#endif /* HAVE_IRQ_AFFINITY_HINT */ } vsi->irqs_ready = true; @@ -3521,8 +4309,12 @@ free_queue_irqs: while (vector) { vector--; irq_num = pf->msix_entries[base + vector].vector; +#ifdef HAVE_IRQ_AFFINITY_NOTIFY irq_set_affinity_notifier(irq_num, NULL); +#endif +#ifdef HAVE_IRQ_AFFINITY_HINT irq_set_affinity_hint(irq_num, NULL); +#endif free_irq(irq_num, &vsi->q_vectors[vector]); } return err; @@ -3587,7 +4379,7 @@ static int i40e_vsi_enable_irq(struct i40e_vsi *vsi) for (i = 0; i < vsi->num_q_vectors; i++) i40e_irq_dynamic_enable(vsi, i); } else { - i40e_irq_dynamic_enable_icr0(pf, true); + i40e_irq_dynamic_enable_icr0(pf); } i40e_flush(&pf->hw); @@ -3595,14 +4387,20 @@ static int i40e_vsi_enable_irq(struct i40e_vsi *vsi) } /** - * i40e_stop_misc_vector - Stop the vector that handles non-queue events + * i40e_free_misc_vector - Free the vector that handles non-queue events * @pf: board private structure **/ -static void i40e_stop_misc_vector(struct i40e_pf *pf) +static void i40e_free_misc_vector(struct i40e_pf *pf) { /* Disable ICR 0 */ wr32(&pf->hw, I40E_PFINT_ICR0_ENA, 0); i40e_flush(&pf->hw); + + if (pf->flags & I40E_FLAG_MSIX_ENABLED && pf->msix_entries) { + synchronize_irq(pf->msix_entries[0].vector); + free_irq(pf->msix_entries[0].vector, pf); + clear_bit(__I40E_MISC_IRQ_REQUESTED, pf->state); + } } /** @@ -3633,7 +4431,6 @@ static irqreturn_t i40e_intr(int irq, void *data) if (((icr0 & ~I40E_PFINT_ICR0_INTEVENT_MASK) == 0) || (icr0 & I40E_PFINT_ICR0_SWINT_MASK)) pf->sw_int_count++; - if ((pf->flags & I40E_FLAG_IWARP_ENABLED) && (icr0 & I40E_PFINT_ICR0_ENA_PE_CRITERR_MASK)) { ena_mask &= ~I40E_PFINT_ICR0_ENA_PE_CRITERR_MASK; @@ -3697,15 +4494,20 @@ static irqreturn_t i40e_intr(int irq, void *data) rd32(hw, I40E_PFHMC_ERRORDATA)); } +#ifdef HAVE_PTP_1588_CLOCK if (icr0 & I40E_PFINT_ICR0_TIMESYNC_MASK) { u32 prttsyn_stat = rd32(hw, I40E_PRTTSYN_STAT_0); - if (prttsyn_stat & I40E_PRTTSYN_STAT_0_TXTIME_MASK) { - icr0 &= ~I40E_PFINT_ICR0_ENA_TIMESYNC_MASK; + if (prttsyn_stat & I40E_PRTTSYN_STAT_0_EVENT0_MASK) + schedule_work(&pf->ptp_extts0_work); + + if (prttsyn_stat & I40E_PRTTSYN_STAT_0_TXTIME_MASK) i40e_ptp_tx_hwtstamp(pf); - } + + icr0 &= ~I40E_PFINT_ICR0_ENA_TIMESYNC_MASK; } +#endif /* HAVE_PTP_1588_CLOCK */ /* If a critical error is pending we have no choice but to reset the * device. * Report and mask out any remaining unexpected interrupts. @@ -3728,9 +4530,10 @@ static irqreturn_t i40e_intr(int irq, void *data) enable_intr: /* re-enable interrupt causes */ wr32(hw, I40E_PFINT_ICR0_ENA, ena_mask); - if (!test_bit(__I40E_DOWN, pf->state)) { + if (!test_bit(__I40E_DOWN, pf->state) || + test_bit(__I40E_RECOVERY_MODE, pf->state)) { i40e_service_event_schedule(pf); - i40e_irq_dynamic_enable_icr0(pf, false); + i40e_irq_dynamic_enable_icr0(pf); } return ret; @@ -3762,7 +4565,7 @@ static bool i40e_clean_fdir_tx_irq(struct i40e_ring *tx_ring, int budget) break; /* prevent any other reads prior to eop_desc */ - smp_rmb(); + read_barrier_depends(); /* if the descriptor isn't done, no work yet to do */ if (!(eop_desc->cmd_type_offset_bsz & @@ -3903,6 +4706,7 @@ static void i40e_vsi_map_rings_to_vectors(struct i40e_vsi *vsi) num_ringpairs = DIV_ROUND_UP(qp_remaining, q_vectors - v_start); q_vector->num_ringpairs = num_ringpairs; + q_vector->reg_idx = q_vector->v_idx + vsi->base_vector - 1; q_vector->rx.count = 0; q_vector->tx.count = 0; @@ -4053,8 +4857,8 @@ static void i40e_control_tx_q(struct i40e_pf *pf, int pf_q, bool enable) * @is_xdp: true if the queue is used for XDP * @enable: start or stop the queue **/ -static int i40e_control_wait_tx_q(int seid, struct i40e_pf *pf, int pf_q, - bool is_xdp, bool enable) +int i40e_control_wait_tx_q(int seid, struct i40e_pf *pf, int pf_q, + bool is_xdp, bool enable) { int ret; @@ -4096,10 +4900,10 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable) ret = i40e_control_wait_tx_q(vsi->seid, pf, pf_q + vsi->alloc_queue_pairs, true /*is xdp*/, enable); + if (ret) break; } - return ret; } @@ -4138,9 +4942,9 @@ static int i40e_pf_rxq_wait(struct i40e_pf *pf, int pf_q, bool enable) * @pf_q: the PF queue to configure * @enable: start or stop the queue * - * This function enables or disables a single queue. Note that any delay - * required after the operation is expected to be handled by the caller of - * this function. + * This function enables or disables a single queue. Note that + * any delay required after the operation is expected to be + * handled by the caller of this function. **/ static void i40e_control_rx_q(struct i40e_pf *pf, int pf_q, bool enable) { @@ -4169,6 +4973,30 @@ static void i40e_control_rx_q(struct i40e_pf *pf, int pf_q, bool enable) wr32(hw, I40E_QRX_ENA(pf_q), rx_reg); } +/** + * i40e_control_wait_rx_q + * @pf: the PF structure + * @pf_q: queue being configured + * @enable: start or stop the rings + * + * This function enables or disables a single queue along with waiting + * for the change to finish. The caller of this function should handle + * the delays needed in the case of disabling queues. + **/ +int i40e_control_wait_rx_q(struct i40e_pf *pf, int pf_q, bool enable) +{ + int ret = 0; + + i40e_control_rx_q(pf, pf_q, enable); + + /* wait for the change to finish */ + ret = i40e_pf_rxq_wait(pf, pf_q, enable); + if (ret) + return ret; + + return ret; +} + /** * i40e_vsi_control_rx - Start or stop a VSI's rings * @vsi: the VSI being configured @@ -4181,10 +5009,7 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable) pf_q = vsi->base_queue; for (i = 0; i < vsi->num_queue_pairs; i++, pf_q++) { - i40e_control_rx_q(pf, pf_q, enable); - - /* wait for the change to finish */ - ret = i40e_pf_rxq_wait(pf, pf_q, enable); + ret = i40e_control_wait_rx_q(pf, pf_q, enable); if (ret) { dev_info(&pf->pdev->dev, "VSI seid %d Rx ring %d %sable timeout\n", @@ -4193,9 +5018,7 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable) } } - /* Due to HW errata, on Rx disable only, the register can indicate done - * before it really is. Needs 50ms to be sure - */ + /* HW needs up to 50ms to finish RX queue disable*/ if (!enable) mdelay(50); @@ -4291,10 +5114,14 @@ static void i40e_vsi_free_irq(struct i40e_vsi *vsi) !vsi->q_vectors[i]->num_ringpairs) continue; +#ifdef HAVE_IRQ_AFFINITY_NOTIFY /* clear the affinity notifier in the IRQ descriptor */ irq_set_affinity_notifier(irq_num, NULL); +#endif +#ifdef HAVE_IRQ_AFFINITY_HINT /* remove our suggested affinity mask for this IRQ */ irq_set_affinity_hint(irq_num, NULL); +#endif synchronize_irq(irq_num); free_irq(irq_num, vsi->q_vectors[i]); @@ -4457,11 +5284,7 @@ static void i40e_clear_interrupt_scheme(struct i40e_pf *pf) { int i; - i40e_stop_misc_vector(pf); - if (pf->flags & I40E_FLAG_MSIX_ENABLED && pf->msix_entries) { - synchronize_irq(pf->msix_entries[0].vector); - free_irq(pf->msix_entries[0].vector, pf); - } + i40e_free_misc_vector(pf); i40e_put_lump(pf->irq_pile, pf->iwarp_base_vector, I40E_IWARP_IRQ_PILE_ID); @@ -4487,7 +5310,7 @@ static void i40e_napi_enable_all(struct i40e_vsi *vsi) for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++) { struct i40e_q_vector *q_vector = vsi->q_vectors[q_idx]; - if (q_vector->rx.ring || q_vector->tx.ring) + if (q_vector->tx.ring || q_vector->rx.ring) napi_enable(&q_vector->napi); } } @@ -4506,7 +5329,7 @@ static void i40e_napi_disable_all(struct i40e_vsi *vsi) for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++) { struct i40e_q_vector *q_vector = vsi->q_vectors[q_idx]; - if (q_vector->rx.ring || q_vector->tx.ring) + if (q_vector->tx.ring || q_vector->rx.ring) napi_disable(&q_vector->napi); } } @@ -4524,23 +5347,27 @@ static void i40e_vsi_close(struct i40e_vsi *vsi) i40e_vsi_free_tx_resources(vsi); i40e_vsi_free_rx_resources(vsi); vsi->current_netdev_flags = 0; - pf->flags |= I40E_FLAG_SERVICE_CLIENT_REQUESTED; + set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state); if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) - pf->flags |= I40E_FLAG_CLIENT_RESET; + set_bit(__I40E_CLIENT_RESET, pf->state); } /** * i40e_quiesce_vsi - Pause a given VSI * @vsi: the VSI being paused **/ -static void i40e_quiesce_vsi(struct i40e_vsi *vsi) +void i40e_quiesce_vsi(struct i40e_vsi *vsi) { if (test_bit(__I40E_VSI_DOWN, vsi->state)) return; set_bit(__I40E_VSI_NEEDS_RESTART, vsi->state); if (vsi->netdev && netif_running(vsi->netdev)) +#ifdef HAVE_NET_DEVICE_OPS vsi->netdev->netdev_ops->ndo_stop(vsi->netdev); +#else /* HAVE_NET_DEVICE_OPS */ + vsi->netdev->stop(vsi->netdev); +#endif /* HAVE_NET_DEVICE_OPS */ else i40e_vsi_close(vsi); } @@ -4549,13 +5376,17 @@ static void i40e_quiesce_vsi(struct i40e_vsi *vsi) * i40e_unquiesce_vsi - Resume a given VSI * @vsi: the VSI being resumed **/ -static void i40e_unquiesce_vsi(struct i40e_vsi *vsi) +void i40e_unquiesce_vsi(struct i40e_vsi *vsi) { if (!test_and_clear_bit(__I40E_VSI_NEEDS_RESTART, vsi->state)) return; if (vsi->netdev && netif_running(vsi->netdev)) +#ifdef HAVE_NET_DEVICE_OPS vsi->netdev->netdev_ops->ndo_open(vsi->netdev); +#else /* HAVE_NET_DEVICE_OPS */ + vsi->netdev->open(vsi->netdev); +#endif /* HAVE_NET_DEVICE_OPS */ else i40e_vsi_open(vsi); /* this clears the DOWN bit */ } @@ -4564,7 +5395,7 @@ static void i40e_unquiesce_vsi(struct i40e_vsi *vsi) * i40e_pf_quiesce_all_vsi - Pause all VSIs on a PF * @pf: the PF **/ -static void i40e_pf_quiesce_all_vsi(struct i40e_pf *pf) +void i40e_pf_quiesce_all_vsi(struct i40e_pf *pf) { int v; @@ -4578,7 +5409,7 @@ static void i40e_pf_quiesce_all_vsi(struct i40e_pf *pf) * i40e_pf_unquiesce_all_vsi - Resume all VSIs on a PF * @pf: the PF **/ -static void i40e_pf_unquiesce_all_vsi(struct i40e_pf *pf) +void i40e_pf_unquiesce_all_vsi(struct i40e_pf *pf) { int v; @@ -4593,7 +5424,7 @@ static void i40e_pf_unquiesce_all_vsi(struct i40e_pf *pf) * @vsi: the VSI being configured * * Wait until all queues on a given VSI have been disabled. - **/ +**/ int i40e_vsi_wait_queues_disabled(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; @@ -4616,6 +5447,7 @@ int i40e_vsi_wait_queues_disabled(struct i40e_vsi *vsi) /* Check and wait for the XDP Tx queue */ ret = i40e_pf_txq_wait(pf, pf_q + vsi->alloc_queue_pairs, false); + if (ret) { dev_info(&pf->pdev->dev, "VSI seid %d XDP Tx ring %d disable timeout\n", @@ -4636,7 +5468,7 @@ wait_rx: return 0; } -#ifdef CONFIG_I40E_DCB +#ifdef CONFIG_DCB /** * i40e_pf_wait_queues_disabled - Wait for all queues of PF VSIs to be disabled * @pf: the PF @@ -4658,106 +5490,7 @@ static int i40e_pf_wait_queues_disabled(struct i40e_pf *pf) return ret; } - -#endif - -/** - * i40e_detect_recover_hung_queue - Function to detect and recover hung_queue - * @q_idx: TX queue number - * @vsi: Pointer to VSI struct - * - * This function checks specified queue for given VSI. Detects hung condition. - * We proactively detect hung TX queues by checking if interrupts are disabled - * but there are pending descriptors. If it appears hung, attempt to recover - * by triggering a SW interrupt. - **/ -static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi) -{ - struct i40e_ring *tx_ring = NULL; - struct i40e_pf *pf; - u32 val, tx_pending; - int i; - - pf = vsi->back; - - /* now that we have an index, find the tx_ring struct */ - for (i = 0; i < vsi->num_queue_pairs; i++) { - if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc) { - if (q_idx == vsi->tx_rings[i]->queue_index) { - tx_ring = vsi->tx_rings[i]; - break; - } - } - } - - if (!tx_ring) - return; - - /* Read interrupt register */ - if (pf->flags & I40E_FLAG_MSIX_ENABLED) - val = rd32(&pf->hw, - I40E_PFINT_DYN_CTLN(tx_ring->q_vector->v_idx + - tx_ring->vsi->base_vector - 1)); - else - val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0); - - tx_pending = i40e_get_tx_pending(tx_ring); - - /* Interrupts are disabled and TX pending is non-zero, - * trigger the SW interrupt (don't wait). Worst case - * there will be one extra interrupt which may result - * into not cleaning any queues because queues are cleaned. - */ - if (tx_pending && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))) - i40e_force_wb(vsi, tx_ring->q_vector); -} - -/** - * i40e_detect_recover_hung - Function to detect and recover hung_queues - * @pf: pointer to PF struct - * - * LAN VSI has netdev and netdev has TX queues. This function is to check - * each of those TX queues if they are hung, trigger recovery by issuing - * SW interrupt. - **/ -static void i40e_detect_recover_hung(struct i40e_pf *pf) -{ - struct net_device *netdev; - struct i40e_vsi *vsi; - unsigned int i; - - /* Only for LAN VSI */ - vsi = pf->vsi[pf->lan_vsi]; - - if (!vsi) - return; - - /* Make sure, VSI state is not DOWN/RECOVERY_PENDING */ - if (test_bit(__I40E_VSI_DOWN, vsi->back->state) || - test_bit(__I40E_RESET_RECOVERY_PENDING, vsi->back->state)) - return; - - /* Make sure type is MAIN VSI */ - if (vsi->type != I40E_VSI_MAIN) - return; - - netdev = vsi->netdev; - if (!netdev) - return; - - /* Bail out if netif_carrier is not OK */ - if (!netif_carrier_ok(netdev)) - return; - - /* Go thru' TX queues for netdev */ - for (i = 0; i < netdev->num_tx_queues; i++) { - struct netdev_queue *q; - - q = netdev_get_tx_queue(netdev, i); - if (q) - i40e_detect_recover_hung_queue(i, vsi); - } -} +#endif /* I40E_DCB */ /** * i40e_get_iscsi_tc_map - Return TC map for iSCSI APP @@ -4849,20 +5582,45 @@ static u8 i40e_dcb_get_enabled_tc(struct i40e_dcbx_config *dcbcfg) return enabled_tc; } +#ifdef __TC_MQPRIO_MODE_MAX +/** + * i40e_mqprio_get_enabled_tc - Get enabled traffic classes + * @pf: PF being queried + * + * Query the current MQPRIO configuration and return the number of + * traffic classes enabled. + **/ +static u8 i40e_mqprio_get_enabled_tc(struct i40e_pf *pf) +{ + struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; + u8 num_tc = vsi->mqprio_qopt.qopt.num_tc; + u8 enabled_tc = 1, i; + + for (i = 1; i < num_tc; i++) + enabled_tc |= BIT(i); + return enabled_tc; +} +#endif + /** * i40e_pf_get_num_tc - Get enabled traffic classes for PF * @pf: PF being queried * * Return number of traffic classes enabled for the given PF **/ -static u8 i40e_pf_get_num_tc(struct i40e_pf *pf) +u8 i40e_pf_get_num_tc(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; - u8 i, enabled_tc = 1; + u8 i, enabled_tc; u8 num_tc = 0; struct i40e_dcbx_config *dcbcfg = &hw->local_dcbx_config; - /* If DCB is not enabled then always in single TC */ +#ifdef __TC_MQPRIO_MODE_MAX + if (pf->flags & I40E_FLAG_TC_MQPRIO) + return pf->vsi[pf->lan_vsi]->mqprio_qopt.qopt.num_tc; +#endif + + /* If neither MQPRIO nor DCB is enabled, then always use single TC */ if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) return 1; @@ -4874,7 +5632,7 @@ static u8 i40e_pf_get_num_tc(struct i40e_pf *pf) if (pf->hw.func_caps.iscsi) enabled_tc = i40e_get_iscsi_tc_map(pf); else - return 1; /* Only TC0 */ + return 1;/* Only TC0 */ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { if (enabled_tc & BIT(i)) @@ -4891,7 +5649,14 @@ static u8 i40e_pf_get_num_tc(struct i40e_pf *pf) **/ static u8 i40e_pf_get_tc_map(struct i40e_pf *pf) { - /* If DCB is not enabled for this PF then just return default TC */ +#ifdef __TC_MQPRIO_MODE_MAX + if (pf->flags & I40E_FLAG_TC_MQPRIO) + return i40e_mqprio_get_enabled_tc(pf); +#endif + + /* If neither MQPRIO nor DCB is enabled for this PF then just return + * default TC + */ if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) return I40E_DEFAULT_TRAFFIC_CLASS; @@ -4951,14 +5716,14 @@ static int i40e_vsi_get_bw_info(struct i40e_vsi *vsi) /* Still continuing */ } - vsi->bw_limit = le16_to_cpu(bw_config.port_bw_limit); + vsi->bw_limit = LE16_TO_CPU(bw_config.port_bw_limit); vsi->bw_max_quanta = bw_config.max_bw; - tc_bw_max = le16_to_cpu(bw_ets_config.tc_bw_max[0]) | - (le16_to_cpu(bw_ets_config.tc_bw_max[1]) << 16); + tc_bw_max = LE16_TO_CPU(bw_ets_config.tc_bw_max[0]) | + (LE16_TO_CPU(bw_ets_config.tc_bw_max[1]) << 16); for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { vsi->bw_ets_share_credits[i] = bw_ets_config.share_credits[i]; vsi->bw_ets_limit_credits[i] = - le16_to_cpu(bw_ets_config.credits[i]); + LE16_TO_CPU(bw_ets_config.credits[i]); /* 3 bits out of 4 for each TC */ vsi->bw_ets_max_quanta[i] = (u8)((tc_bw_max >> (i*4)) & 0x7); } @@ -4970,7 +5735,7 @@ static int i40e_vsi_get_bw_info(struct i40e_vsi *vsi) * i40e_vsi_configure_bw_alloc - Configure VSI BW allocation per TC * @vsi: the VSI being configured * @enabled_tc: TC bitmap - * @bw_credits: BW shared credits per TC + * @bw_share: BW shared credits per TC * * Returns 0 on success, negative value on failure **/ @@ -4978,19 +5743,32 @@ static int i40e_vsi_configure_bw_alloc(struct i40e_vsi *vsi, u8 enabled_tc, u8 *bw_share) { struct i40e_aqc_configure_vsi_tc_bw_data bw_data; + struct i40e_pf *pf = vsi->back; i40e_status ret; int i; +#ifdef __TC_MQPRIO_MODE_MAX + /* There is no need to reset BW when mqprio mode is on. */ + if (pf->flags & I40E_FLAG_TC_MQPRIO) + return 0; + if (!vsi->mqprio_qopt.qopt.hw && !(pf->flags & I40E_FLAG_DCB_ENABLED)) { + ret = i40e_set_bw_limit(vsi, vsi->seid, 0); + if (ret) + dev_info(&pf->pdev->dev, + "Failed to reset tx rate for vsi->seid %u\n", + vsi->seid); + return ret; + } +#endif bw_data.tc_valid_bits = enabled_tc; for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) bw_data.tc_bw_credits[i] = bw_share[i]; - ret = i40e_aq_config_vsi_tc_bw(&vsi->back->hw, vsi->seid, &bw_data, - NULL); + ret = i40e_aq_config_vsi_tc_bw(&pf->hw, vsi->seid, &bw_data, NULL); if (ret) { - dev_info(&vsi->back->pdev->dev, + dev_info(&pf->pdev->dev, "AQ command Config VSI BW allocation per TC failed = %d\n", - vsi->back->hw.aq.asq_last_status); + pf->hw.aq.asq_last_status); return -EINVAL; } @@ -5043,6 +5821,9 @@ static void i40e_vsi_config_netdev_tc(struct i40e_vsi *vsi, u8 enabled_tc) vsi->tc_config.tc_info[i].qoffset); } + if (pf->flags & I40E_FLAG_TC_MQPRIO) + return; + /* Assign UP2TC map for the VSI */ for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { /* Get the actual TC# for the UP */ @@ -5085,16 +5866,23 @@ static void i40e_vsi_update_queue_map(struct i40e_vsi *vsi, * It is expected that the VSI queues have been quisced before calling * this function. **/ -static int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc) +int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc) { u8 bw_share[I40E_MAX_TRAFFIC_CLASS] = {0}; + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; struct i40e_vsi_context ctxt; int ret = 0; int i; - +#ifdef __TC_MQPRIO_MODE_MAX /* Check if enabled_tc is same as existing or new TCs */ + if (vsi->tc_config.enabled_tc == enabled_tc && + vsi->mqprio_qopt.mode != TC_MQPRIO_MODE_CHANNEL) + return ret; +#else if (vsi->tc_config.enabled_tc == enabled_tc) return ret; +#endif /* Enable ETS TCs with equal BW Share for now across all VSIs */ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { @@ -5104,19 +5892,74 @@ static int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc) ret = i40e_vsi_configure_bw_alloc(vsi, enabled_tc, bw_share); if (ret) { - dev_info(&vsi->back->pdev->dev, + struct i40e_aqc_query_vsi_bw_config_resp bw_config = {0}; + + dev_info(&pf->pdev->dev, "Failed configuring TC map %d for VSI %d\n", enabled_tc, vsi->seid); - goto out; - } + ret = i40e_aq_query_vsi_bw_config(hw, vsi->seid, + &bw_config, NULL); + if (ret) { + dev_info(&pf->pdev->dev, + "Failed querying vsi bw info, err %s aq_err %s\n", + i40e_stat_str(hw, ret), + i40e_aq_str(hw, hw->aq.asq_last_status)); + goto out; + } + if ((bw_config.tc_valid_bits & enabled_tc) != enabled_tc) { + u8 valid_tc = bw_config.tc_valid_bits & enabled_tc; - /* Update Queue Pairs Mapping for currently enabled UPs */ + if (!valid_tc) + valid_tc = bw_config.tc_valid_bits; + /* Always enable TC0, no matter what */ + valid_tc |= 1; + dev_info(&pf->pdev->dev, + "Requested tc 0x%x, but FW reports 0x%x as valid. Attempting to use 0x%x.\n", + enabled_tc, bw_config.tc_valid_bits, valid_tc); + enabled_tc = valid_tc; + } + + ret = i40e_vsi_configure_bw_alloc(vsi, enabled_tc, bw_share); + if (ret) { + dev_err(&pf->pdev->dev, + "Unable to configure TC map %d for VSI %d\n", + enabled_tc, vsi->seid); + goto out; + } + } + + /* Update Queue Pairs Mapping for currently enabled UPs */ ctxt.seid = vsi->seid; ctxt.pf_num = vsi->back->hw.pf_id; ctxt.vf_num = 0; ctxt.uplink_seid = vsi->uplink_seid; ctxt.info = vsi->info; + +#ifdef __TC_MQPRIO_MODE_MAX + if (vsi->back->flags & I40E_FLAG_TC_MQPRIO) { + ret = i40e_vsi_setup_queue_map_mqprio(vsi, &ctxt, enabled_tc); + if (ret) + goto out; + } else { + i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, false); + } + /* On destroying the qdisc, reset vsi->rss_size, as number of enabled + * queues changed. + */ + if (!vsi->mqprio_qopt.qopt.hw && vsi->reconfig_rss) { + vsi->rss_size = min_t(int, vsi->back->alloc_rss_size, + vsi->num_queue_pairs); + ret = i40e_vsi_config_rss(vsi); + if (ret) { + dev_info(&vsi->back->pdev->dev, + "Failed to reconfig rss for num_queues\n"); + return ret; + } + vsi->reconfig_rss = false; + } +#else i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, false); +#endif if (vsi->back->flags & I40E_FLAG_IWARP_ENABLED) { ctxt.info.valid_sections |= @@ -5124,14 +5967,15 @@ static int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc) ctxt.info.queueing_opt_flags |= I40E_AQ_VSI_QUE_OPT_TCP_ENA; } - /* Update the VSI after updating the VSI queue-mapping information */ - ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL); + /* Update the VSI after updating the VSI queue-mapping + * information + */ + ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL); if (ret) { - dev_info(&vsi->back->pdev->dev, + dev_info(&pf->pdev->dev, "Update vsi tc config failed, err %s aq_err %s\n", - i40e_stat_str(&vsi->back->hw, ret), - i40e_aq_str(&vsi->back->hw, - vsi->back->hw.aq.asq_last_status)); + i40e_stat_str(hw, ret), + i40e_aq_str(hw, hw->aq.asq_last_status)); goto out; } /* update the local VSI info with updated queue map */ @@ -5141,11 +5985,10 @@ static int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc) /* Update current VSI BW information */ ret = i40e_vsi_get_bw_info(vsi); if (ret) { - dev_info(&vsi->back->pdev->dev, + dev_info(&pf->pdev->dev, "Failed updating vsi bw info, err %s aq_err %s\n", - i40e_stat_str(&vsi->back->hw, ret), - i40e_aq_str(&vsi->back->hw, - vsi->back->hw.aq.asq_last_status)); + i40e_stat_str(hw, ret), + i40e_aq_str(hw, hw->aq.asq_last_status)); goto out; } @@ -5205,7 +6048,7 @@ out: return ret; } -#ifdef CONFIG_I40E_DCB +#ifdef CONFIG_DCB /** * i40e_dcb_reconfigure - Reconfigure all VEBs and VSIs * @pf: PF struct @@ -5256,8 +6099,10 @@ static void i40e_dcb_reconfigure(struct i40e_pf *pf) } else { /* Re-configure VSI vectors based on updated TC map */ i40e_vsi_map_rings_to_vectors(pf->vsi[v]); +#ifdef HAVE_DCBNL_IEEE if (pf->vsi[v]->netdev) i40e_dcbnl_set_all(pf->vsi[v]); +#endif /* HAVE_DCBNL_IEEE */ } } } @@ -5300,12 +6145,17 @@ static int i40e_init_pf_dcb(struct i40e_pf *pf) struct i40e_hw *hw = &pf->hw; int err = 0; - /* Do not enable DCB for SW1 and SW2 images even if the FW is capable */ - if (pf->hw_features & I40E_HW_NO_DCB_SUPPORT) + /* Do not enable DCB for SW1 and SW2 images even if the FW is capable + * Also do not enable DCBx if FW LLDP agent is disabled + */ + if ((pf->hw_features & I40E_HW_NO_DCB_SUPPORT) || + (pf->flags & I40E_FLAG_DISABLE_FW_LLDP)) { + dev_info(&pf->pdev->dev, "DCB is not supported or FW LLDP is disabled\n"); + err = I40E_NOT_SUPPORTED; goto out; + } - /* Get the initial DCB configuration */ - err = i40e_init_dcb(hw); + err = i40e_init_dcb(hw, true); if (!err) { /* Device/Function is not DCBX capable */ if ((!hw->func_caps.dcb) || @@ -5319,7 +6169,7 @@ static int i40e_init_pf_dcb(struct i40e_pf *pf) pf->flags |= I40E_FLAG_DCB_CAPABLE; /* Enable DCB tagging only when more than one TC - * or explicitly disable if only one TC + * or explicity disable if only one TC */ if (i40e_dcb_get_num_tc(&hw->local_dcbx_config) > 1) pf->flags |= I40E_FLAG_DCB_ENABLED; @@ -5328,6 +6178,9 @@ static int i40e_init_pf_dcb(struct i40e_pf *pf) dev_dbg(&pf->pdev->dev, "DCBX offload is supported for this PF.\n"); } + } else if (pf->hw.aq.asq_last_status == I40E_AQ_RC_EPERM) { + dev_info(&pf->pdev->dev, "FW LLDP disabled for this PF.\n"); + pf->flags |= I40E_FLAG_DISABLE_FW_LLDP; } else { dev_info(&pf->pdev->dev, "Query for DCB configuration failed, err %s aq_err %s\n", @@ -5338,28 +6191,36 @@ static int i40e_init_pf_dcb(struct i40e_pf *pf) out: return err; } -#endif /* CONFIG_I40E_DCB */ + +#endif /* CONFIG_DCB */ #define SPEED_SIZE 14 #define FC_SIZE 8 /** * i40e_print_link_message - print link up or down * @vsi: the VSI for which link needs a message + * @isup: true of link is up, false otherwise */ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) { enum i40e_aq_link_speed new_speed; - char *speed = "Unknown"; + struct i40e_pf *pf = vsi->back; + char *speed = "Unknown "; char *fc = "Unknown"; char *fec = ""; char *req_fec = ""; char *an = ""; - new_speed = vsi->back->hw.phy.link_info.link_speed; + if (isup) + new_speed = pf->hw.phy.link_info.link_speed; + else + new_speed = I40E_LINK_SPEED_UNKNOWN; if ((vsi->current_isup == isup) && (vsi->current_speed == new_speed)) return; + vsi->current_isup = isup; vsi->current_speed = new_speed; + if (!isup) { netdev_info(vsi->netdev, "NIC Link is Down\n"); return; @@ -5368,13 +6229,13 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) /* Warn user if link speed on NPAR enabled partition is not at * least 10GB */ - if (vsi->back->hw.func_caps.npar_enable && - (vsi->back->hw.phy.link_info.link_speed == I40E_LINK_SPEED_1GB || - vsi->back->hw.phy.link_info.link_speed == I40E_LINK_SPEED_100MB)) + if (pf->hw.func_caps.npar_enable && + (pf->hw.phy.link_info.link_speed == I40E_LINK_SPEED_1GB || + pf->hw.phy.link_info.link_speed == I40E_LINK_SPEED_100MB)) netdev_warn(vsi->netdev, "The partition detected link speed that is less than 10Gbps\n"); - switch (vsi->back->hw.phy.link_info.link_speed) { + switch (pf->hw.phy.link_info.link_speed) { case I40E_LINK_SPEED_40GB: speed = "40 G"; break; @@ -5387,6 +6248,12 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) case I40E_LINK_SPEED_10GB: speed = "10 G"; break; + case I40E_LINK_SPEED_5GB: + speed = "5 G"; + break; + case I40E_LINK_SPEED_2_5GB: + speed = "2.5 G"; + break; case I40E_LINK_SPEED_1GB: speed = "1000 M"; break; @@ -5397,7 +6264,7 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) break; } - switch (vsi->back->hw.fc.current_mode) { + switch (pf->hw.fc.current_mode) { case I40E_FC_FULL: fc = "RX/TX"; break; @@ -5412,36 +6279,60 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) break; } - if (vsi->back->hw.phy.link_info.link_speed == I40E_LINK_SPEED_25GB) { - req_fec = ", Requested FEC: None"; - fec = ", FEC: None"; - an = ", Autoneg: False"; + if (pf->hw.phy.link_info.link_speed == I40E_LINK_SPEED_25GB) { + req_fec = "None"; + fec = "None"; + an = "False"; - if (vsi->back->hw.phy.link_info.an_info & I40E_AQ_AN_COMPLETED) - an = ", Autoneg: True"; + if (pf->hw.phy.link_info.an_info & I40E_AQ_AN_COMPLETED) + an = "True"; - if (vsi->back->hw.phy.link_info.fec_info & + if (pf->hw.phy.link_info.fec_info & I40E_AQ_CONFIG_FEC_KR_ENA) - fec = ", FEC: CL74 FC-FEC/BASE-R"; - else if (vsi->back->hw.phy.link_info.fec_info & + fec = "CL74 FC-FEC/BASE-R"; + else if (pf->hw.phy.link_info.fec_info & I40E_AQ_CONFIG_FEC_RS_ENA) - fec = ", FEC: CL108 RS-FEC"; + fec = "CL108 RS-FEC"; /* 'CL108 RS-FEC' should be displayed when RS is requested, or * both RS and FC are requested */ - if (vsi->back->hw.phy.link_info.req_fec_info & + if (pf->hw.phy.link_info.req_fec_info & (I40E_AQ_REQUEST_FEC_KR | I40E_AQ_REQUEST_FEC_RS)) { - if (vsi->back->hw.phy.link_info.req_fec_info & + if (pf->hw.phy.link_info.req_fec_info & I40E_AQ_REQUEST_FEC_RS) - req_fec = ", Requested FEC: CL108 RS-FEC"; + req_fec = "CL108 RS-FEC"; else - req_fec = ", Requested FEC: CL74 FC-FEC/BASE-R"; + req_fec = "CL74 FC-FEC/BASE-R"; } + netdev_info(vsi->netdev, + "NIC Link is Up, %sbps Full Duplex, Requested FEC: %s, Negotiated FEC: %s, Autoneg: %s, Flow Control: %s\n", + speed, req_fec, fec, an, fc); + } else { + struct ethtool_eee edata; + + edata.supported = 0; + edata.eee_enabled = false; +#ifdef HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT + if (get_ethtool_ops_ext(vsi->netdev)->get_eee) + get_ethtool_ops_ext(vsi->netdev) + ->get_eee(vsi->netdev, &edata); +#else + if (vsi->netdev->ethtool_ops->get_eee) + vsi->netdev->ethtool_ops->get_eee(vsi->netdev, &edata); +#endif /* HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT */ + + if (edata.supported) + netdev_info(vsi->netdev, + "NIC Link is Up, %sbps Full Duplex, Flow Control: %s, EEE: %s\n", + speed, fc, + edata.eee_enabled ? "Enabled" : "Disabled"); + else + netdev_info(vsi->netdev, + "NIC Link is Up, %sbps Full Duplex, Flow Control: %s\n", + speed, fc); } - netdev_info(vsi->netdev, "NIC Link is Up, %sbps Full Duplex%s%s%s, Flow Control: %s\n", - speed, req_fec, fec, an, fc); } /** @@ -5472,29 +6363,20 @@ static int i40e_up_complete(struct i40e_vsi *vsi) i40e_print_link_message(vsi, true); netif_tx_start_all_queues(vsi->netdev); netif_carrier_on(vsi->netdev); - } else if (vsi->netdev) { - i40e_print_link_message(vsi, false); - /* need to check for qualified module here*/ - if ((pf->hw.phy.link_info.link_info & - I40E_AQ_MEDIA_AVAILABLE) && - (!(pf->hw.phy.link_info.an_info & - I40E_AQ_QUALIFIED_MODULE))) - netdev_err(vsi->netdev, - "the driver failed to link because an unqualified module was detected."); - } - - /* replay FDIR SB filters */ + } + + /* replay flow filters */ if (vsi->type == I40E_VSI_FDIR) { /* reset fd counters */ - pf->fd_add_err = 0; - pf->fd_atr_cnt = 0; + pf->fd_add_err = pf->fd_atr_cnt = 0; i40e_fdir_filter_restore(vsi); + i40e_cloud_filter_restore(pf); } /* On the next run of the service_task, notify any clients of the new * opened netdev */ - pf->flags |= I40E_FLAG_SERVICE_CLIENT_REQUESTED; + set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state); i40e_service_event_schedule(pf); return 0; @@ -5515,7 +6397,6 @@ static void i40e_vsi_reinit_locked(struct i40e_vsi *vsi) while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) usleep_range(1000, 2000); i40e_down(vsi); - i40e_up(vsi); clear_bit(__I40E_CONFIG_BUSY, pf->state); } @@ -5528,6 +6409,11 @@ int i40e_up(struct i40e_vsi *vsi) { int err; + if ((vsi->type == I40E_VSI_MAIN && + vsi->back->flags & I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED) || + (vsi->back->flags & I40E_FLAG_TOTAL_PORT_SHUTDOWN)) + i40e_force_link_state(vsi->back, true); + err = i40e_vsi_configure(vsi); if (!err) err = i40e_up_complete(vsi); @@ -5535,6 +6421,111 @@ int i40e_up(struct i40e_vsi *vsi) return err; } +/** + * i40e_force_link_state - Force the link status + * @pf: board private structure + * @is_up: whether the link state should be forced up or down + **/ +static i40e_status i40e_force_link_state(struct i40e_pf *pf, bool is_up) +{ + struct i40e_aq_get_phy_abilities_resp abilities; + struct i40e_aq_set_phy_config config = {0}; + struct i40e_hw *hw = &pf->hw; + bool non_zero_phy_type; + i40e_status err; + u64 mask; + u8 speed; + + non_zero_phy_type = is_up; + /* Card might've been put in an unstable state by other drivers + * and applications, which causes incorrect speed values being + * set on startup. In order to clear speed registers, we call + * get_phy_capabilities twice, once to get initial state of + * available speeds, and once to get current phy config. + */ + err = i40e_aq_get_phy_capabilities(hw, false, true, &abilities, + NULL); + if (err) { + dev_err(&pf->pdev->dev, + "failed to get phy cap., ret = %s last_status = %s\n", + i40e_stat_str(hw, err), + i40e_aq_str(hw, hw->aq.asq_last_status)); + return err; + } + speed = abilities.link_speed; + + /* Get the current phy config */ + err = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, + NULL); + if (err) { + dev_err(&pf->pdev->dev, + "failed to get phy cap., ret = %s last_status = %s\n", + i40e_stat_str(hw, err), + i40e_aq_str(hw, hw->aq.asq_last_status)); + return err; + } + + /* If link needs to go up, but was not forced to go down, + * and its speed values are OK, no need for a flap + * if non_zero_phy_type was set, still need to force up + */ + if (pf->flags & I40E_FLAG_TOTAL_PORT_SHUTDOWN) + non_zero_phy_type = true; + else if (is_up && abilities.phy_type != 0 && abilities.link_speed != 0) + return I40E_SUCCESS; + + /* To force link we need to set bits for all supported PHY types, + * but there are now more than 32, so we need to split the bitmap + * across two fields. + */ + mask = I40E_PHY_TYPES_BITMASK; + config.phy_type = + non_zero_phy_type ? cpu_to_le32((u32)(mask & 0xffffffff)) : 0; + config.phy_type_ext = + non_zero_phy_type ? (u8)((mask >> 32) & 0xff) : 0; + /* Copy the old settings, except of phy_type */ + config.abilities = abilities.abilities; + if (pf->flags & I40E_FLAG_TOTAL_PORT_SHUTDOWN) { + if (is_up) + config.abilities |= I40E_AQ_PHY_ENABLE_LINK; + else + config.abilities &= ~(I40E_AQ_PHY_ENABLE_LINK); + } + if (abilities.link_speed != 0) + config.link_speed = abilities.link_speed; + else + config.link_speed = speed; + config.eee_capability = abilities.eee_capability; + config.eeer = abilities.eeer_val; + config.low_power_ctrl = abilities.d3_lpan; + config.fec_config = abilities.fec_cfg_curr_mod_ext_info & + I40E_AQ_PHY_FEC_CONFIG_MASK; + err = i40e_aq_set_phy_config(hw, &config, NULL); + + if (err) { + dev_err(&pf->pdev->dev, + "set phy config ret = %s last_status = %s\n", + i40e_stat_str(&pf->hw, err), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + return err; + } + + /* Update the link info */ + err = i40e_update_link_info(hw); + if (err) { + /* Wait a little bit (on 40G cards it sometimes takes a really + * long time for link to come back from the atomic reset) + * and try once more + */ + msleep(1000); + i40e_update_link_info(hw); + } + + i40e_aq_set_link_restart_an(hw, is_up, NULL); + + return I40E_SUCCESS; +} + /** * i40e_down - Shutdown the connection processing * @vsi: the VSI being stopped @@ -5552,6 +6543,10 @@ void i40e_down(struct i40e_vsi *vsi) } i40e_vsi_disable_irq(vsi); i40e_vsi_stop_rings(vsi); + if ((vsi->type == I40E_VSI_MAIN && + vsi->back->flags & I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED) || + (vsi->back->flags & I40E_FLAG_TOTAL_PORT_SHUTDOWN)) + i40e_force_link_state(vsi->back, false); i40e_napi_disable_all(vsi); for (i = 0; i < vsi->num_queue_pairs; i++) { @@ -5564,3795 +6559,6555 @@ void i40e_down(struct i40e_vsi *vsi) } /** - * i40e_setup_tc - configure multiple traffic classes - * @netdev: net device to configure - * @tc: number of traffic classes to enable + * i40e_get_link_speed - Returns link speed for the interface + * @vsi: VSI to be configured + * **/ -static int i40e_setup_tc(struct net_device *netdev, u8 tc) +int i40e_get_link_speed(struct i40e_vsi *vsi) { - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - u8 enabled_tc = 0; - int ret = -EINVAL; - int i; - /* Check if DCB enabled to continue */ - if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) { - netdev_info(netdev, "DCB is not enabled for adapter\n"); - goto exit; + switch (pf->hw.phy.link_info.link_speed) { + case I40E_LINK_SPEED_40GB: + return 40000; + case I40E_LINK_SPEED_25GB: + return 25000; + case I40E_LINK_SPEED_20GB: + return 20000; + case I40E_LINK_SPEED_10GB: + return 10000; + case I40E_LINK_SPEED_1GB: + return 1000; + default: + return -EINVAL; } +} - /* Check if MFP enabled */ - if (pf->flags & I40E_FLAG_MFP_ENABLED) { - netdev_info(netdev, "Configuring TC not supported in MFP mode\n"); - goto exit; - } +/** + * i40e_set_bw_limit - setup BW limit for Tx traffic based on max_tx_rate + * @vsi: VSI to be configured + * @seid: seid of the channel/VSI + * @max_tx_rate: max TX rate to be configured as BW limit + * + * Helper function to set BW limit for a given VSI + **/ +int i40e_set_bw_limit(struct i40e_vsi *vsi, u16 seid, u64 max_tx_rate) +{ + struct i40e_pf *pf = vsi->back; + int speed = 0; + int ret = 0; - /* Check whether tc count is within enabled limit */ - if (tc > i40e_pf_get_num_tc(pf)) { - netdev_info(netdev, "TC count greater than enabled on link for adapter\n"); - goto exit; + speed = i40e_get_link_speed(vsi); + if (max_tx_rate > speed) { + dev_err(&pf->pdev->dev, + "Invalid max tx rate %llu specified for VSI seid %d.", + max_tx_rate, seid); + return -EINVAL; + } + if (max_tx_rate && max_tx_rate < 50) { + dev_warn(&pf->pdev->dev, + "Setting max tx rate to minimum usable value of 50Mbps.\n"); + max_tx_rate = 50; } - /* Generate TC map for number of tc requested */ - for (i = 0; i < tc; i++) - enabled_tc |= BIT(i); + /* Tx rate credits are in values of 50Mbps, 0 is disabled */ + ret = i40e_aq_config_vsi_bw_limit(&pf->hw, seid, + max_tx_rate / I40E_BW_CREDIT_DIVISOR, + I40E_MAX_BW_INACTIVE_ACCUM, NULL); + if (ret) + dev_err(&pf->pdev->dev, + "Failed set tx rate (%llu Mbps) for vsi->seid %u, err %s aq_err %s\n", + max_tx_rate, seid, i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + return ret; +} - /* Requesting same TC configuration as already enabled */ - if (enabled_tc == vsi->tc_config.enabled_tc) - return 0; +#ifdef __TC_MQPRIO_MODE_MAX +/** + * i40e_validate_and_set_switch_mode - sets up switch mode correctly + * @vsi: ptr to VSI which has PF backing + * + * Sets up switch mode correctly if it needs to be changed and perform + * what are allowed modes. + **/ +static int i40e_validate_and_set_switch_mode(struct i40e_vsi *vsi) +{ + u8 mode; + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; + int ret; - /* Quiesce VSI queues */ - i40e_quiesce_vsi(vsi); + ret = i40e_get_capabilities(pf, i40e_aqc_opc_list_dev_capabilities); + if (ret) + return -EINVAL; - /* Configure VSI for enabled TCs */ - ret = i40e_vsi_config_tc(vsi, enabled_tc); - if (ret) { - netdev_info(netdev, "Failed configuring TC for VSI seid=%d\n", - vsi->seid); - goto exit; + if (hw->dev_caps.switch_mode) { + /* if switch mode is set, support mode2 (non-tunneled for + * cloud filter) for now + */ + u32 switch_mode = hw->dev_caps.switch_mode & + I40E_SWITCH_MODE_MASK; + if (switch_mode >= I40E_CLOUD_FILTER_MODE1) { + if (switch_mode == I40E_CLOUD_FILTER_MODE2) + return 0; + dev_err(&pf->pdev->dev, + "Invalid switch_mode (%d), only non-tunneled mode for cloud filter is supported\n", + hw->dev_caps.switch_mode); + return -EINVAL; + } } - /* Unquiesce VSI */ - i40e_unquiesce_vsi(vsi); - -exit: - return ret; -} + /* Set Bit 7 to be valid */ + mode = I40E_AQ_SET_SWITCH_BIT7_VALID; -static int __i40e_setup_tc(struct net_device *netdev, enum tc_setup_type type, - void *type_data) -{ - struct tc_mqprio_qopt *mqprio = type_data; + /* Set L4type for TCP support */ + mode |= I40E_AQ_SET_SWITCH_L4_TYPE_TCP; - if (type != TC_SETUP_MQPRIO) - return -EOPNOTSUPP; + /* Set cloud filter mode */ + mode |= I40E_AQ_SET_SWITCH_MODE_NON_TUNNEL; - mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + /* Prep mode field for set_switch_config */ + ret = i40e_aq_set_switch_config(hw, pf->last_sw_conf_flags, + pf->last_sw_conf_valid_flags, + mode, NULL); + if (ret && hw->aq.asq_last_status != I40E_AQ_RC_ESRCH) + dev_err(&pf->pdev->dev, + "couldn't set switch config bits, err %s aq_err %s\n", + i40e_stat_str(hw, ret), + i40e_aq_str(hw, + hw->aq.asq_last_status)); - return i40e_setup_tc(netdev, mqprio->num_tc); + return ret; } /** - * i40e_open - Called when a network interface is made active - * @netdev: network interface device structure - * - * The open entry point is called when a network interface is made - * active by the system (IFF_UP). At this point all resources needed - * for transmit and receive operations are allocated, the interrupt - * handler is registered with the OS, the netdev watchdog subtask is - * enabled, and the stack is notified that the interface is ready. + * i40e_add_del_cloud_filter_big_buf - Add/del cloud filter using big_buf + * @vsi: pointer to VSI + * @filter: cloud filter rule + * @add: if true, add, if false, delete * - * Returns 0 on success, negative value on failure + * Add or delete a cloud filter for a specific flow spec using big buffer. + * Returns 0 if the filter were successfully added. **/ -int i40e_open(struct net_device *netdev) +int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi, + struct i40e_cloud_filter *filter, + bool add) { - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_vsi *vsi = np->vsi; + struct i40e_aqc_cloud_filters_element_bb cld_filter; struct i40e_pf *pf = vsi->back; - int err; + int ret; - /* disallow open during test or if eeprom is broken */ - if (test_bit(__I40E_TESTING, pf->state) || - test_bit(__I40E_BAD_EEPROM, pf->state)) - return -EBUSY; + if (i40e_is_l4mode_enabled()) { + dev_err(&pf->pdev->dev, + "Not expected to run in L4 cloud filter mode\n"); + return -EINVAL; + } - netif_carrier_off(netdev); + /* Both (src/dst) valid mac_addr are not supported */ + if ((is_valid_ether_addr(filter->dst_mac) && + is_valid_ether_addr(filter->src_mac)) || + (is_multicast_ether_addr(filter->dst_mac) && + is_multicast_ether_addr(filter->src_mac))) + return -EOPNOTSUPP; - err = i40e_vsi_open(vsi); - if (err) - return err; + /* Big buffer cloud filter needs 'L4 port' to be non-zero. Also, UDP + * ports are not supported via big buffer now. + */ + if (!filter->dst_port || filter->ip_proto == IPPROTO_UDP) + return -EOPNOTSUPP; - /* configure global TSO hardware offload settings */ - wr32(&pf->hw, I40E_GLLAN_TSOMSK_F, be32_to_cpu(TCP_FLAG_PSH | - TCP_FLAG_FIN) >> 16); - wr32(&pf->hw, I40E_GLLAN_TSOMSK_M, be32_to_cpu(TCP_FLAG_PSH | - TCP_FLAG_FIN | - TCP_FLAG_CWR) >> 16); - wr32(&pf->hw, I40E_GLLAN_TSOMSK_L, be32_to_cpu(TCP_FLAG_CWR) >> 16); + /* adding filter using src_port/src_ip is not supported at this stage */ + if (filter->src_port || filter->src_ipv4 || + !ipv6_addr_any(&filter->ip.v6.src_ip6)) + return -EOPNOTSUPP; - udp_tunnel_get_rx_info(netdev); + /* copy element needed to add cloud filter from filter */ + i40e_set_cld_element(filter, &cld_filter.element); - return 0; + if (is_valid_ether_addr(filter->dst_mac) || + is_valid_ether_addr(filter->src_mac) || + is_multicast_ether_addr(filter->dst_mac) || + is_multicast_ether_addr(filter->src_mac)) { + /* MAC + IP : unsupported mode */ + if (filter->dst_ipv4) + return -EOPNOTSUPP; + + /* since we validated that L4 port must be valid before + * we get here, start with respective "flags" value + * and update if vlan is present or not + */ + cld_filter.element.flags = + cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT); + + if (filter->vlan_id) { + cld_filter.element.flags = + cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT); + } + + } else if (filter->dst_ipv4 || + !ipv6_addr_any(&filter->ip.v6.dst_ip6)) { + cld_filter.element.flags = + cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_IP_PORT); + if (filter->n_proto == ETH_P_IPV6) + cld_filter.element.flags |= + cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV6); + else + cld_filter.element.flags |= + cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV4); + } else { + dev_err(&pf->pdev->dev, + "either mac or ip has to be valid for cloud filter\n"); + return -EINVAL; + } + + /* Now copy L4 port in Byte 6..7 in general fields */ + cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0] = + be16_to_cpu(filter->dst_port); + + if (add) { + /* Validate current device switch mode, change if necessary */ + ret = i40e_validate_and_set_switch_mode(vsi); + if (ret) { + dev_err(&pf->pdev->dev, + "failed to set switch mode, ret %d\n", + ret); + return ret; + } + + ret = i40e_aq_add_cloud_filters_bb(&pf->hw, filter->seid, + &cld_filter, 1); + } else { + ret = i40e_aq_rem_cloud_filters_bb(&pf->hw, filter->seid, + &cld_filter, 1); + } + + if (ret) + dev_dbg(&pf->pdev->dev, + "Failed to %s cloud filter(big buffer) err %d aq_err %d\n", + add ? "add" : "delete", ret, pf->hw.aq.asq_last_status); + else + dev_info(&pf->pdev->dev, + "%s cloud filter for VSI: %d, L4 port: %d\n", + add ? "add" : "delete", filter->seid, + ntohs(filter->dst_port)); + return ret; } /** - * i40e_vsi_open - - * @vsi: the VSI to open - * - * Finish initialization of the VSI. - * - * Returns 0 on success, negative value on failure + * i40e_remove_queue_channels - Remove queue channels for the TCs + * @vsi: VSI to be configured * - * Note: expects to be called while under rtnl_lock() + * Remove queue channels for the TCs **/ -int i40e_vsi_open(struct i40e_vsi *vsi) +static void i40e_remove_queue_channels(struct i40e_vsi *vsi) { + enum i40e_admin_queue_err last_aq_status; + struct i40e_cloud_filter *cfilter; + struct i40e_channel *ch, *ch_tmp; struct i40e_pf *pf = vsi->back; - char int_name[I40E_INT_NAME_STR_LEN]; - int err; + struct hlist_node *node; + int ret, i; - /* allocate descriptors */ - err = i40e_vsi_setup_tx_resources(vsi); - if (err) - goto err_setup_tx; - err = i40e_vsi_setup_rx_resources(vsi); - if (err) - goto err_setup_rx; + /* Reset rss size that was stored when reconfiguring rss for + * channel VSIs with non-power-of-2 queue count. + */ + vsi->current_rss_size = 0; - err = i40e_vsi_configure(vsi); - if (err) - goto err_setup_rx; + /* perform cleanup for channels if they exist */ + if (list_empty(&vsi->ch_list)) + return; - if (vsi->netdev) { - snprintf(int_name, sizeof(int_name) - 1, "%s-%s", - dev_driver_string(&pf->pdev->dev), vsi->netdev->name); - err = i40e_vsi_request_irq(vsi, int_name); - if (err) - goto err_setup_rx; + list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list, list) { + struct i40e_vsi *p_vsi; - /* Notify the stack of the actual queue counts. */ - err = netif_set_real_num_tx_queues(vsi->netdev, - vsi->num_queue_pairs); - if (err) - goto err_set_queues; + list_del(&ch->list); + p_vsi = ch->parent_vsi; + if (!p_vsi || !ch->initialized) { + kfree(ch); + continue; + } + /* Reset queue contexts */ + for (i = 0; i < ch->num_queue_pairs; i++) { + struct i40e_ring *tx_ring, *rx_ring; + u16 pf_q; - err = netif_set_real_num_rx_queues(vsi->netdev, - vsi->num_queue_pairs); - if (err) - goto err_set_queues; + pf_q = ch->base_queue + i; + tx_ring = vsi->tx_rings[pf_q]; + tx_ring->ch = NULL; - } else if (vsi->type == I40E_VSI_FDIR) { - snprintf(int_name, sizeof(int_name) - 1, "%s-%s:fdir", - dev_driver_string(&pf->pdev->dev), - dev_name(&pf->pdev->dev)); - err = i40e_vsi_request_irq(vsi, int_name); + rx_ring = vsi->rx_rings[pf_q]; + rx_ring->ch = NULL; + } - } else { - err = -EINVAL; - goto err_setup_rx; - } + /* Reset BW configured for this VSI via mqprio */ + ret = i40e_set_bw_limit(vsi, ch->seid, 0); + if (ret) + dev_info(&vsi->back->pdev->dev, + "Failed to reset tx rate for ch->seid %u\n", + ch->seid); - err = i40e_up_complete(vsi); - if (err) - goto err_up_complete; + /* delete cloud filters associated with this channel */ + hlist_for_each_entry_safe(cfilter, node, + &pf->cloud_filter_list, cloud_node) { + if (cfilter->seid != ch->seid) + continue; - return 0; + hash_del(&cfilter->cloud_node); + if (cfilter->dst_port) + ret = i40e_add_del_cloud_filter_big_buf(vsi, + cfilter, + false); + else + ret = i40e_add_del_cloud_filter(vsi, cfilter, + false); + last_aq_status = pf->hw.aq.asq_last_status; + if (ret) + dev_info(&pf->pdev->dev, + "Failed to delete cloud filter, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, last_aq_status)); + kfree(cfilter); + } -err_up_complete: - i40e_down(vsi); -err_set_queues: - i40e_vsi_free_irq(vsi); -err_setup_rx: - i40e_vsi_free_rx_resources(vsi); -err_setup_tx: - i40e_vsi_free_tx_resources(vsi); - if (vsi == pf->vsi[pf->lan_vsi]) - i40e_do_reset(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED), true); + /* delete cloud filters associated with this channel */ + hlist_for_each_entry_safe(cfilter, node, + &pf->cloud_filter_list, cloud_node) { + if (cfilter->seid != ch->seid) + continue; - return err; + hash_del(&cfilter->cloud_node); + if (cfilter->dst_port) + ret = i40e_add_del_cloud_filter_big_buf(vsi, + cfilter, + false); + else + ret = i40e_add_del_cloud_filter(vsi, cfilter, + false); + last_aq_status = pf->hw.aq.asq_last_status; + if (ret) + dev_info(&pf->pdev->dev, + "Failed to delete cloud filter, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, last_aq_status)); + kfree(cfilter); + } + + /* delete VSI from FW */ + ret = i40e_aq_delete_element(&vsi->back->hw, ch->seid, + NULL); + if (ret) + dev_err(&vsi->back->pdev->dev, + "unable to remove channel (%d) for parent VSI(%d)\n", + ch->seid, p_vsi->seid); + kfree(ch); + } + INIT_LIST_HEAD(&vsi->ch_list); } /** - * i40e_fdir_filter_exit - Cleans up the Flow Director accounting - * @pf: Pointer to PF + * i40e_get_max_queues_for_channel + * @vsi: ptr to VSI to which channels are associated with * - * This function destroys the hlist where all the Flow Director - * filters were saved. + * Helper function which returns max value among the queue counts set on the + * channels/TCs created. **/ -static void i40e_fdir_filter_exit(struct i40e_pf *pf) +static int i40e_get_max_queues_for_channel(struct i40e_vsi *vsi) { - struct i40e_fdir_filter *filter; - struct i40e_flex_pit *pit_entry, *tmp; - struct hlist_node *node2; + struct i40e_channel *ch, *ch_tmp; + int max = 0; - hlist_for_each_entry_safe(filter, node2, - &pf->fdir_filter_list, fdir_node) { - hlist_del(&filter->fdir_node); - kfree(filter); + list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list, list) { + if (!ch->initialized) + continue; + if (ch->num_queue_pairs > max) + max = ch->num_queue_pairs; } - list_for_each_entry_safe(pit_entry, tmp, &pf->l3_flex_pit_list, list) { - list_del(&pit_entry->list); - kfree(pit_entry); + return max; +} + +/** + * i40e_validate_num_queues - validate num_queues w.r.t channel + * @pf: ptr to PF device + * @num_queues: number of queues + * @vsi: the parent VSI + * @reconfig_rss: indicates should the RSS be reconfigured or not + * + * This function validates number of queues in the context of new channel + * which is being established and determines if RSS should be reconfigured + * or not for parent VSI. + **/ +static int i40e_validate_num_queues(struct i40e_pf *pf, int num_queues, + struct i40e_vsi *vsi, bool *reconfig_rss) +{ + int max_ch_queues; + + if (!reconfig_rss) + return -EINVAL; + + *reconfig_rss = false; + if (vsi->current_rss_size) { + if (num_queues > vsi->current_rss_size) { + dev_dbg(&pf->pdev->dev, + "Error: num_queues (%d) > vsi's current_size(%d)\n", + num_queues, vsi->current_rss_size); + return -EINVAL; + } else if ((num_queues < vsi->current_rss_size) && + (!is_power_of_2(num_queues))) { + dev_dbg(&pf->pdev->dev, + "Error: num_queues (%d) < vsi's current_size(%d), but not power of 2\n", + num_queues, vsi->current_rss_size); + return -EINVAL; + } } - INIT_LIST_HEAD(&pf->l3_flex_pit_list); - list_for_each_entry_safe(pit_entry, tmp, &pf->l4_flex_pit_list, list) { - list_del(&pit_entry->list); - kfree(pit_entry); + if (!is_power_of_2(num_queues)) { + /* Find the max num_queues configured for channel if channel + * exist. + * if channel exist, then enforce 'num_queues' to be more than + * max ever queues configured for channel. + */ + max_ch_queues = i40e_get_max_queues_for_channel(vsi); + if (num_queues < max_ch_queues) { + dev_dbg(&pf->pdev->dev, + "Error: num_queues (%d) < max queues configured for channel(%d)\n", + num_queues, max_ch_queues); + return -EINVAL; + } + *reconfig_rss = true; } - INIT_LIST_HEAD(&pf->l4_flex_pit_list); - pf->fdir_pf_active_filters = 0; - pf->fd_tcp4_filter_cnt = 0; - pf->fd_udp4_filter_cnt = 0; - pf->fd_sctp4_filter_cnt = 0; - pf->fd_ip4_filter_cnt = 0; + return 0; +} - /* Reprogram the default input set for TCP/IPv4 */ - i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_TCP, - I40E_L3_SRC_MASK | I40E_L3_DST_MASK | - I40E_L4_SRC_MASK | I40E_L4_DST_MASK); +/** + * i40e_vsi_reconfig_rss - reconfig RSS based on specified rss_size + * @vsi: the VSI being setup + * @rss_size: size of RSS, accordingly LUT gets reprogrammed + * + * This function reconfigures RSS by reprogramming LUTs using 'rss_size' + **/ +static int i40e_vsi_reconfig_rss(struct i40e_vsi *vsi, u16 rss_size) +{ + struct i40e_pf *pf = vsi->back; + u8 seed[I40E_HKEY_ARRAY_SIZE]; + struct i40e_hw *hw = &pf->hw; + int local_rss_size; + u8 *lut; + int ret; - /* Reprogram the default input set for UDP/IPv4 */ - i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_UDP, - I40E_L3_SRC_MASK | I40E_L3_DST_MASK | - I40E_L4_SRC_MASK | I40E_L4_DST_MASK); + if (!vsi->rss_size) + return -EINVAL; - /* Reprogram the default input set for SCTP/IPv4 */ - i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_SCTP, - I40E_L3_SRC_MASK | I40E_L3_DST_MASK | - I40E_L4_SRC_MASK | I40E_L4_DST_MASK); + if (rss_size > vsi->rss_size) + return -EINVAL; - /* Reprogram the default input set for Other/IPv4 */ - i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_OTHER, - I40E_L3_SRC_MASK | I40E_L3_DST_MASK); + local_rss_size = min_t(int, vsi->rss_size, rss_size); + lut = kzalloc(vsi->rss_table_size, GFP_KERNEL); + if (!lut) + return -ENOMEM; + + /* Ignoring user configured lut if there is one */ + i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, local_rss_size); + + /* Use user configured hash key if there is one, otherwise + * use default. + */ + if (vsi->rss_hkey_user) + memcpy(seed, vsi->rss_hkey_user, I40E_HKEY_ARRAY_SIZE); + else + netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE); + + ret = i40e_config_rss(vsi, seed, lut, vsi->rss_table_size); + if (ret) { + dev_info(&pf->pdev->dev, + "Cannot set RSS lut, err %s aq_err %s\n", + i40e_stat_str(hw, ret), + i40e_aq_str(hw, hw->aq.asq_last_status)); + kfree(lut); + return ret; + } + kfree(lut); + + /* Do the update w.r.t. storing rss_size */ + if (!vsi->orig_rss_size) + vsi->orig_rss_size = vsi->rss_size; + vsi->current_rss_size = local_rss_size; + + return ret; } /** - * i40e_close - Disables a network interface - * @netdev: network interface device structure - * - * The close entry point is called when an interface is de-activated - * by the OS. The hardware is still under the driver's control, but - * this netdev interface is disabled. + * i40e_channel_setup_queue_map - Setup a channel queue map + * @pf: ptr to PF device + * @ctxt: VSI context structure + * @ch: ptr to channel structure * - * Returns 0, this is not allowed to fail + * Setup queue map for a specific channel **/ -int i40e_close(struct net_device *netdev) +static int i40e_channel_setup_queue_map(struct i40e_pf *pf, + struct i40e_vsi_context *ctxt, + struct i40e_channel *ch) { - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_vsi *vsi = np->vsi; + u16 qmap, sections = 0; + u8 offset = 0; + int pow; - i40e_vsi_close(vsi); + if (ch->num_queue_pairs > pf->num_lan_msix) { + dev_err(&pf->pdev->dev, + "Requested queues number exceeded max available MSI-X vectors. Refused to set queue map\n"); + return -EINVAL; + } + + sections = I40E_AQ_VSI_PROP_QUEUE_MAP_VALID; + sections |= I40E_AQ_VSI_PROP_SCHED_VALID; + + /* find the next higher power-of-2 of num queue pairs */ + pow = ilog2(ch->num_queue_pairs); + if (!is_power_of_2(ch->num_queue_pairs)) + pow++; + + qmap = (offset << I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT) | + (pow << I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT); + + /* Setup queue TC[0].qmap for given VSI context */ + ctxt->info.tc_mapping[0] = cpu_to_le16(qmap); + + ctxt->info.up_enable_bits = 0x1; /* TC0 enabled */ + ctxt->info.mapping_flags |= cpu_to_le16(I40E_AQ_VSI_QUE_MAP_CONTIG); + ctxt->info.queue_mapping[0] = cpu_to_le16(ch->base_queue); + ctxt->info.valid_sections |= cpu_to_le16(sections); return 0; } /** - * i40e_do_reset - Start a PF or Core Reset sequence - * @pf: board private structure - * @reset_flags: which reset is requested - * @lock_acquired: indicates whether or not the lock has been acquired - * before this function was called. + * i40e_add_channel - add a channel by adding VSI + * @pf: ptr to PF device + * @uplink_seid: underlying HW switching element (VEB) ID + * @ch: ptr to channel structure * - * The essential difference in resets is that the PF Reset - * doesn't clear the packet buffers, doesn't reset the PE - * firmware, and doesn't bother the other PFs on the chip. + * Add a channel (VSI) using add_vsi and queue_map **/ -void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags, bool lock_acquired) +static int i40e_add_channel(struct i40e_pf *pf, u16 uplink_seid, + struct i40e_channel *ch) { - u32 val; - - WARN_ON(in_interrupt()); + struct i40e_hw *hw = &pf->hw; + struct i40e_vsi_context ctxt; + u8 enabled_tc = 0x1; /* TC0 enabled */ + int ret; + if (ch->type != I40E_VSI_VMDQ2) { + dev_info(&pf->pdev->dev, + "add new vsi failed, ch->type %d\n", ch->type); + return -EINVAL; + } - /* do the biggest reset indicated */ - if (reset_flags & BIT_ULL(__I40E_GLOBAL_RESET_REQUESTED)) { + memset(&ctxt, 0, sizeof(ctxt)); + ctxt.pf_num = hw->pf_id; + ctxt.vf_num = 0; + ctxt.uplink_seid = uplink_seid; + ctxt.connection_type = I40E_AQ_VSI_CONN_TYPE_NORMAL; + if (ch->type == I40E_VSI_VMDQ2) + ctxt.flags = I40E_AQ_VSI_TYPE_VMDQ2; - /* Request a Global Reset - * - * This will start the chip's countdown to the actual full - * chip reset event, and a warning interrupt to be sent - * to all PFs, including the requestor. Our handler - * for the warning interrupt will deal with the shutdown - * and recovery of the switch setup. - */ - dev_dbg(&pf->pdev->dev, "GlobalR requested\n"); - val = rd32(&pf->hw, I40E_GLGEN_RTRIG); - val |= I40E_GLGEN_RTRIG_GLOBR_MASK; - wr32(&pf->hw, I40E_GLGEN_RTRIG, val); + if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED) { + ctxt.info.valid_sections |= + cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID); + ctxt.info.switch_id = + cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB); + } - } else if (reset_flags & BIT_ULL(__I40E_CORE_RESET_REQUESTED)) { + /* Set queue map for a given VSI context */ + ret = i40e_channel_setup_queue_map(pf, &ctxt, ch); + if (ret) + return ret; - /* Request a Core Reset - * - * Same as Global Reset, except does *not* include the MAC/PHY - */ - dev_dbg(&pf->pdev->dev, "CoreR requested\n"); - val = rd32(&pf->hw, I40E_GLGEN_RTRIG); - val |= I40E_GLGEN_RTRIG_CORER_MASK; - wr32(&pf->hw, I40E_GLGEN_RTRIG, val); - i40e_flush(&pf->hw); + /* Now time to create VSI */ + ret = i40e_aq_add_vsi(hw, &ctxt, NULL); + if (ret) { + dev_info(&pf->pdev->dev, + "add new vsi failed, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); + return -ENOENT; + } - } else if (reset_flags & BIT_ULL(__I40E_PF_RESET_REQUESTED)) { + /* Success, update channel */ + ch->enabled_tc = enabled_tc; + ch->seid = ctxt.seid; + ch->vsi_number = ctxt.vsi_number; + ch->stat_counter_idx = le16_to_cpu(ctxt.info.stat_counter_idx); - /* Request a PF Reset - * - * Resets only the PF-specific registers - * - * This goes directly to the tear-down and rebuild of - * the switch, since we need to do all the recovery as - * for the Core Reset. - */ - dev_dbg(&pf->pdev->dev, "PFR requested\n"); - i40e_handle_reset_warning(pf, lock_acquired); + /* copy just the sections touched not the entire info + * since not all sections are valid as returned by + * update vsi params + */ + ch->info.mapping_flags = ctxt.info.mapping_flags; + memcpy(&ch->info.queue_mapping, + &ctxt.info.queue_mapping, sizeof(ctxt.info.queue_mapping)); + memcpy(&ch->info.tc_mapping, ctxt.info.tc_mapping, + sizeof(ctxt.info.tc_mapping)); - } else if (reset_flags & BIT_ULL(__I40E_REINIT_REQUESTED)) { - int v; + return 0; +} - /* Find the VSI(s) that requested a re-init */ - dev_info(&pf->pdev->dev, - "VSI reinit requested\n"); - for (v = 0; v < pf->num_alloc_vsi; v++) { - struct i40e_vsi *vsi = pf->vsi[v]; - - if (vsi != NULL && - test_and_clear_bit(__I40E_VSI_REINIT_REQUESTED, - vsi->state)) - i40e_vsi_reinit_locked(pf->vsi[v]); - } - } else if (reset_flags & BIT_ULL(__I40E_DOWN_REQUESTED)) { - int v; +static int i40e_channel_config_bw(struct i40e_vsi *vsi, struct i40e_channel *ch, + u8 *bw_share) +{ + struct i40e_aqc_configure_vsi_tc_bw_data bw_data; + i40e_status ret; + int i; - /* Find the VSI(s) that needs to be brought down */ - dev_info(&pf->pdev->dev, "VSI down requested\n"); - for (v = 0; v < pf->num_alloc_vsi; v++) { - struct i40e_vsi *vsi = pf->vsi[v]; + bw_data.tc_valid_bits = ch->enabled_tc; + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) + bw_data.tc_bw_credits[i] = bw_share[i]; - if (vsi != NULL && - test_and_clear_bit(__I40E_VSI_DOWN_REQUESTED, - vsi->state)) { - set_bit(__I40E_VSI_DOWN, vsi->state); - i40e_down(vsi); - } - } - } else { - dev_info(&pf->pdev->dev, - "bad reset request 0x%08x\n", reset_flags); + ret = i40e_aq_config_vsi_tc_bw(&vsi->back->hw, ch->seid, + &bw_data, NULL); + if (ret) { + dev_info(&vsi->back->pdev->dev, + "Config VSI BW allocation per TC failed, aq_err: %d for new_vsi->seid %u\n", + vsi->back->hw.aq.asq_last_status, ch->seid); + return -EINVAL; } + + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) + ch->info.qs_handle[i] = bw_data.qs_handles[i]; + + return 0; } -#ifdef CONFIG_I40E_DCB /** - * i40e_dcb_need_reconfig - Check if DCB needs reconfig - * @pf: board private structure - * @old_cfg: current DCB config - * @new_cfg: new DCB config + * i40e_channel_config_tx_ring - config TX ring associated with new channel + * @pf: ptr to PF device + * @vsi: the VSI being setup + * @ch: ptr to channel structure + * + * Configure TX rings associated with channel (VSI) since queues are being + * from parent VSI. **/ -bool i40e_dcb_need_reconfig(struct i40e_pf *pf, - struct i40e_dcbx_config *old_cfg, - struct i40e_dcbx_config *new_cfg) +static int i40e_channel_config_tx_ring(struct i40e_pf *pf, + struct i40e_vsi *vsi, + struct i40e_channel *ch) { - bool need_reconfig = false; - - /* Check if ETS configuration has changed */ - if (memcmp(&new_cfg->etscfg, - &old_cfg->etscfg, - sizeof(new_cfg->etscfg))) { - /* If Priority Table has changed reconfig is needed */ - if (memcmp(&new_cfg->etscfg.prioritytable, - &old_cfg->etscfg.prioritytable, - sizeof(new_cfg->etscfg.prioritytable))) { - need_reconfig = true; - dev_dbg(&pf->pdev->dev, "ETS UP2TC changed.\n"); - } - - if (memcmp(&new_cfg->etscfg.tcbwtable, - &old_cfg->etscfg.tcbwtable, - sizeof(new_cfg->etscfg.tcbwtable))) - dev_dbg(&pf->pdev->dev, "ETS TC BW Table changed.\n"); + i40e_status ret; + int i; + u8 bw_share[I40E_MAX_TRAFFIC_CLASS] = {0}; - if (memcmp(&new_cfg->etscfg.tsatable, - &old_cfg->etscfg.tsatable, - sizeof(new_cfg->etscfg.tsatable))) - dev_dbg(&pf->pdev->dev, "ETS TSA Table changed.\n"); + /* Enable ETS TCs with equal BW Share for now across all VSIs */ + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { + if (ch->enabled_tc & BIT(i)) + bw_share[i] = 1; } - /* Check if PFC configuration has changed */ - if (memcmp(&new_cfg->pfc, - &old_cfg->pfc, - sizeof(new_cfg->pfc))) { - need_reconfig = true; - dev_dbg(&pf->pdev->dev, "PFC config change detected.\n"); + /* configure BW for new VSI */ + ret = i40e_channel_config_bw(vsi, ch, bw_share); + if (ret) { + dev_info(&vsi->back->pdev->dev, + "Failed configuring TC map %d for channel (seid %u)\n", + ch->enabled_tc, ch->seid); + return ret; } - /* Check if APP Table has changed */ - if (memcmp(&new_cfg->app, - &old_cfg->app, - sizeof(new_cfg->app))) { - need_reconfig = true; - dev_dbg(&pf->pdev->dev, "APP Table change detected.\n"); + for (i = 0; i < ch->num_queue_pairs; i++) { + struct i40e_ring *tx_ring, *rx_ring; + u16 pf_q; + + pf_q = ch->base_queue + i; + + /* Get to TX ring ptr of main VSI, for re-setup TX queue + * context + */ + tx_ring = vsi->tx_rings[pf_q]; + tx_ring->ch = ch; + + /* Get the RX ring ptr */ + rx_ring = vsi->rx_rings[pf_q]; + rx_ring->ch = ch; } - dev_dbg(&pf->pdev->dev, "dcb need_reconfig=%d\n", need_reconfig); - return need_reconfig; + return 0; } /** - * i40e_handle_lldp_event - Handle LLDP Change MIB event - * @pf: board private structure - * @e: event info posted on ARQ + * i40e_setup_hw_channel - setup new channel + * @pf: ptr to PF device + * @vsi: the VSI being setup + * @ch: ptr to channel structure + * @uplink_seid: underlying HW switching element (VEB) ID + * @type: type of channel to be created (VMDq2/VF) + * + * Setup new channel (VSI) based on specified type (VMDq2/VF) + * and configures TX rings accordingly **/ -static int i40e_handle_lldp_event(struct i40e_pf *pf, - struct i40e_arq_event_info *e) +static inline int i40e_setup_hw_channel(struct i40e_pf *pf, + struct i40e_vsi *vsi, + struct i40e_channel *ch, + u16 uplink_seid, u8 type) { - struct i40e_aqc_lldp_get_mib *mib = - (struct i40e_aqc_lldp_get_mib *)&e->desc.params.raw; - struct i40e_hw *hw = &pf->hw; - struct i40e_dcbx_config tmp_dcbx_cfg; - bool need_reconfig = false; - int ret = 0; - u8 type; + int ret; - /* Not DCB capable or capability disabled */ - if (!(pf->flags & I40E_FLAG_DCB_CAPABLE)) + ch->initialized = false; + ch->base_queue = vsi->next_base_queue; + ch->type = type; + + /* Proceed with creation of channel (VMDq2) VSI */ + ret = i40e_add_channel(pf, uplink_seid, ch); + if (ret) { + dev_info(&pf->pdev->dev, + "failed to add_channel using uplink_seid %u\n", + uplink_seid); return ret; + } - /* Ignore if event is not for Nearest Bridge */ - type = ((mib->type >> I40E_AQ_LLDP_BRIDGE_TYPE_SHIFT) - & I40E_AQ_LLDP_BRIDGE_TYPE_MASK); - dev_dbg(&pf->pdev->dev, "LLDP event mib bridge type 0x%x\n", type); - if (type != I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE) + /* Mark the successful creation of channel */ + ch->initialized = true; + + /* Reconfigure TX queues using QTX_CTL register */ + ret = i40e_channel_config_tx_ring(pf, vsi, ch); + if (ret) { + dev_info(&pf->pdev->dev, + "failed to configure TX rings for channel %u\n", + ch->seid); return ret; + } - /* Check MIB Type and return if event for Remote MIB update */ - type = mib->type & I40E_AQ_LLDP_MIB_TYPE_MASK; + /* update 'next_base_queue' */ + vsi->next_base_queue = vsi->next_base_queue + ch->num_queue_pairs; dev_dbg(&pf->pdev->dev, - "LLDP event mib type %s\n", type ? "remote" : "local"); - if (type == I40E_AQ_LLDP_MIB_REMOTE) { - /* Update the remote cached instance and return */ - ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE, - I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE, - &hw->remote_dcbx_config); - goto exit; + "Added channel: vsi_seid %u, vsi_number %u, stat_counter_idx %u, num_queue_pairs %u, pf->next_base_queue %d\n", + ch->seid, ch->vsi_number, ch->stat_counter_idx, + ch->num_queue_pairs, + vsi->next_base_queue); + return ret; +} + +/** + * i40e_setup_channel - setup new channel using uplink element + * @pf: ptr to PF device + * @vsi: the VSI being setup + * @ch: ptr to channel structure + * + * Setup new channel (VSI) based on specified type (VMDq2/VF) + * and uplink switching element (uplink_seid) + **/ +static bool i40e_setup_channel(struct i40e_pf *pf, struct i40e_vsi *vsi, + struct i40e_channel *ch) +{ + u8 vsi_type; + u16 seid; + int ret; + + if (vsi->type == I40E_VSI_MAIN) { + vsi_type = I40E_VSI_VMDQ2; + } else { + dev_err(&pf->pdev->dev, "unsupported parent vsi type(%d)\n", + vsi->type); + return false; } - /* Store the old configuration */ - tmp_dcbx_cfg = hw->local_dcbx_config; + /* underlying switching element */ + seid = pf->vsi[pf->lan_vsi]->uplink_seid; - /* Reset the old DCBx configuration data */ - memset(&hw->local_dcbx_config, 0, sizeof(hw->local_dcbx_config)); - /* Get updated DCBX data from firmware */ - ret = i40e_get_dcb_config(&pf->hw); + /* create channel (VSI), configure TX rings */ + ret = i40e_setup_hw_channel(pf, vsi, ch, seid, vsi_type); if (ret) { - dev_info(&pf->pdev->dev, - "Failed querying DCB configuration data from firmware, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - goto exit; + dev_err(&pf->pdev->dev, "failed to setup hw_channel\n"); + return false; } - /* No change detected in DCBX configs */ - if (!memcmp(&tmp_dcbx_cfg, &hw->local_dcbx_config, - sizeof(tmp_dcbx_cfg))) { - dev_dbg(&pf->pdev->dev, "No change detected in DCBX configuration.\n"); - goto exit; + return ch->initialized ? true : false; +} + +/** + * i40e_create_queue_channel - function to create channel + * @vsi: VSI to be configured + * @ch: ptr to channel (it contains channel specific params) + * + * This function creates channel (VSI) using num_queues specified by user, + * reconfigs RSS if needed. + **/ +int i40e_create_queue_channel(struct i40e_vsi *vsi, + struct i40e_channel *ch) +{ + struct i40e_pf *pf = vsi->back; + bool reconfig_rss; + int err; + + if (!ch) + return -EINVAL; + + if (!ch->num_queue_pairs) { + dev_err(&pf->pdev->dev, "Invalid num_queues requested: %d\n", + ch->num_queue_pairs); + return -EINVAL; } - need_reconfig = i40e_dcb_need_reconfig(pf, &tmp_dcbx_cfg, - &hw->local_dcbx_config); + /* validate user requested num_queues for channel */ + err = i40e_validate_num_queues(pf, ch->num_queue_pairs, vsi, + &reconfig_rss); + if (err) { + dev_info(&pf->pdev->dev, "Failed to validate num_queues (%d)\n", + ch->num_queue_pairs); + return -EINVAL; + } - i40e_dcbnl_flush_apps(pf, &tmp_dcbx_cfg, &hw->local_dcbx_config); + /* By default we are in VEPA mode, if this is the first VF/VMDq + * VSI to be added switch to VEB mode. + */ + if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) { + pf->flags |= I40E_FLAG_VEB_MODE_ENABLED; - if (!need_reconfig) - goto exit; + if (vsi->type == I40E_VSI_MAIN) { + if (pf->flags & I40E_FLAG_TC_MQPRIO) + i40e_do_reset(pf, I40E_PF_RESET_FLAG, true); + else + i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG); + } + /* now onwards for main VSI, number of queues will be value + * of TC0's queue count + */ + } - /* Enable DCB tagging only when more than one TC */ - if (i40e_dcb_get_num_tc(&hw->local_dcbx_config) > 1) - pf->flags |= I40E_FLAG_DCB_ENABLED; - else - pf->flags &= ~I40E_FLAG_DCB_ENABLED; + /* By this time, vsi->cnt_q_avail shall be set to non-zero and + * it should be more than num_queues + */ + if (!vsi->cnt_q_avail || vsi->cnt_q_avail < ch->num_queue_pairs) { + dev_dbg(&pf->pdev->dev, + "Error: cnt_q_avail (%u) less than num_queues %d\n", + vsi->cnt_q_avail, ch->num_queue_pairs); + return -EINVAL; + } - set_bit(__I40E_PORT_SUSPENDED, pf->state); - /* Reconfiguration needed quiesce all VSIs */ - i40e_pf_quiesce_all_vsi(pf); + /* reconfig_rss only if vsi type is MAIN_VSI */ + if (reconfig_rss && vsi->type == I40E_VSI_MAIN) { + err = i40e_vsi_reconfig_rss(vsi, ch->num_queue_pairs); + if (err) { + dev_info(&pf->pdev->dev, + "Error: unable to reconfig rss for num_queues (%u)\n", + ch->num_queue_pairs); + return -EINVAL; + } + } - /* Changes in configuration update VEB/VSI */ - i40e_dcb_reconfigure(pf); + if (!i40e_setup_channel(pf, vsi, ch)) { + dev_info(&pf->pdev->dev, "Failed to setup channel\n"); + return -EINVAL; + } - ret = i40e_resume_port_tx(pf); + dev_info(&pf->pdev->dev, + "Setup channel (id:%u) utilizing num_queues %d\n", + ch->seid, ch->num_queue_pairs); - clear_bit(__I40E_PORT_SUSPENDED, pf->state); - /* In case of error no point in resuming VSIs */ - if (ret) - goto exit; + /* configure VSI for BW limit */ + if (ch->max_tx_rate) { + if (i40e_set_bw_limit(vsi, ch->seid, ch->max_tx_rate)) + return -EINVAL; - /* Wait for the PF's queues to be disabled */ - ret = i40e_pf_wait_queues_disabled(pf); - if (ret) { - /* Schedule PF reset to recover */ - set_bit(__I40E_PF_RESET_REQUESTED, pf->state); - i40e_service_event_schedule(pf); - } else { - i40e_pf_unquiesce_all_vsi(pf); - pf->flags |= (I40E_FLAG_SERVICE_CLIENT_REQUESTED | - I40E_FLAG_CLIENT_L2_CHANGE); + dev_dbg(&pf->pdev->dev, + "Set tx rate of %llu Mbps (count of 50Mbps %llu) for vsi->seid %u\n", + ch->max_tx_rate, + ch->max_tx_rate / I40E_BW_CREDIT_DIVISOR, ch->seid); } -exit: - return ret; + /* in case of VF, this will be main SRIOV VSI */ + ch->parent_vsi = vsi; + + /* and update main_vsi's count for queue_available to use */ + vsi->cnt_q_avail -= ch->num_queue_pairs; + + return 0; } -#endif /* CONFIG_I40E_DCB */ /** - * i40e_do_reset_safe - Protected reset path for userland calls. - * @pf: board private structure - * @reset_flags: which reset is requested + * i40e_configure_queue_channels - Add queue channel for the given TCs + * @vsi: VSI to be configured * + * Configures queue channel mapping to the given TCs **/ -void i40e_do_reset_safe(struct i40e_pf *pf, u32 reset_flags) +static int i40e_configure_queue_channels(struct i40e_vsi *vsi) { - rtnl_lock(); - i40e_do_reset(pf, reset_flags, true); - rtnl_unlock(); + struct i40e_channel *ch; + int ret = 0, i; + + /* Create app vsi with the TCs. Main VSI with TC0 is already set up */ + vsi->tc_seid_map[0] = vsi->seid; + for (i = 1; i < I40E_MAX_TRAFFIC_CLASS; i++) { + if (vsi->tc_config.enabled_tc & BIT(i)) { + ch = kzalloc(sizeof(*ch), GFP_KERNEL); + if (!ch) { + ret = -ENOMEM; + goto err_free; + } + + INIT_LIST_HEAD(&ch->list); + ch->num_queue_pairs = + vsi->tc_config.tc_info[i].qcount; + ch->base_queue = + vsi->tc_config.tc_info[i].qoffset; + + /* Bandwidth limit through tc interface is in bytes/s, + * change to Mbit/s + */ + ch->max_tx_rate = + vsi->mqprio_qopt.max_rate[i] / (1000000 / 8); + + list_add_tail(&ch->list, &vsi->ch_list); + + ret = i40e_create_queue_channel(vsi, ch); + if (ret) { + dev_err(&vsi->back->pdev->dev, + "Failed creating queue channel with TC%d: queues %d\n", + i, ch->num_queue_pairs); + goto err_free; + } + vsi->tc_seid_map[i] = ch->seid; + } + } + return ret; + +err_free: + i40e_remove_queue_channels(vsi); + return ret; } /** - * i40e_handle_lan_overflow_event - Handler for LAN queue overflow event - * @pf: board private structure - * @e: event info posted on ARQ - * - * Handler for LAN Queue Overflow Event generated by the firmware for PF - * and VF queues + * i40e_validate_mqprio_qopt- validate queue mapping info + * @vsi: the VSI being configured + * @mqprio_qopt: queue parametrs **/ -static void i40e_handle_lan_overflow_event(struct i40e_pf *pf, - struct i40e_arq_event_info *e) +static int i40e_validate_mqprio_qopt(struct i40e_vsi *vsi, + struct tc_mqprio_qopt_offload *mqprio_qopt) { - struct i40e_aqc_lan_overflow *data = - (struct i40e_aqc_lan_overflow *)&e->desc.params.raw; - u32 queue = le32_to_cpu(data->prtdcb_rupto); - u32 qtx_ctl = le32_to_cpu(data->otx_ctl); - struct i40e_hw *hw = &pf->hw; - struct i40e_vf *vf; - u16 vf_id; + u64 sum_max_rate = 0; + int i; - dev_dbg(&pf->pdev->dev, "overflow Rx Queue Number = %d QTX_CTL=0x%08x\n", - queue, qtx_ctl); + if (mqprio_qopt->qopt.offset[0] != 0 || + mqprio_qopt->qopt.num_tc < 1 || + mqprio_qopt->qopt.num_tc > I40E_MAX_TRAFFIC_CLASS) + return -EINVAL; + for (i = 0; ; i++) { + if (!mqprio_qopt->qopt.count[i]) + return -EINVAL; + if (mqprio_qopt->min_rate[i]) { + dev_err(&vsi->back->pdev->dev, + "Invalid min tx rate (greater than 0) specified\n"); + return -EINVAL; + } + sum_max_rate += (mqprio_qopt->max_rate[i] / (1000000 / 8)); - /* Queue belongs to VF, find the VF and issue VF reset */ - if (((qtx_ctl & I40E_QTX_CTL_PFVF_Q_MASK) - >> I40E_QTX_CTL_PFVF_Q_SHIFT) == I40E_QTX_CTL_VF_QUEUE) { - vf_id = (u16)((qtx_ctl & I40E_QTX_CTL_VFVM_INDX_MASK) - >> I40E_QTX_CTL_VFVM_INDX_SHIFT); - vf_id -= hw->func_caps.vf_base_id; - vf = &pf->vf[vf_id]; - i40e_vc_notify_vf_reset(vf); - /* Allow VF to process pending reset notification */ - msleep(20); - i40e_reset_vf(vf, false); + if (i >= mqprio_qopt->qopt.num_tc - 1) + break; + if (mqprio_qopt->qopt.offset[i + 1] != + (mqprio_qopt->qopt.offset[i] + mqprio_qopt->qopt.count[i])) + return -EINVAL; + } + if (vsi->num_queue_pairs < + (mqprio_qopt->qopt.offset[i] + mqprio_qopt->qopt.count[i])) { + dev_err(&vsi->back->pdev->dev, + "Failed to create traffic channel, insufficient number of queues.\n"); + return -EINVAL; + } + if (sum_max_rate > i40e_get_link_speed(vsi)) { + dev_err(&vsi->back->pdev->dev, + "Invalid max tx rate specified\n"); + return -EINVAL; } + return 0; } /** - * i40e_get_cur_guaranteed_fd_count - Get the consumed guaranteed FD filters - * @pf: board private structure + * i40e_vsi_set_default_tc_config - set default values for tc configuration + * @vsi: the VSI being configured **/ -u32 i40e_get_cur_guaranteed_fd_count(struct i40e_pf *pf) +static void i40e_vsi_set_default_tc_config(struct i40e_vsi *vsi) { - u32 val, fcnt_prog; + u16 qcount; + int i; - val = rd32(&pf->hw, I40E_PFQF_FDSTAT); - fcnt_prog = (val & I40E_PFQF_FDSTAT_GUARANT_CNT_MASK); - return fcnt_prog; + /* Only TC0 is enabled */ + vsi->tc_config.numtc = 1; + vsi->tc_config.enabled_tc = 1; + qcount = min_t(int, vsi->alloc_queue_pairs, + i40e_pf_get_max_q_per_tc(vsi->back)); + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { + /* For the TC that is not enabled set the offset to to default + * queue and allocate one queue for the given TC. + */ + vsi->tc_config.tc_info[i].qoffset = 0; + if (i == 0) + vsi->tc_config.tc_info[i].qcount = qcount; + else + vsi->tc_config.tc_info[i].qcount = 1; + vsi->tc_config.tc_info[i].netdev_tc = 0; + } } /** - * i40e_get_current_fd_count - Get total FD filters programmed for this PF - * @pf: board private structure + * i40e_rebuild_cloud_filters - Rebuilds cloud filters for VSIs + * @vsi: PF main vsi + * @seid: seid of main or channel VSIs + * + * Rebuilds cloud filters associated with main VSI and channel VSIs if they + * existed before reset **/ -u32 i40e_get_current_fd_count(struct i40e_pf *pf) +static int i40e_rebuild_cloud_filters(struct i40e_vsi *vsi, u16 seid) { - u32 val, fcnt_prog; + struct i40e_cloud_filter *cfilter; + struct i40e_pf *pf = vsi->back; + struct hlist_node *node; + i40e_status ret; - val = rd32(&pf->hw, I40E_PFQF_FDSTAT); - fcnt_prog = (val & I40E_PFQF_FDSTAT_GUARANT_CNT_MASK) + - ((val & I40E_PFQF_FDSTAT_BEST_CNT_MASK) >> - I40E_PFQF_FDSTAT_BEST_CNT_SHIFT); - return fcnt_prog; + /* Add cloud filters back if they exist */ + hlist_for_each_entry_safe(cfilter, node, &pf->cloud_filter_list, + cloud_node) { + if (cfilter->seid != seid) + continue; + + if (cfilter->dst_port) + ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter, + true); + else + ret = i40e_add_del_cloud_filter(vsi, cfilter, true); + + if (ret) { + dev_dbg(&pf->pdev->dev, + "Failed to rebuild cloud filter, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); + return ret; + } + } + return 0; } /** - * i40e_get_global_fd_count - Get total FD filters programmed on device - * @pf: board private structure + * i40e_rebuild_channels - Rebuilds channel VSIs if they existed before reset + * @vsi: PF main vsi + * + * Rebuilds channel VSIs if they existed before reset **/ -u32 i40e_get_global_fd_count(struct i40e_pf *pf) +static int i40e_rebuild_channels(struct i40e_vsi *vsi) { - u32 val, fcnt_prog; + struct i40e_channel *ch, *ch_tmp; + i40e_status ret; - val = rd32(&pf->hw, I40E_GLQF_FDCNT_0); - fcnt_prog = (val & I40E_GLQF_FDCNT_0_GUARANT_CNT_MASK) + - ((val & I40E_GLQF_FDCNT_0_BESTCNT_MASK) >> - I40E_GLQF_FDCNT_0_BESTCNT_SHIFT); - return fcnt_prog; + if (list_empty(&vsi->ch_list)) + return 0; + + list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list, list) { + if (!ch->initialized) + break; + /* Proceed with creation of channel (VMDq2) VSI */ + ret = i40e_add_channel(vsi->back, vsi->uplink_seid, ch); + if (ret) { + dev_info(&vsi->back->pdev->dev, + "failed to rebuild channels using uplink_seid %u\n", + vsi->uplink_seid); + return ret; + } + if (ch->max_tx_rate) { + if (i40e_set_bw_limit(vsi, ch->seid, + ch->max_tx_rate)) + return -EINVAL; + + dev_dbg(&vsi->back->pdev->dev, + "Set tx rate of %llu Mbps (count of 50Mbps %llu) for vsi->seid %u\n", + ch->max_tx_rate, + ch->max_tx_rate / I40E_BW_CREDIT_DIVISOR, + ch->seid); + } + ret = i40e_rebuild_cloud_filters(vsi, ch->seid); + if (ret) { + dev_dbg(&vsi->back->pdev->dev, + "Failed to rebuild cloud filters for channel VSI %u\n", + ch->seid); + return ret; + } + } + return 0; } +#endif /* __TC_MQPRIO_MODE_MAX */ +#ifdef HAVE_SETUP_TC /** - * i40e_fdir_check_and_reenable - Function to reenabe FD ATR or SB if disabled - * @pf: board private structure + * i40e_setup_tc - configure multiple traffic classes + * @netdev: net device to configure + * @type_data: tc offload data **/ -void i40e_fdir_check_and_reenable(struct i40e_pf *pf) +#ifdef __TC_MQPRIO_MODE_MAX +static int i40e_setup_tc(struct net_device *netdev, void *type_data) +#else +static int i40e_setup_tc(struct net_device *netdev, u8 tc) +#endif { - struct i40e_fdir_filter *filter; - u32 fcnt_prog, fcnt_avail; - struct hlist_node *node; +#ifdef __TC_MQPRIO_MODE_MAX + struct tc_mqprio_qopt_offload *mqprio_qopt = type_data; +#endif + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + u8 enabled_tc = 0, num_tc; +#ifdef __TC_MQPRIO_MODE_MAX + bool need_reset = false; + int old_queue_pairs; +#endif + int ret = -EINVAL; +#ifdef __TC_MQPRIO_MODE_MAX + u16 mode; + u8 hw; +#endif + int i; - if (test_bit(__I40E_FD_FLUSH_REQUESTED, pf->state)) - return; +#ifdef __TC_MQPRIO_MODE_MAX + old_queue_pairs = vsi->num_queue_pairs; +#endif + /* Check if MFP enabled */ + if (pf->flags & I40E_FLAG_MFP_ENABLED) { + netdev_info(netdev, + "Configuring TC not supported in MFP mode\n"); + goto exit; + } - /* Check if we have enough room to re-enable FDir SB capability. */ - fcnt_prog = i40e_get_global_fd_count(pf); - fcnt_avail = pf->fdir_pf_filter_count; - if ((fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM)) || - (pf->fd_add_err == 0) || - (i40e_get_current_atr_cnt(pf) < pf->fd_atr_cnt)) { - if (pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED) { - pf->flags &= ~I40E_FLAG_FD_SB_AUTO_DISABLED; - if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) && - (I40E_DEBUG_FD & pf->hw.debug_mask)) - dev_info(&pf->pdev->dev, "FD Sideband/ntuple is being enabled since we have space in the table now\n"); - } +#ifndef HAVE_NDO_SETUP_TC_REMOVE_TC_TO_NETDEV + /* Check whether tc count is within enabled limit */ + if (tc > i40e_pf_get_num_tc(pf)) { + netdev_info(netdev, "TC count greater than enabled on link for adapter\n"); + goto exit; + } +#endif + +#ifdef __TC_MQPRIO_MODE_MAX + num_tc = mqprio_qopt->qopt.num_tc; + hw = mqprio_qopt->qopt.hw; + mode = mqprio_qopt->mode; + if (!hw) { + pf->flags &= ~I40E_FLAG_TC_MQPRIO; + memcpy(&vsi->mqprio_qopt, mqprio_qopt, sizeof(*mqprio_qopt)); + goto config_tc; } - /* We should wait for even more space before re-enabling ATR. - * Additionally, we cannot enable ATR as long as we still have TCP SB - * rules active. - */ - if ((fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) && - (pf->fd_tcp4_filter_cnt == 0)) { - if (pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED) { - pf->flags &= ~I40E_FLAG_FD_ATR_AUTO_DISABLED; - if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && - (I40E_DEBUG_FD & pf->hw.debug_mask)) - dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table and there are no conflicting ntuple rules\n"); + switch (mode) { + case TC_MQPRIO_MODE_DCB: + pf->flags &= ~I40E_FLAG_TC_MQPRIO; + + /* Check if DCB enabled to continue */ + if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) { + netdev_info(netdev, + "DCB is not enabled for adapter\n"); + return ret; + } + + /* Check whether tc count is within enabled limit */ + if (num_tc > i40e_pf_get_num_tc(pf)) { + netdev_info(netdev, + "TC count greater than enabled on link for adapter\n"); + return ret; + } + break; + case TC_MQPRIO_MODE_CHANNEL: + if (pf->flags & I40E_FLAG_DCB_ENABLED) { + netdev_info(netdev, + "Full offload of TC Mqprio options is not supported when DCB is enabled\n"); + return ret; } + if (!(pf->flags & I40E_FLAG_MSIX_ENABLED)) + return ret; + ret = i40e_validate_mqprio_qopt(vsi, mqprio_qopt); + if (ret) + return ret; + memcpy(&vsi->mqprio_qopt, mqprio_qopt, + sizeof(*mqprio_qopt)); + pf->flags |= I40E_FLAG_TC_MQPRIO; + pf->flags &= ~I40E_FLAG_DCB_ENABLED; + break; + default: + return -EINVAL; } - /* if hw had a problem adding a filter, delete it */ - if (pf->fd_inv > 0) { - hlist_for_each_entry_safe(filter, node, - &pf->fdir_filter_list, fdir_node) { - if (filter->fd_id == pf->fd_inv) { - hlist_del(&filter->fdir_node); - kfree(filter); - pf->fdir_pf_active_filters--; +config_tc: +#else + /* Check if DCB enabled to continue */ + if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) { + netdev_info(netdev, "DCB is not enabled for adapter\n"); + goto exit; + } + num_tc = tc; +#endif + /* Generate TC map for number of tc requested */ + for (i = 0; i < num_tc; i++) + enabled_tc |= BIT(i); + + /* Requesting same TC configuration as already enabled */ +#ifdef __TC_MQPRIO_MODE_MAX + if (enabled_tc == vsi->tc_config.enabled_tc && + mode != TC_MQPRIO_MODE_CHANNEL) + return 0; +#else + if (enabled_tc == vsi->tc_config.enabled_tc) + return 0; +#endif + + /* Quiesce VSI queues */ + i40e_quiesce_vsi(vsi); + +#ifdef __TC_MQPRIO_MODE_MAX + if (!hw && !(pf->flags & I40E_FLAG_TC_MQPRIO)) + i40e_remove_queue_channels(vsi); +#endif + + /* Configure VSI for enabled TCs */ + ret = i40e_vsi_config_tc(vsi, enabled_tc); + if (ret) { + netdev_info(netdev, "Failed configuring TC for VSI seid=%d\n", + vsi->seid); +#ifdef __TC_MQPRIO_MODE_MAX + need_reset = true; +#endif + goto exit; +#ifdef __TC_MQPRIO_MODE_MAX + } else if (enabled_tc && + (!is_power_of_2(vsi->tc_config.tc_info[0].qcount))) { + netdev_info(netdev, + "Failed to create channel. Override queues (%u) not power of 2\n", + vsi->tc_config.tc_info[0].qcount); + pf->flags &= ~I40E_FLAG_TC_MQPRIO; + vsi->num_queue_pairs = old_queue_pairs; + ret = -EINVAL; + need_reset = true; + goto exit; +#endif + } + + dev_info(&vsi->back->pdev->dev, + "Setup channel (id:%u) utilizing num_queues %d\n", + vsi->seid, vsi->tc_config.tc_info[0].qcount); + +#ifdef __TC_MQPRIO_MODE_MAX + if (pf->flags & I40E_FLAG_TC_MQPRIO) { + if (vsi->mqprio_qopt.max_rate[0]) { + u64 max_tx_rate = vsi->mqprio_qopt.max_rate[0] / + (1000000 / 8); + ret = i40e_set_bw_limit(vsi, vsi->seid, max_tx_rate); + if (!ret) { + dev_dbg(&vsi->back->pdev->dev, + "Set tx rate of %llu Mbps (count of 50Mbps %llu) for vsi->seid %u\n", + max_tx_rate, + max_tx_rate / I40E_BW_CREDIT_DIVISOR, + vsi->seid); + } else { + need_reset = true; + goto exit; } } + ret = i40e_configure_queue_channels(vsi); + if (ret) { + vsi->num_queue_pairs = old_queue_pairs; + netdev_info(netdev, + "Failed configuring queue channels\n"); + need_reset = true; + goto exit; + } + } + +#endif + +exit: +#ifdef __TC_MQPRIO_MODE_MAX + /* Reset the configuration data to defaults, only TC0 is enabled */ + if (need_reset) { + i40e_vsi_set_default_tc_config(vsi); + need_reset = false; } +#endif + /* Unquiesce VSI */ + i40e_unquiesce_vsi(vsi); + return ret; } -#define I40E_MIN_FD_FLUSH_INTERVAL 10 -#define I40E_MIN_FD_FLUSH_SB_ATR_UNSTABLE 30 +#ifdef __TC_MQPRIO_MODE_MAX /** - * i40e_fdir_flush_and_replay - Function to flush all FD filters and replay SB - * @pf: board private structure + * i40e_parse_cls_flower - Parse tc flower filters provided by kernel + * @vsi: Pointer to VSI + * @f: Pointer to struct flow_cls_offload + * @filter: Pointer to cloud filter structure + * **/ -static void i40e_fdir_flush_and_replay(struct i40e_pf *pf) +static int i40e_parse_cls_flower(struct i40e_vsi *vsi, + struct flow_cls_offload *f, + struct i40e_cloud_filter *filter) { - unsigned long min_flush_time; - int flush_wait_retry = 50; - bool disable_atr = false; - int fd_room; - int reg; + struct flow_rule *rule = flow_cls_offload_flow_rule(f); + struct flow_dissector *dissector = rule->match.dissector; + u16 n_proto_mask = 0, n_proto_key = 0, addr_type = 0; + struct i40e_pf *pf = vsi->back; + u8 field_flags = 0; + + if (dissector->used_keys & + ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | + BIT(FLOW_DISSECTOR_KEY_BASIC) | + BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) | +#ifndef HAVE_TC_FLOWER_VLAN_IN_TAGS + BIT(FLOW_DISSECTOR_KEY_VLAN) | +#endif + BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | +#ifdef HAVE_TC_FLOWER_ENC + BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) | +#endif + BIT(FLOW_DISSECTOR_KEY_PORTS))) { + dev_err(&pf->pdev->dev, "Unsupported key used: 0x%x\n", + dissector->used_keys); + return -EOPNOTSUPP; + } - if (!time_after(jiffies, pf->fd_flush_timestamp + - (I40E_MIN_FD_FLUSH_INTERVAL * HZ))) - return; +#ifdef HAVE_TC_FLOWER_ENC + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) { + struct flow_match_enc_keyid match; - /* If the flush is happening too quick and we have mostly SB rules we - * should not re-enable ATR for some time. - */ - min_flush_time = pf->fd_flush_timestamp + - (I40E_MIN_FD_FLUSH_SB_ATR_UNSTABLE * HZ); - fd_room = pf->fdir_pf_filter_count - pf->fdir_pf_active_filters; + flow_rule_match_enc_keyid(rule, &match); + if (match.mask->keyid != 0) + field_flags |= I40E_CLOUD_FIELD_TEN_ID; - if (!(time_after(jiffies, min_flush_time)) && - (fd_room < I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) { - if (I40E_DEBUG_FD & pf->hw.debug_mask) - dev_info(&pf->pdev->dev, "ATR disabled, not enough FD filter space.\n"); - disable_atr = true; + filter->tenant_id = be32_to_cpu(match.key->keyid); } +#endif /* HAVE_TC_FLOWER_ENC */ - pf->fd_flush_timestamp = jiffies; - pf->flags |= I40E_FLAG_FD_ATR_AUTO_DISABLED; - /* flush all filters */ - wr32(&pf->hw, I40E_PFQF_CTL_1, - I40E_PFQF_CTL_1_CLEARFDTABLE_MASK); - i40e_flush(&pf->hw); - pf->fd_flush_cnt++; - pf->fd_add_err = 0; - do { - /* Check FD flush status every 5-6msec */ - usleep_range(5000, 6000); - reg = rd32(&pf->hw, I40E_PFQF_CTL_1); - if (!(reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK)) - break; - } while (flush_wait_retry--); - if (reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK) { - dev_warn(&pf->pdev->dev, "FD table did not flush, needs more time\n"); - } else { - /* replay sideband filters */ - i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); - if (!disable_atr && !pf->fd_tcp4_filter_cnt) - pf->flags &= ~I40E_FLAG_FD_ATR_AUTO_DISABLED; - clear_bit(__I40E_FD_FLUSH_REQUESTED, pf->state); - if (I40E_DEBUG_FD & pf->hw.debug_mask) - dev_info(&pf->pdev->dev, "FD Filter table flushed and FD-SB replayed.\n"); + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { + struct flow_match_basic match; + + flow_rule_match_basic(rule, &match); + n_proto_key = ntohs(match.key->n_proto); + n_proto_mask = ntohs(match.mask->n_proto); + + if (n_proto_key == ETH_P_ALL) { + n_proto_key = 0; + n_proto_mask = 0; + } + filter->n_proto = n_proto_key & n_proto_mask; + filter->ip_proto = match.key->ip_proto; } -} -/** - * i40e_get_current_atr_count - Get the count of total FD ATR filters programmed - * @pf: board private structure - **/ -u32 i40e_get_current_atr_cnt(struct i40e_pf *pf) -{ - return i40e_get_current_fd_count(pf) - pf->fdir_pf_active_filters; -} + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + struct flow_match_eth_addrs match; -/* We can see up to 256 filter programming desc in transit if the filters are - * being applied really fast; before we see the first - * filter miss error on Rx queue 0. Accumulating enough error messages before - * reacting will make sure we don't cause flush too often. - */ -#define I40E_MAX_FD_PROGRAM_ERROR 256 + flow_rule_match_eth_addrs(rule, &match); -/** - * i40e_fdir_reinit_subtask - Worker thread to reinit FDIR filter table - * @pf: board private structure - **/ -static void i40e_fdir_reinit_subtask(struct i40e_pf *pf) -{ + /* use is_broadcast and is_zero to check for all 0xf or 0 */ + if (!is_zero_ether_addr(match.mask->dst)) { + if (is_broadcast_ether_addr(match.mask->dst)) { + field_flags |= I40E_CLOUD_FIELD_OMAC; + } else { + dev_err(&pf->pdev->dev, "Bad ether dest mask %pM\n", + match.mask->dst); + return I40E_ERR_CONFIG; + } + } - /* if interface is down do nothing */ - if (test_bit(__I40E_DOWN, pf->state)) - return; + if (!is_zero_ether_addr(match.mask->src)) { + if (is_broadcast_ether_addr(match.mask->src)) { + field_flags |= I40E_CLOUD_FIELD_IMAC; + } else { + dev_err(&pf->pdev->dev, "Bad ether src mask %pM\n", + match.mask->src); + return I40E_ERR_CONFIG; + } + } + ether_addr_copy(filter->dst_mac, match.key->dst); + ether_addr_copy(filter->src_mac, match.key->src); + } - if (test_bit(__I40E_FD_FLUSH_REQUESTED, pf->state)) - i40e_fdir_flush_and_replay(pf); +#ifndef HAVE_TC_FLOWER_VLAN_IN_TAGS + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { + struct flow_match_vlan match; - i40e_fdir_check_and_reenable(pf); + flow_rule_match_vlan(rule, &match); + if (match.mask->vlan_id) { + if (match.mask->vlan_id == VLAN_VID_MASK) { + field_flags |= I40E_CLOUD_FIELD_IVLAN; -} + } else { + dev_err(&pf->pdev->dev, "Bad vlan mask 0x%04x\n", + match.mask->vlan_id); + return I40E_ERR_CONFIG; + } + } -/** - * i40e_vsi_link_event - notify VSI of a link event - * @vsi: vsi to be notified - * @link_up: link up or down - **/ -static void i40e_vsi_link_event(struct i40e_vsi *vsi, bool link_up) -{ - if (!vsi || test_bit(__I40E_VSI_DOWN, vsi->state)) - return; + filter->vlan_id = cpu_to_be16(match.key->vlan_id); + } +#endif /* !HAVE_TC_FLOWER_VLAN_IN_TAGS */ - switch (vsi->type) { - case I40E_VSI_MAIN: - if (!vsi->netdev || !vsi->netdev_registered) - break; + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { + struct flow_match_control match; - if (link_up) { - netif_carrier_on(vsi->netdev); - netif_tx_wake_all_queues(vsi->netdev); - } else { - netif_carrier_off(vsi->netdev); - netif_tx_stop_all_queues(vsi->netdev); + flow_rule_match_control(rule, &match); + addr_type = match.key->addr_type; + } + + if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { + struct flow_match_ipv4_addrs match; + + flow_rule_match_ipv4_addrs(rule, &match); + if (match.mask->dst) { + if (match.mask->dst == cpu_to_be32(0xffffffff)) { + field_flags |= I40E_CLOUD_FIELD_IIP; + } else { + dev_err(&pf->pdev->dev, "Bad ip dst mask %pI4b\n", + &match.mask->dst); + return I40E_ERR_CONFIG; + } } - break; - case I40E_VSI_SRIOV: - case I40E_VSI_VMDQ2: - case I40E_VSI_CTRL: - case I40E_VSI_IWARP: - case I40E_VSI_MIRROR: - default: - /* there is no notification for other VSIs */ - break; + if (match.mask->src) { + if (match.mask->src == cpu_to_be32(0xffffffff)) { + field_flags |= I40E_CLOUD_FIELD_IIP; + } else { + dev_err(&pf->pdev->dev, "Bad ip src mask %pI4b\n", + &match.mask->src); + return I40E_ERR_CONFIG; + } + } + + if (field_flags & I40E_CLOUD_FIELD_TEN_ID) { + dev_err(&pf->pdev->dev, "Tenant id not allowed for ip filter\n"); + return I40E_ERR_CONFIG; + } + filter->dst_ipv4 = match.key->dst; + filter->src_ipv4 = match.key->src; + } + + if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { + struct flow_match_ipv6_addrs match; + + flow_rule_match_ipv6_addrs(rule, &match); + + /* src and dest IPV6 address should not be LOOPBACK + * (0:0:0:0:0:0:0:1), which can be represented as ::1 + */ + if (ipv6_addr_loopback(&match.key->dst) || + ipv6_addr_loopback(&match.key->src)) { + dev_err(&pf->pdev->dev, + "Bad ipv6, addr is LOOPBACK\n"); + return I40E_ERR_CONFIG; + } + if (!ipv6_addr_any(&match.mask->dst) || + !ipv6_addr_any(&match.mask->src)) + field_flags |= I40E_CLOUD_FIELD_IIP; + + memcpy(&filter->src_ipv6, &match.key->src.s6_addr32, + sizeof(filter->src_ipv6)); + memcpy(&filter->dst_ipv6, &match.key->dst.s6_addr32, + sizeof(filter->dst_ipv6)); + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { + struct flow_match_ports match; + + flow_rule_match_ports(rule, &match); + if (match.mask->src) { + if (match.mask->src == cpu_to_be16(0xffff)) { + field_flags |= I40E_CLOUD_FIELD_IIP; + } else { + dev_err(&pf->pdev->dev, "Bad src port mask 0x%04x\n", + be16_to_cpu(match.mask->src)); + return I40E_ERR_CONFIG; + } + } + + if (match.mask->dst) { + if (match.mask->dst == cpu_to_be16(0xffff)) { + field_flags |= I40E_CLOUD_FIELD_IIP; + } else { + dev_err(&pf->pdev->dev, "Bad dst port mask 0x%04x\n", + be16_to_cpu(match.mask->dst)); + return I40E_ERR_CONFIG; + } + } + + filter->dst_port = match.key->dst; + filter->src_port = match.key->src; + + switch (filter->ip_proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + break; + default: + dev_err(&pf->pdev->dev, + "Only UDP and TCP transport are supported\n"); + return -EINVAL; + } } + filter->flags = field_flags; + return 0; } /** - * i40e_veb_link_event - notify elements on the veb of a link event - * @veb: veb to be notified - * @link_up: link up or down + * i40e_handle_tclass: Forward to a traffic class on the device + * @vsi: Pointer to VSI + * @tc: traffic class index on the device + * @filter: Pointer to cloud filter structure + * **/ -static void i40e_veb_link_event(struct i40e_veb *veb, bool link_up) +static int i40e_handle_tclass(struct i40e_vsi *vsi, u32 tc, + struct i40e_cloud_filter *filter) { - struct i40e_pf *pf; - int i; - - if (!veb || !veb->pf) - return; - pf = veb->pf; - - /* depth first... */ - for (i = 0; i < I40E_MAX_VEB; i++) - if (pf->veb[i] && (pf->veb[i]->uplink_seid == veb->seid)) - i40e_veb_link_event(pf->veb[i], link_up); + struct i40e_channel *ch, *ch_tmp; - /* ... now the local VSIs */ - for (i = 0; i < pf->num_alloc_vsi; i++) - if (pf->vsi[i] && (pf->vsi[i]->uplink_seid == veb->seid)) - i40e_vsi_link_event(pf->vsi[i], link_up); + /* direct to a traffic class on the same device */ + if (tc == 0) { + filter->seid = vsi->seid; + return 0; + } else if (vsi->tc_config.enabled_tc & BIT(tc)) { + if (!filter->dst_port) { + dev_err(&vsi->back->pdev->dev, + "Specify destination port to direct to traffic class that is not default\n"); + return -EINVAL; + } + if (list_empty(&vsi->ch_list)) + return -EINVAL; + list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list, + list) { + if (ch->seid == vsi->tc_seid_map[tc]) + filter->seid = ch->seid; + } + return 0; + } + dev_err(&vsi->back->pdev->dev, "TC is not enabled\n"); + return -EINVAL; } /** - * i40e_link_event - Update netif_carrier status - * @pf: board private structure + * i40e_configure_clsflower - Configure tc flower filters + * @vsi: Pointer to VSI + * @cls_flower: Pointer to struct flow_cls_offload + * **/ -static void i40e_link_event(struct i40e_pf *pf) +static int i40e_configure_clsflower(struct i40e_vsi *vsi, + struct flow_cls_offload *cls_flower) { - struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; - u8 new_link_speed, old_link_speed; - i40e_status status; - bool new_link, old_link; - - /* save off old link status information */ - pf->hw.phy.link_info_old = pf->hw.phy.link_info; + int tc = tc_classid_to_hwtc(vsi->netdev, cls_flower->classid); + struct i40e_cloud_filter *filter = NULL; + struct i40e_pf *pf = vsi->back; + int err = 0; - /* set this to force the get_link_status call to refresh state */ - pf->hw.phy.get_link_info = true; + if (tc < 0) { + dev_err(&vsi->back->pdev->dev, "Invalid traffic class\n"); + return -EINVAL; + } - old_link = (pf->hw.phy.link_info_old.link_info & I40E_AQ_LINK_UP); + if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) || + test_bit(__I40E_RESET_INTR_RECEIVED, pf->state)) + return -EBUSY; - status = i40e_get_link_status(&pf->hw, &new_link); + if (pf->fdir_pf_active_filters || + (!hlist_empty(&pf->fdir_filter_list))) { + dev_err(&vsi->back->pdev->dev, + "Flow Director Sideband filters exists, turn ntuple off to configure cloud filters\n"); + return -EOPNOTSUPP; + } - /* On success, disable temp link polling */ - if (status == I40E_SUCCESS) { - if (pf->flags & I40E_FLAG_TEMP_LINK_POLLING) - pf->flags &= ~I40E_FLAG_TEMP_LINK_POLLING; - } else { - /* Enable link polling temporarily until i40e_get_link_status - * returns I40E_SUCCESS - */ - pf->flags |= I40E_FLAG_TEMP_LINK_POLLING; - dev_dbg(&pf->pdev->dev, "couldn't get link state, status: %d\n", - status); - return; + if (vsi->back->flags & I40E_FLAG_FD_SB_ENABLED) { + dev_err(&vsi->back->pdev->dev, + "Disable Flow Director Sideband, configuring Cloud filters via tc-flower\n"); + vsi->back->flags &= ~I40E_FLAG_FD_SB_ENABLED; + vsi->back->flags |= I40E_FLAG_FD_SB_TO_CLOUD_FILTER; } - old_link_speed = pf->hw.phy.link_info_old.link_speed; - new_link_speed = pf->hw.phy.link_info.link_speed; + filter = kzalloc(sizeof(*filter), GFP_KERNEL); + if (!filter) + return -ENOMEM; - if (new_link == old_link && - new_link_speed == old_link_speed && - (test_bit(__I40E_VSI_DOWN, vsi->state) || - new_link == netif_carrier_ok(vsi->netdev))) - return; + filter->cookie = cls_flower->cookie; - if (!test_bit(__I40E_VSI_DOWN, vsi->state)) - i40e_print_link_message(vsi, new_link); + err = i40e_parse_cls_flower(vsi, cls_flower, filter); + if (err < 0) + goto err; - /* Notify the base of the switch tree connected to - * the link. Floating VEBs are not notified. - */ - if (pf->lan_veb != I40E_NO_VEB && pf->veb[pf->lan_veb]) - i40e_veb_link_event(pf->veb[pf->lan_veb], new_link); + err = i40e_handle_tclass(vsi, tc, filter); + if (err < 0) + goto err; + + /* Add cloud filter */ + if (filter->dst_port) + err = i40e_add_del_cloud_filter_big_buf(vsi, filter, true); else - i40e_vsi_link_event(vsi, new_link); + err = i40e_add_del_cloud_filter(vsi, filter, true); - if (pf->vf) - i40e_vc_notify_link_state(pf); + if (err) { + dev_err(&pf->pdev->dev, + "Failed to add cloud filter, err %s\n", + i40e_stat_str(&pf->hw, err)); + goto err; + } - if (pf->flags & I40E_FLAG_PTP) - i40e_ptp_set_increment(pf); + /* add filter to the ordered list */ + INIT_HLIST_NODE(&filter->cloud_node); + + hlist_add_head(&filter->cloud_node, &pf->cloud_filter_list); + + pf->num_cloud_filters++; + + return err; +err: + kfree(filter); + return err; } /** - * i40e_watchdog_subtask - periodic checks not using event driven response - * @pf: board private structure + * i40e_find_cloud_filter - Find the could filter in the list + * @vsi: Pointer to VSI + * @cookie: filter specific cookie + * **/ -static void i40e_watchdog_subtask(struct i40e_pf *pf) +static struct i40e_cloud_filter *i40e_find_cloud_filter(struct i40e_vsi *vsi, + unsigned long *cookie) { - int i; + struct i40e_cloud_filter *filter = NULL; + struct hlist_node *node2; - /* if interface is down do nothing */ - if (test_bit(__I40E_DOWN, pf->state) || - test_bit(__I40E_CONFIG_BUSY, pf->state)) - return; + hlist_for_each_entry_safe(filter, node2, + &vsi->back->cloud_filter_list, cloud_node) + if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie))) + return filter; + return NULL; +} - /* make sure we don't do these things too often */ - if (time_before(jiffies, (pf->service_timer_previous + - pf->service_timer_period))) - return; - pf->service_timer_previous = jiffies; +/** + * i40e_delete_clsflower - Remove tc flower filters + * @vsi: Pointer to VSI + * @cls_flower: Pointer to struct flow_cls_offload + * + **/ +static int i40e_delete_clsflower(struct i40e_vsi *vsi, + struct flow_cls_offload *cls_flower) +{ + struct i40e_cloud_filter *filter = NULL; + struct i40e_pf *pf = vsi->back; + int err = 0; - if ((pf->flags & I40E_FLAG_LINK_POLLING_ENABLED) || - (pf->flags & I40E_FLAG_TEMP_LINK_POLLING)) - i40e_link_event(pf); + filter = i40e_find_cloud_filter(vsi, &cls_flower->cookie); - /* Update the stats for active netdevs so the network stack - * can look at updated numbers whenever it cares to - */ - for (i = 0; i < pf->num_alloc_vsi; i++) - if (pf->vsi[i] && pf->vsi[i]->netdev) - i40e_update_stats(pf->vsi[i]); + if (!filter) + return -EINVAL; - if (pf->flags & I40E_FLAG_VEB_STATS_ENABLED) { - /* Update the stats for the active switching components */ - for (i = 0; i < I40E_MAX_VEB; i++) - if (pf->veb[i]) - i40e_update_veb_stats(pf->veb[i]); + hash_del(&filter->cloud_node); + + if (filter->dst_port) + err = i40e_add_del_cloud_filter_big_buf(vsi, filter, false); + else + err = i40e_add_del_cloud_filter(vsi, filter, false); + + kfree(filter); + if (err) { + dev_err(&pf->pdev->dev, + "Failed to delete cloud filter, err %s\n", + i40e_stat_str(&pf->hw, err)); + return i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status); } - i40e_ptp_rx_hang(pf); - i40e_ptp_tx_hang(pf); + pf->num_cloud_filters--; + if (!pf->num_cloud_filters) + if ((pf->flags & I40E_FLAG_FD_SB_TO_CLOUD_FILTER) && + !(pf->flags & I40E_FLAG_FD_SB_INACTIVE)) { + pf->flags |= I40E_FLAG_FD_SB_ENABLED; + pf->flags &= ~I40E_FLAG_FD_SB_TO_CLOUD_FILTER; + pf->flags &= ~I40E_FLAG_FD_SB_INACTIVE; + } + return 0; } /** - * i40e_reset_subtask - Set up for resetting the device and driver - * @pf: board private structure + * i40e_setup_tc_cls_flower - flower classifier offloads + * @np: net device to configure + * @cls_flower: pointer to cls flower offload structure **/ -static void i40e_reset_subtask(struct i40e_pf *pf) +static int i40e_setup_tc_cls_flower(struct i40e_netdev_priv *np, + struct flow_cls_offload *cls_flower) { - u32 reset_flags = 0; + struct i40e_vsi *vsi = np->vsi; - if (test_bit(__I40E_REINIT_REQUESTED, pf->state)) { - reset_flags |= BIT(__I40E_REINIT_REQUESTED); - clear_bit(__I40E_REINIT_REQUESTED, pf->state); - } - if (test_bit(__I40E_PF_RESET_REQUESTED, pf->state)) { - reset_flags |= BIT(__I40E_PF_RESET_REQUESTED); - clear_bit(__I40E_PF_RESET_REQUESTED, pf->state); - } - if (test_bit(__I40E_CORE_RESET_REQUESTED, pf->state)) { - reset_flags |= BIT(__I40E_CORE_RESET_REQUESTED); - clear_bit(__I40E_CORE_RESET_REQUESTED, pf->state); - } - if (test_bit(__I40E_GLOBAL_RESET_REQUESTED, pf->state)) { - reset_flags |= BIT(__I40E_GLOBAL_RESET_REQUESTED); - clear_bit(__I40E_GLOBAL_RESET_REQUESTED, pf->state); - } - if (test_bit(__I40E_DOWN_REQUESTED, pf->state)) { - reset_flags |= BIT(__I40E_DOWN_REQUESTED); - clear_bit(__I40E_DOWN_REQUESTED, pf->state); - } + if (cls_flower->common.chain_index) + return -EOPNOTSUPP; - /* If there's a recovery already waiting, it takes - * precedence before starting a new reset sequence. - */ - if (test_bit(__I40E_RESET_INTR_RECEIVED, pf->state)) { - i40e_prep_for_reset(pf, false); - i40e_reset(pf); - i40e_rebuild(pf, false, false); + switch (cls_flower->command) { + case FLOW_CLS_REPLACE: + return i40e_configure_clsflower(vsi, cls_flower); + case FLOW_CLS_DESTROY: + return i40e_delete_clsflower(vsi, cls_flower); + case FLOW_CLS_STATS: + return -EOPNOTSUPP; + default: + return -EOPNOTSUPP; } +} - /* If we're already down or resetting, just bail */ - if (reset_flags && - !test_bit(__I40E_DOWN, pf->state) && - !test_bit(__I40E_CONFIG_BUSY, pf->state)) { - i40e_do_reset(pf, reset_flags, false); +static int i40e_setup_tc_block_cb(enum tc_setup_type type, void *type_data, + void *cb_priv) +{ + struct i40e_netdev_priv *np = cb_priv; + + switch (type) { + case TC_SETUP_CLSFLOWER: + return i40e_setup_tc_cls_flower(np, type_data); + + default: + return -EOPNOTSUPP; } } -/** - * i40e_handle_link_event - Handle link event - * @pf: board private structure - * @e: event info posted on ARQ - **/ -static void i40e_handle_link_event(struct i40e_pf *pf, - struct i40e_arq_event_info *e) +static LIST_HEAD(i40e_block_cb_list); +#endif + +#ifdef NETIF_F_HW_TC +#ifdef HAVE_NDO_SETUP_TC_REMOVE_TC_TO_NETDEV +static int __i40e_setup_tc(struct net_device *netdev, enum tc_setup_type type, + void *type_data) +#elif defined(HAVE_NDO_SETUP_TC_CHAIN_INDEX) +static int __i40e_setup_tc(struct net_device *netdev, u32 handle, + u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) +#else +static int __i40e_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, + struct tc_to_netdev *tc) +#endif { - struct i40e_aqc_get_link_status *status = - (struct i40e_aqc_get_link_status *)&e->desc.params.raw; +#ifdef __TC_MQPRIO_MODE_MAX + struct i40e_netdev_priv *np = netdev_priv(netdev); +#endif +#ifdef HAVE_NDO_SETUP_TC_REMOVE_TC_TO_NETDEV +#ifndef __TC_MQPRIO_MODE_MAX + struct tc_mqprio_qopt *mqprio = type_data; +#endif +#else +#ifdef TC_MQPRIO_HW_OFFLOAD_MAX + struct tc_mqprio_qopt *mqprio = tc->mqprio; +#endif /* TC_MQPRIO_HW_OFFLOAD_MAX*/ + unsigned int type = tc->type; +#endif /* HAVE_NDO_SETUP_TC_REMOVE_TC_TO_NETDEV */ +#ifdef __TC_MQPRIO_MODE_MAX + switch (type) { + case TC_SETUP_QDISC_MQPRIO: + return i40e_setup_tc(netdev, type_data); + case TC_SETUP_BLOCK: + return flow_block_cb_setup_simple(type_data, + &i40e_block_cb_list, + i40e_setup_tc_block_cb, + np, np, true); + default: + return -EOPNOTSUPP; + } +#else + if (type != TC_SETUP_QDISC_MQPRIO) + return -EINVAL; - /* Do a new status request to re-enable LSE reporting - * and load new status information into the hw struct - * This completely ignores any state information - * in the ARQ event info, instead choosing to always - * issue the AQ update link status command. - */ - i40e_link_event(pf); +#ifdef TC_MQPRIO_HW_OFFLOAD_MAX + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - /* check for unqualified module, if link is down */ - if ((status->link_info & I40E_AQ_MEDIA_AVAILABLE) && - (!(status->an_info & I40E_AQ_QUALIFIED_MODULE)) && - (!(status->link_info & I40E_AQ_LINK_UP))) - dev_err(&pf->pdev->dev, - "The driver failed to link because an unqualified module was detected.\n"); + return i40e_setup_tc(netdev, mqprio->num_tc); +#else + return i40e_setup_tc(netdev, tc->tc); +#endif /* TC_MQPRIO_HW_OFFLOAD_MAX */ +#endif /* __TC_MQPRIO_MODE_MAX */ } +#endif /* NETIF_F_HW_TC */ +#endif /* HAVE_SETUP_TC */ /** - * i40e_clean_adminq_subtask - Clean the AdminQ rings - * @pf: board private structure + * i40e_open - Called when a network interface is made active + * @netdev: network interface device structure + * + * The open entry point is called when a network interface is made + * active by the system (IFF_UP). At this point all resources needed + * for transmit and receive operations are allocated, the interrupt + * handler is registered with the OS, the netdev watchdog subtask is + * enabled, and the stack is notified that the interface is ready. + * + * Returns 0 on success, negative value on failure **/ -static void i40e_clean_adminq_subtask(struct i40e_pf *pf) +int i40e_open(struct net_device *netdev) { - struct i40e_arq_event_info event; - struct i40e_hw *hw = &pf->hw; - u16 pending, i = 0; - i40e_status ret; - u16 opcode; - u32 oldval; - u32 val; - - /* Do not run clean AQ when PF reset fails */ - if (test_bit(__I40E_RESET_FAILED, pf->state)) - return; - - /* check for error indications */ - val = rd32(&pf->hw, pf->hw.aq.arq.len); - oldval = val; - if (val & I40E_PF_ARQLEN_ARQVFE_MASK) { - if (hw->debug_mask & I40E_DEBUG_AQ) - dev_info(&pf->pdev->dev, "ARQ VF Error detected\n"); - val &= ~I40E_PF_ARQLEN_ARQVFE_MASK; - } - if (val & I40E_PF_ARQLEN_ARQOVFL_MASK) { - if (hw->debug_mask & I40E_DEBUG_AQ) - dev_info(&pf->pdev->dev, "ARQ Overflow Error detected\n"); - val &= ~I40E_PF_ARQLEN_ARQOVFL_MASK; - pf->arq_overflows++; - } - if (val & I40E_PF_ARQLEN_ARQCRIT_MASK) { - if (hw->debug_mask & I40E_DEBUG_AQ) - dev_info(&pf->pdev->dev, "ARQ Critical Error detected\n"); - val &= ~I40E_PF_ARQLEN_ARQCRIT_MASK; - } - if (oldval != val) - wr32(&pf->hw, pf->hw.aq.arq.len, val); - - val = rd32(&pf->hw, pf->hw.aq.asq.len); - oldval = val; - if (val & I40E_PF_ATQLEN_ATQVFE_MASK) { - if (pf->hw.debug_mask & I40E_DEBUG_AQ) - dev_info(&pf->pdev->dev, "ASQ VF Error detected\n"); - val &= ~I40E_PF_ATQLEN_ATQVFE_MASK; - } - if (val & I40E_PF_ATQLEN_ATQOVFL_MASK) { - if (pf->hw.debug_mask & I40E_DEBUG_AQ) - dev_info(&pf->pdev->dev, "ASQ Overflow Error detected\n"); - val &= ~I40E_PF_ATQLEN_ATQOVFL_MASK; - } - if (val & I40E_PF_ATQLEN_ATQCRIT_MASK) { - if (pf->hw.debug_mask & I40E_DEBUG_AQ) - dev_info(&pf->pdev->dev, "ASQ Critical Error detected\n"); - val &= ~I40E_PF_ATQLEN_ATQCRIT_MASK; - } - if (oldval != val) - wr32(&pf->hw, pf->hw.aq.asq.len, val); - - event.buf_len = I40E_MAX_AQ_BUF_SIZE; - event.msg_buf = kzalloc(event.buf_len, GFP_KERNEL); - if (!event.msg_buf) - return; + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + int err; - do { - ret = i40e_clean_arq_element(hw, &event, &pending); - if (ret == I40E_ERR_ADMIN_QUEUE_NO_WORK) - break; - else if (ret) { - dev_info(&pf->pdev->dev, "ARQ event error %d\n", ret); - break; - } + /* disallow open during test or if eeprom is broken */ + if (test_bit(__I40E_TESTING, pf->state) || + test_bit(__I40E_BAD_EEPROM, pf->state)) + return -EBUSY; - opcode = le16_to_cpu(event.desc.opcode); - switch (opcode) { + netif_carrier_off(netdev); - case i40e_aqc_opc_get_link_status: - i40e_handle_link_event(pf, &event); - break; - case i40e_aqc_opc_send_msg_to_pf: - ret = i40e_vc_process_vf_msg(pf, - le16_to_cpu(event.desc.retval), - le32_to_cpu(event.desc.cookie_high), - le32_to_cpu(event.desc.cookie_low), - event.msg_buf, - event.msg_len); - break; - case i40e_aqc_opc_lldp_update_mib: - dev_dbg(&pf->pdev->dev, "ARQ: Update LLDP MIB event received\n"); -#ifdef CONFIG_I40E_DCB - rtnl_lock(); - ret = i40e_handle_lldp_event(pf, &event); - rtnl_unlock(); -#endif /* CONFIG_I40E_DCB */ - break; - case i40e_aqc_opc_event_lan_overflow: - dev_dbg(&pf->pdev->dev, "ARQ LAN queue overflow event received\n"); - i40e_handle_lan_overflow_event(pf, &event); - break; - case i40e_aqc_opc_send_msg_to_peer: - dev_info(&pf->pdev->dev, "ARQ: Msg from other pf\n"); - break; - case i40e_aqc_opc_nvm_erase: - case i40e_aqc_opc_nvm_update: - case i40e_aqc_opc_oem_post_update: - i40e_debug(&pf->hw, I40E_DEBUG_NVM, - "ARQ NVM operation 0x%04x completed\n", - opcode); - break; - default: - dev_info(&pf->pdev->dev, - "ARQ: Unknown event 0x%04x ignored\n", - opcode); - break; - } - } while (i++ < pf->adminq_work_limit); + if (i40e_force_link_state(pf, true)) + return -EAGAIN; - if (i < pf->adminq_work_limit) - clear_bit(__I40E_ADMINQ_EVENT_PENDING, pf->state); + err = i40e_vsi_open(vsi); + if (err) + return err; + /* configure global TSO hardware offload settings */ + wr32(&pf->hw, I40E_GLLAN_TSOMSK_F, be32_to_cpu(TCP_FLAG_PSH | + TCP_FLAG_FIN) >> 16); + wr32(&pf->hw, I40E_GLLAN_TSOMSK_M, be32_to_cpu(TCP_FLAG_PSH | + TCP_FLAG_FIN | + TCP_FLAG_CWR) >> 16); + wr32(&pf->hw, I40E_GLLAN_TSOMSK_L, be32_to_cpu(TCP_FLAG_CWR) >> 16); - /* re-enable Admin queue interrupt cause */ - val = rd32(hw, I40E_PFINT_ICR0_ENA); - val |= I40E_PFINT_ICR0_ENA_ADMINQ_MASK; - wr32(hw, I40E_PFINT_ICR0_ENA, val); - i40e_flush(hw); +#ifdef HAVE_VXLAN_RX_OFFLOAD +#if IS_ENABLED(CONFIG_VXLAN) + vxlan_get_rx_port(netdev); +#endif +#endif /* HAVE_VXLAN_RX_OFFLOAD */ +#ifdef HAVE_GENEVE_RX_OFFLOAD +#if IS_ENABLED(CONFIG_GENEVE) + if (pf->hw_features & I40E_HW_GENEVE_OFFLOAD_CAPABLE) + geneve_get_rx_port(netdev); +#endif +#endif /* HAVE_GENEVE_RX_OFFLOAD */ +#ifdef HAVE_UDP_ENC_RX_OFFLOAD + udp_tunnel_get_rx_info(netdev); +#endif /* HAVE_UDP_ENC_RX_OFFLOAD */ - kfree(event.msg_buf); + return 0; } /** - * i40e_verify_eeprom - make sure eeprom is good to use - * @pf: board private structure + * i40e_vsi_open - + * @vsi: the VSI to open + * + * Finish initialization of the VSI. + * + * Returns 0 on success, negative value on failure + * + * Note: expects to be called while under rtnl_lock() **/ -static void i40e_verify_eeprom(struct i40e_pf *pf) +int i40e_vsi_open(struct i40e_vsi *vsi) { + struct i40e_pf *pf = vsi->back; + char int_name[I40E_INT_NAME_STR_LEN]; int err; + int i; + u8 enabled_tc = 0; - err = i40e_diag_eeprom_test(&pf->hw); - if (err) { - /* retry in case of garbage read */ - err = i40e_diag_eeprom_test(&pf->hw); - if (err) { - dev_info(&pf->pdev->dev, "eeprom check failed (%d), Tx/Rx traffic disabled\n", - err); - set_bit(__I40E_BAD_EEPROM, pf->state); - } - } + /* allocate descriptors */ + err = i40e_vsi_setup_tx_resources(vsi); + if (err) + goto err_setup_tx; + err = i40e_vsi_setup_rx_resources(vsi); + if (err) + goto err_setup_rx; - if (!err && test_bit(__I40E_BAD_EEPROM, pf->state)) { - dev_info(&pf->pdev->dev, "eeprom check passed, Tx/Rx traffic enabled\n"); - clear_bit(__I40E_BAD_EEPROM, pf->state); - } -} + err = i40e_vsi_configure(vsi); + if (err) + goto err_setup_rx; -/** - * i40e_enable_pf_switch_lb - * @pf: pointer to the PF structure - * - * enable switch loop back or die - no point in a return value + if (vsi->netdev) { + snprintf(int_name, sizeof(int_name) - 1, "%s-%s", + dev_driver_string(&pf->pdev->dev), vsi->netdev->name); + err = i40e_vsi_request_irq(vsi, int_name); + if (err) + goto err_setup_rx; + + /* If real_num_tx_queues is changed the tc mapping may no + * longer be valid. Run tc reset function with new value + * of queues. + */ +#ifdef __TC_MQPRIO_MODE_MAX + for (i = 0; i < vsi->mqprio_qopt.qopt.num_tc; i++) +#else + for (i = 0; i < vsi->tc_config.numtc; i++) +#endif + enabled_tc |= BIT(i); + if (!enabled_tc) + netdev_reset_tc(vsi->netdev); + + /* Notify the stack of the actual queue counts. */ + err = netif_set_real_num_tx_queues(vsi->netdev, + vsi->num_queue_pairs); + if (err) + goto err_set_queues; + + /* When reducing the number of Tx queues, any pre-existing + * skbuffs might target a now removed queue. Older versions of + * the Linux kernel do not check for this, and it can result + * in a kernel panic. Avoid this by flushing all skbs now, so + * that we avoid attempting to transmit one that has an + * invalid queue mapping. + */ + qdisc_reset_all_tx(vsi->netdev); + + err = netif_set_real_num_rx_queues(vsi->netdev, + vsi->num_queue_pairs); + if (err) + goto err_set_queues; + + } else if (vsi->type == I40E_VSI_FDIR) { + snprintf(int_name, sizeof(int_name) - 1, "%s-%s:fdir", + dev_driver_string(&pf->pdev->dev), + dev_name(&pf->pdev->dev)); + err = i40e_vsi_request_irq(vsi, int_name); + + } else { + err = -EINVAL; + goto err_setup_rx; + } + + err = i40e_up_complete(vsi); + if (err) + goto err_up_complete; + + return 0; + +err_up_complete: + i40e_down(vsi); +err_set_queues: + i40e_vsi_free_irq(vsi); +err_setup_rx: + i40e_vsi_free_rx_resources(vsi); +err_setup_tx: + i40e_vsi_free_tx_resources(vsi); + if (vsi == pf->vsi[pf->lan_vsi]) + i40e_do_reset(pf, I40E_PF_RESET_FLAG, true); + + return err; +} + +/** + * i40e_fdir_filter_exit - Cleans up the Flow Director accounting + * @pf: Pointer to PF + * + * This function destroys the hlist where all the Flow Director + * filters were saved. **/ -static void i40e_enable_pf_switch_lb(struct i40e_pf *pf) +static void i40e_fdir_filter_exit(struct i40e_pf *pf) { - struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; - struct i40e_vsi_context ctxt; - int ret; + struct i40e_fdir_filter *filter; + struct i40e_flex_pit *pit_entry, *tmp; + struct hlist_node *node2; - ctxt.seid = pf->main_vsi_seid; - ctxt.pf_num = pf->hw.pf_id; - ctxt.vf_num = 0; - ret = i40e_aq_get_vsi_params(&pf->hw, &ctxt, NULL); - if (ret) { - dev_info(&pf->pdev->dev, - "couldn't get PF vsi config, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - return; + hlist_for_each_entry_safe(filter, node2, + &pf->fdir_filter_list, fdir_node) { + hlist_del(&filter->fdir_node); + kfree(filter); } - ctxt.flags = I40E_AQ_VSI_TYPE_PF; - ctxt.info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID); - ctxt.info.switch_id |= cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB); - ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL); - if (ret) { - dev_info(&pf->pdev->dev, - "update vsi switch failed, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + list_for_each_entry_safe(pit_entry, tmp, &pf->l3_flex_pit_list, list) { + list_del(&pit_entry->list); + kfree(pit_entry); + } + INIT_LIST_HEAD(&pf->l3_flex_pit_list); + + list_for_each_entry_safe(pit_entry, tmp, &pf->l4_flex_pit_list, list) { + list_del(&pit_entry->list); + kfree(pit_entry); } + INIT_LIST_HEAD(&pf->l4_flex_pit_list); + + pf->fdir_pf_active_filters = 0; + pf->fd_tcp4_filter_cnt = 0; + pf->fd_udp4_filter_cnt = 0; + pf->fd_sctp4_filter_cnt = 0; + pf->fd_ip4_filter_cnt = 0; + + /* Reprogram the default input set for TCP/IPv4 */ + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_TCP, + I40E_L3_SRC_MASK | I40E_L3_DST_MASK | + I40E_L4_SRC_MASK | I40E_L4_DST_MASK); + + /* Reprogram the default input set for UDP/IPv4 */ + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_UDP, + I40E_L3_SRC_MASK | I40E_L3_DST_MASK | + I40E_L4_SRC_MASK | I40E_L4_DST_MASK); + + /* Reprogram the default input set for SCTP/IPv4 */ + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_SCTP, + I40E_L3_SRC_MASK | I40E_L3_DST_MASK | + I40E_L4_SRC_MASK | I40E_L4_DST_MASK); + + /* Reprogram the default input set for Other/IPv4 */ + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_OTHER, + I40E_L3_SRC_MASK | I40E_L3_DST_MASK); + + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_FRAG_IPV4, + I40E_L3_SRC_MASK | I40E_L3_DST_MASK); } /** - * i40e_disable_pf_switch_lb - * @pf: pointer to the PF structure + * i40e_cloud_filter_exit - Cleans up Cloud Filters + * @pf: Pointer to PF * - * disable switch loop back or die - no point in a return value + * This function destroys the hlist which keeps all the Cloud Filters. **/ -static void i40e_disable_pf_switch_lb(struct i40e_pf *pf) +static void i40e_cloud_filter_exit(struct i40e_pf *pf) { - struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; - struct i40e_vsi_context ctxt; - int ret; + struct i40e_cloud_filter *cfilter; + struct hlist_node *node; - ctxt.seid = pf->main_vsi_seid; - ctxt.pf_num = pf->hw.pf_id; - ctxt.vf_num = 0; - ret = i40e_aq_get_vsi_params(&pf->hw, &ctxt, NULL); - if (ret) { - dev_info(&pf->pdev->dev, - "couldn't get PF vsi config, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - return; + hlist_for_each_entry_safe(cfilter, node, + &pf->cloud_filter_list, cloud_node) { + hlist_del(&cfilter->cloud_node); + kfree(cfilter); } - ctxt.flags = I40E_AQ_VSI_TYPE_PF; - ctxt.info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID); - ctxt.info.switch_id &= ~cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB); + pf->num_cloud_filters = 0; - ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL); - if (ret) { - dev_info(&pf->pdev->dev, - "update vsi switch failed, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + if ((pf->flags & I40E_FLAG_FD_SB_TO_CLOUD_FILTER) && + !(pf->flags & I40E_FLAG_FD_SB_INACTIVE)) { + pf->flags |= I40E_FLAG_FD_SB_ENABLED; + pf->flags &= ~I40E_FLAG_FD_SB_TO_CLOUD_FILTER; + pf->flags &= ~I40E_FLAG_FD_SB_INACTIVE; } } /** - * i40e_config_bridge_mode - Configure the HW bridge mode - * @veb: pointer to the bridge instance + * i40e_close - Disables a network interface + * @netdev: network interface device structure * - * Configure the loop back mode for the LAN VSI that is downlink to the - * specified HW bridge instance. It is expected this function is called - * when a new HW bridge is instantiated. + * The close entry point is called when an interface is de-activated + * by the OS. The hardware is still under the driver's control, but + * this netdev interface is disabled. + * + * Returns 0, this is not allowed to fail **/ -static void i40e_config_bridge_mode(struct i40e_veb *veb) +int i40e_close(struct net_device *netdev) { - struct i40e_pf *pf = veb->pf; + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; - if (pf->hw.debug_mask & I40E_DEBUG_LAN) - dev_info(&pf->pdev->dev, "enabling bridge mode: %s\n", - veb->bridge_mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); - if (veb->bridge_mode & BRIDGE_MODE_VEPA) - i40e_disable_pf_switch_lb(pf); - else - i40e_enable_pf_switch_lb(pf); + i40e_vsi_close(vsi); + + return 0; } /** - * i40e_reconstitute_veb - rebuild the VEB and anything connected to it - * @veb: pointer to the VEB instance + * i40e_do_reset - Start a PF or Core Reset sequence + * @pf: board private structure + * @reset_flags: which reset is requested + * @lock_acquired: indicates whether or not the lock has been acquired + * before this function was called. * - * This is a recursive function that first builds the attached VSIs then - * recurses in to build the next layer of VEB. We track the connections - * through our own index numbers because the seid's from the HW could - * change across the reset. + * The essential difference in resets is that the PF Reset + * doesn't clear the packet buffers, doesn't reset the PE + * firmware, and doesn't bother the other PFs on the chip. **/ -static int i40e_reconstitute_veb(struct i40e_veb *veb) +void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags, bool lock_acquired) { - struct i40e_vsi *ctl_vsi = NULL; - struct i40e_pf *pf = veb->pf; - int v, veb_idx; - int ret; + u32 val; - /* build VSI that owns this VEB, temporarily attached to base VEB */ - for (v = 0; v < pf->num_alloc_vsi && !ctl_vsi; v++) { - if (pf->vsi[v] && - pf->vsi[v]->veb_idx == veb->idx && - pf->vsi[v]->flags & I40E_VSI_FLAG_VEB_OWNER) { - ctl_vsi = pf->vsi[v]; - break; - } - } - if (!ctl_vsi) { - dev_info(&pf->pdev->dev, - "missing owner VSI for veb_idx %d\n", veb->idx); - ret = -ENOENT; - goto end_reconstitute; - } - if (ctl_vsi != pf->vsi[pf->lan_vsi]) - ctl_vsi->uplink_seid = pf->vsi[pf->lan_vsi]->uplink_seid; - ret = i40e_add_vsi(ctl_vsi); - if (ret) { - dev_info(&pf->pdev->dev, - "rebuild of veb_idx %d owner VSI failed: %d\n", - veb->idx, ret); - goto end_reconstitute; - } - i40e_vsi_reset_stats(ctl_vsi); + WARN_ON(in_interrupt()); - /* create the VEB in the switch and move the VSI onto the VEB */ - ret = i40e_add_veb(veb, ctl_vsi); - if (ret) - goto end_reconstitute; - if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED) - veb->bridge_mode = BRIDGE_MODE_VEB; - else - veb->bridge_mode = BRIDGE_MODE_VEPA; - i40e_config_bridge_mode(veb); + /* do the biggest reset indicated */ + if (reset_flags & BIT_ULL(__I40E_GLOBAL_RESET_REQUESTED)) { - /* create the remaining VSIs attached to this VEB */ - for (v = 0; v < pf->num_alloc_vsi; v++) { - if (!pf->vsi[v] || pf->vsi[v] == ctl_vsi) - continue; + /* Request a Global Reset + * + * This will start the chip's countdown to the actual full + * chip reset event, and a warning interrupt to be sent + * to all PFs, including the requestor. Our handler + * for the warning interrupt will deal with the shutdown + * and recovery of the switch setup. + */ + dev_dbg(&pf->pdev->dev, "GlobalR requested\n"); + val = rd32(&pf->hw, I40E_GLGEN_RTRIG); + val |= I40E_GLGEN_RTRIG_GLOBR_MASK; + wr32(&pf->hw, I40E_GLGEN_RTRIG, val); - if (pf->vsi[v]->veb_idx == veb->idx) { - struct i40e_vsi *vsi = pf->vsi[v]; + } else if (reset_flags & BIT_ULL(__I40E_CORE_RESET_REQUESTED)) { - vsi->uplink_seid = veb->seid; - ret = i40e_add_vsi(vsi); - if (ret) { - dev_info(&pf->pdev->dev, - "rebuild of vsi_idx %d failed: %d\n", - v, ret); - goto end_reconstitute; - } - i40e_vsi_reset_stats(vsi); - } - } + /* Request a Core Reset + * + * Same as Global Reset, except does *not* include the MAC/PHY + */ + dev_dbg(&pf->pdev->dev, "CoreR requested\n"); + val = rd32(&pf->hw, I40E_GLGEN_RTRIG); + val |= I40E_GLGEN_RTRIG_CORER_MASK; + wr32(&pf->hw, I40E_GLGEN_RTRIG, val); + i40e_flush(&pf->hw); - /* create any VEBs attached to this VEB - RECURSION */ - for (veb_idx = 0; veb_idx < I40E_MAX_VEB; veb_idx++) { - if (pf->veb[veb_idx] && pf->veb[veb_idx]->veb_idx == veb->idx) { - pf->veb[veb_idx]->uplink_seid = veb->seid; - ret = i40e_reconstitute_veb(pf->veb[veb_idx]); - if (ret) - break; - } - } + } else if (reset_flags & I40E_PF_RESET_FLAG) { -end_reconstitute: - return ret; -} + /* Request a PF Reset + * + * Resets only the PF-specific registers + * + * This goes directly to the tear-down and rebuild of + * the switch, since we need to do all the recovery as + * for the Core Reset. + */ + dev_dbg(&pf->pdev->dev, "PFR requested\n"); + i40e_handle_reset_warning(pf, lock_acquired); -/** - * i40e_get_capabilities - get info about the HW - * @pf: the PF struct - **/ -static int i40e_get_capabilities(struct i40e_pf *pf) -{ - struct i40e_aqc_list_capabilities_element_resp *cap_buf; - u16 data_size; - int buf_len; - int err; + dev_info(&pf->pdev->dev, + pf->flags & I40E_FLAG_DISABLE_FW_LLDP ? + "FW LLDP is disabled\n" : + "FW LLDP is enabled\n"); - buf_len = 40 * sizeof(struct i40e_aqc_list_capabilities_element_resp); - do { - cap_buf = kzalloc(buf_len, GFP_KERNEL); - if (!cap_buf) - return -ENOMEM; + } else if (reset_flags & BIT_ULL(__I40E_REINIT_REQUESTED)) { + int v; - /* this loads the data into the hw struct for us */ - err = i40e_aq_discover_capabilities(&pf->hw, cap_buf, buf_len, - &data_size, - i40e_aqc_opc_list_func_capabilities, - NULL); - /* data loaded, buffer no longer needed */ - kfree(cap_buf); + /* Find the VSI(s) that requested a re-init */ + dev_info(&pf->pdev->dev, + "VSI reinit requested\n"); + for (v = 0; v < pf->num_alloc_vsi; v++) { + struct i40e_vsi *vsi = pf->vsi[v]; - if (pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOMEM) { - /* retry with a larger buffer */ - buf_len = data_size; - } else if (pf->hw.aq.asq_last_status != I40E_AQ_RC_OK) { - dev_info(&pf->pdev->dev, - "capability discovery failed, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, err), - i40e_aq_str(&pf->hw, - pf->hw.aq.asq_last_status)); - return -ENODEV; + if (vsi != NULL && + test_and_clear_bit(__I40E_VSI_REINIT_REQUESTED, + vsi->state)) + i40e_vsi_reinit_locked(pf->vsi[v]); } - } while (err); + } else if (reset_flags & BIT_ULL(__I40E_DOWN_REQUESTED)) { + int v; - if (pf->hw.debug_mask & I40E_DEBUG_USER) - dev_info(&pf->pdev->dev, - "pf=%d, num_vfs=%d, msix_pf=%d, msix_vf=%d, fd_g=%d, fd_b=%d, pf_max_q=%d num_vsi=%d\n", - pf->hw.pf_id, pf->hw.func_caps.num_vfs, - pf->hw.func_caps.num_msix_vectors, - pf->hw.func_caps.num_msix_vectors_vf, - pf->hw.func_caps.fd_filters_guaranteed, - pf->hw.func_caps.fd_filters_best_effort, - pf->hw.func_caps.num_tx_qp, - pf->hw.func_caps.num_vsis); - -#define DEF_NUM_VSI (1 + (pf->hw.func_caps.fcoe ? 1 : 0) \ - + pf->hw.func_caps.num_vfs) - if (pf->hw.revision_id == 0 && (DEF_NUM_VSI > pf->hw.func_caps.num_vsis)) { + /* Find the VSI(s) that needs to be brought down */ + dev_info(&pf->pdev->dev, "VSI down requested\n"); + for (v = 0; v < pf->num_alloc_vsi; v++) { + struct i40e_vsi *vsi = pf->vsi[v]; + + if (vsi != NULL && + test_and_clear_bit(__I40E_VSI_DOWN_REQUESTED, + vsi->state)) { + set_bit(__I40E_VSI_DOWN, vsi->state); + i40e_down(vsi); + } + } + } else { dev_info(&pf->pdev->dev, - "got num_vsis %d, setting num_vsis to %d\n", - pf->hw.func_caps.num_vsis, DEF_NUM_VSI); - pf->hw.func_caps.num_vsis = DEF_NUM_VSI; + "bad reset request 0x%08x\n", reset_flags); } - - return 0; } -static int i40e_vsi_clear(struct i40e_vsi *vsi); +/** + * i40e_do_reset_safe - Protected reset path for userland calls. + * @pf: board private structure + * @reset_flags: which reset is requested + * + **/ +void i40e_do_reset_safe(struct i40e_pf *pf, u32 reset_flags) +{ + rtnl_lock(); + i40e_do_reset(pf, reset_flags, true); + rtnl_unlock(); +} +#ifdef CONFIG_DCB /** - * i40e_fdir_sb_setup - initialize the Flow Director resources for Sideband + * i40e_dcb_need_reconfig - Check if DCB needs reconfig * @pf: board private structure + * @old_cfg: current DCB config + * @new_cfg: new DCB config **/ -static void i40e_fdir_sb_setup(struct i40e_pf *pf) +bool i40e_dcb_need_reconfig(struct i40e_pf *pf, + struct i40e_dcbx_config *old_cfg, + struct i40e_dcbx_config *new_cfg) { - struct i40e_vsi *vsi; + bool need_reconfig = false; - /* quick workaround for an NVM issue that leaves a critical register - * uninitialized - */ - if (!rd32(&pf->hw, I40E_GLQF_HKEY(0))) { - static const u32 hkey[] = { - 0xe640d33f, 0xcdfe98ab, 0x73fa7161, 0x0d7a7d36, - 0xeacb7d61, 0xaa4f05b6, 0x9c5c89ed, 0xfc425ddb, - 0xa4654832, 0xfc7461d4, 0x8f827619, 0xf5c63c21, - 0x95b3a76d}; - int i; + /* Check if ETS configuration has changed */ + if (memcmp(&new_cfg->etscfg, + &old_cfg->etscfg, + sizeof(new_cfg->etscfg))) { + /* If Priority Table has changed reconfig is needed */ + if (memcmp(&new_cfg->etscfg.prioritytable, + &old_cfg->etscfg.prioritytable, + sizeof(new_cfg->etscfg.prioritytable))) { + need_reconfig = true; + dev_dbg(&pf->pdev->dev, "ETS UP2TC changed.\n"); + } - for (i = 0; i <= I40E_GLQF_HKEY_MAX_INDEX; i++) - wr32(&pf->hw, I40E_GLQF_HKEY(i), hkey[i]); - } + if (memcmp(&new_cfg->etscfg.tcbwtable, + &old_cfg->etscfg.tcbwtable, + sizeof(new_cfg->etscfg.tcbwtable))) + dev_dbg(&pf->pdev->dev, "ETS TC BW Table changed.\n"); - if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED)) - return; + if (memcmp(&new_cfg->etscfg.tsatable, + &old_cfg->etscfg.tsatable, + sizeof(new_cfg->etscfg.tsatable))) + dev_dbg(&pf->pdev->dev, "ETS TSA Table changed.\n"); + } - /* find existing VSI and see if it needs configuring */ - vsi = i40e_find_vsi_by_type(pf, I40E_VSI_FDIR); + /* Check if PFC configuration has changed */ + if (memcmp(&new_cfg->pfc, + &old_cfg->pfc, + sizeof(new_cfg->pfc))) { + need_reconfig = true; + dev_dbg(&pf->pdev->dev, "PFC config change detected.\n"); + } - /* create a new VSI if none exists */ - if (!vsi) { - vsi = i40e_vsi_setup(pf, I40E_VSI_FDIR, - pf->vsi[pf->lan_vsi]->seid, 0); - if (!vsi) { - dev_info(&pf->pdev->dev, "Couldn't create FDir VSI\n"); - pf->flags &= ~I40E_FLAG_FD_SB_ENABLED; - return; - } + /* Check if APP Table has changed */ + if (memcmp(&new_cfg->app, + &old_cfg->app, + sizeof(new_cfg->app))) { + need_reconfig = true; + dev_dbg(&pf->pdev->dev, "APP Table change detected.\n"); } - i40e_vsi_setup_irqhandler(vsi, i40e_fdir_clean_ring); + dev_dbg(&pf->pdev->dev, "dcb need_reconfig=%d\n", need_reconfig); + return need_reconfig; } /** - * i40e_fdir_teardown - release the Flow Director resources + * i40e_handle_lldp_event - Handle LLDP Change MIB event * @pf: board private structure + * @e: event info posted on ARQ **/ -static void i40e_fdir_teardown(struct i40e_pf *pf) -{ - struct i40e_vsi *vsi; - - i40e_fdir_filter_exit(pf); - vsi = i40e_find_vsi_by_type(pf, I40E_VSI_FDIR); - if (vsi) - i40e_vsi_release(vsi); -} - -/** - * i40e_prep_for_reset - prep for the core to reset - * @pf: board private structure - * @lock_acquired: indicates whether or not the lock has been acquired - * before this function was called. - * - * Close up the VFs and other things in prep for PF Reset. - **/ -static void i40e_prep_for_reset(struct i40e_pf *pf, bool lock_acquired) +static int i40e_handle_lldp_event(struct i40e_pf *pf, + struct i40e_arq_event_info *e) { + struct i40e_aqc_lldp_get_mib *mib = + (struct i40e_aqc_lldp_get_mib *)&e->desc.params.raw; struct i40e_hw *hw = &pf->hw; - i40e_status ret = 0; - u32 v; + struct i40e_dcbx_config tmp_dcbx_cfg; + bool need_reconfig = false; + int ret = 0; + u8 type; - clear_bit(__I40E_RESET_INTR_RECEIVED, pf->state); - if (test_and_set_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) - return; - if (i40e_check_asq_alive(&pf->hw)) - i40e_vc_notify_reset(pf); + /* X710-T*L 2.5G and 5G speeds don't support DCB */ + if (I40E_IS_X710TL_DEVICE(hw->device_id) && + (hw->phy.link_info.link_speed & + ~(I40E_LINK_SPEED_2_5GB | I40E_LINK_SPEED_5GB)) && + !(pf->flags & I40E_FLAG_DCB_CAPABLE)) + /* let firmware decide if the DCB should be disabled */ + pf->flags |= I40E_FLAG_DCB_CAPABLE; - dev_dbg(&pf->pdev->dev, "Tearing down internal switch for reset\n"); + /* Not DCB capable or capability disabled */ + if (!(pf->flags & I40E_FLAG_DCB_CAPABLE)) + return ret; - /* quiesce the VSIs and their queues that are not already DOWN */ - /* pf_quiesce_all_vsi modifies netdev structures -rtnl_lock needed */ - if (!lock_acquired) - rtnl_lock(); - i40e_pf_quiesce_all_vsi(pf); - if (!lock_acquired) - rtnl_unlock(); + /* Ignore if event is not for Nearest Bridge */ + type = ((mib->type >> I40E_AQ_LLDP_BRIDGE_TYPE_SHIFT) + & I40E_AQ_LLDP_BRIDGE_TYPE_MASK); + dev_dbg(&pf->pdev->dev, "LLDP event mib bridge type 0x%x\n", type); + if (type != I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE) + return ret; - for (v = 0; v < pf->num_alloc_vsi; v++) { - if (pf->vsi[v]) - pf->vsi[v]->seid = 0; + /* Check MIB Type and return if event for Remote MIB update */ + type = mib->type & I40E_AQ_LLDP_MIB_TYPE_MASK; + dev_dbg(&pf->pdev->dev, + "LLDP event mib type %s\n", type ? "remote" : "local"); + if (type == I40E_AQ_LLDP_MIB_REMOTE) { + /* Update the remote cached instance and return */ + ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE, + I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE, + &hw->remote_dcbx_config); + goto exit; } - i40e_shutdown_adminq(&pf->hw); + /* Store the old configuration */ + tmp_dcbx_cfg = hw->local_dcbx_config; - /* call shutdown HMC */ - if (hw->hmc.hmc_obj) { - ret = i40e_shutdown_lan_hmc(hw); - if (ret) - dev_warn(&pf->pdev->dev, - "shutdown_lan_hmc failed: %d\n", ret); - } + /* Reset the old DCBx configuration data */ + memset(&hw->local_dcbx_config, 0, sizeof(hw->local_dcbx_config)); + /* Get updated DCBX data from firmware */ + ret = i40e_get_dcb_config(&pf->hw); + if (ret) { + /* X710-T*L 2.5G and 5G speeds don't support DCB */ + if (I40E_IS_X710TL_DEVICE(hw->device_id) && + (hw->phy.link_info.link_speed & + (I40E_LINK_SPEED_2_5GB | I40E_LINK_SPEED_5GB))) { + dev_warn(&pf->pdev->dev, + "DCB is not supported for X710-T*L 2.5/5G speeds\n"); + pf->flags &= ~I40E_FLAG_DCB_CAPABLE; + } else { + dev_info(&pf->pdev->dev, + "Failed querying DCB configuration data from firmware, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); + } + goto exit; + } + + /* No change detected in DCBX configs */ + if (!memcmp(&tmp_dcbx_cfg, &hw->local_dcbx_config, + sizeof(tmp_dcbx_cfg))) { + dev_dbg(&pf->pdev->dev, "No change detected in DCBX configuration.\n"); + goto exit; + } + + need_reconfig = i40e_dcb_need_reconfig(pf, &tmp_dcbx_cfg, + &hw->local_dcbx_config); + +#ifdef HAVE_DCBNL_IEEE + i40e_dcbnl_flush_apps(pf, &tmp_dcbx_cfg, &hw->local_dcbx_config); +#endif /* HAVE_DCBNL_IEEE */ + + if (!need_reconfig) + goto exit; + + /* Enable DCB tagging only when more than one TC */ + if (i40e_dcb_get_num_tc(&hw->local_dcbx_config) > 1) + pf->flags |= I40E_FLAG_DCB_ENABLED; + else + pf->flags &= ~I40E_FLAG_DCB_ENABLED; + + set_bit(__I40E_PORT_SUSPENDED, pf->state); + /* Reconfiguration needed quiesce all VSIs */ + i40e_pf_quiesce_all_vsi(pf); + + /* Changes in configuration update VEB/VSI */ + i40e_dcb_reconfigure(pf); + + ret = i40e_resume_port_tx(pf); + + clear_bit(__I40E_PORT_SUSPENDED, pf->state); + /* In case of error no point in resuming VSIs */ + if (ret) + goto exit; + + /* Wait for the PF's queues to be disabled */ + ret = i40e_pf_wait_queues_disabled(pf); + if (ret) { + /* Schedule PF reset to recover */ + set_bit(__I40E_PF_RESET_REQUESTED, pf->state); + i40e_service_event_schedule(pf); + } else { + i40e_pf_unquiesce_all_vsi(pf); + set_bit(__I40E_CLIENT_SERVICE_REQUESTED, pf->state); + set_bit(__I40E_CLIENT_L2_CHANGE, pf->state); + } + +exit: + return ret; } +#endif /* CONFIG_DCB */ /** - * i40e_send_version - update firmware with driver version - * @pf: PF struct - */ -static void i40e_send_version(struct i40e_pf *pf) + * i40e_handle_lan_overflow_event - Handler for LAN queue overflow event + * @pf: board private structure + * @e: event info posted on ARQ + * + * Handler for LAN Queue Overflow Event generated by the firmware for PF + * and VF queues + **/ +static void i40e_handle_lan_overflow_event(struct i40e_pf *pf, + struct i40e_arq_event_info *e) { - struct i40e_driver_version dv; + struct i40e_aqc_lan_overflow *data = + (struct i40e_aqc_lan_overflow *)&e->desc.params.raw; + u32 queue = le32_to_cpu(data->prtdcb_rupto); + u32 qtx_ctl = le32_to_cpu(data->otx_ctl); + struct i40e_hw *hw = &pf->hw; + struct i40e_vf *vf; + u16 vf_id; - dv.major_version = DRV_VERSION_MAJOR; - dv.minor_version = DRV_VERSION_MINOR; - dv.build_version = DRV_VERSION_BUILD; - dv.subbuild_version = 0; - strlcpy(dv.driver_string, DRV_VERSION, sizeof(dv.driver_string)); - i40e_aq_send_driver_version(&pf->hw, &dv, NULL); + dev_dbg(&pf->pdev->dev, "overflow Rx Queue Number = %d QTX_CTL=0x%08x\n", + queue, qtx_ctl); + + /* Queue belongs to VF, find the VF and issue VF reset */ + if (((qtx_ctl & I40E_QTX_CTL_PFVF_Q_MASK) + >> I40E_QTX_CTL_PFVF_Q_SHIFT) == I40E_QTX_CTL_VF_QUEUE) { + vf_id = (u16)((qtx_ctl & I40E_QTX_CTL_VFVM_INDX_MASK) + >> I40E_QTX_CTL_VFVM_INDX_SHIFT); + vf_id -= hw->func_caps.vf_base_id; + vf = &pf->vf[vf_id]; + i40e_vc_notify_vf_reset(vf); + /* Allow VF to process pending reset notification */ + msleep(20); + i40e_reset_vf(vf, false); + } } /** - * i40e_get_oem_version - get OEM specific version information - * @hw: pointer to the hardware structure + * i40e_get_cur_guaranteed_fd_count - Get the consumed guaranteed FD filters + * @pf: board private structure **/ -static void i40e_get_oem_version(struct i40e_hw *hw) +u32 i40e_get_cur_guaranteed_fd_count(struct i40e_pf *pf) { - u16 block_offset = 0xffff; - u16 block_length = 0; - u16 capabilities = 0; - u16 gen_snap = 0; - u16 release = 0; - -#define I40E_SR_NVM_OEM_VERSION_PTR 0x1B -#define I40E_NVM_OEM_LENGTH_OFFSET 0x00 -#define I40E_NVM_OEM_CAPABILITIES_OFFSET 0x01 -#define I40E_NVM_OEM_GEN_OFFSET 0x02 -#define I40E_NVM_OEM_RELEASE_OFFSET 0x03 -#define I40E_NVM_OEM_CAPABILITIES_MASK 0x000F -#define I40E_NVM_OEM_LENGTH 3 - - /* Check if pointer to OEM version block is valid. */ - i40e_read_nvm_word(hw, I40E_SR_NVM_OEM_VERSION_PTR, &block_offset); - if (block_offset == 0xffff) - return; + u32 val, fcnt_prog; - /* Check if OEM version block has correct length. */ - i40e_read_nvm_word(hw, block_offset + I40E_NVM_OEM_LENGTH_OFFSET, - &block_length); - if (block_length < I40E_NVM_OEM_LENGTH) - return; + val = rd32(&pf->hw, I40E_PFQF_FDSTAT); + fcnt_prog = (val & I40E_PFQF_FDSTAT_GUARANT_CNT_MASK); + return fcnt_prog; +} - /* Check if OEM version format is as expected. */ - i40e_read_nvm_word(hw, block_offset + I40E_NVM_OEM_CAPABILITIES_OFFSET, - &capabilities); - if ((capabilities & I40E_NVM_OEM_CAPABILITIES_MASK) != 0) - return; +/** + * i40e_get_current_fd_count - Get total FD filters programmed for this PF + * @pf: board private structure + **/ +u32 i40e_get_current_fd_count(struct i40e_pf *pf) +{ + u32 val, fcnt_prog; - i40e_read_nvm_word(hw, block_offset + I40E_NVM_OEM_GEN_OFFSET, - &gen_snap); - i40e_read_nvm_word(hw, block_offset + I40E_NVM_OEM_RELEASE_OFFSET, - &release); - hw->nvm.oem_ver = (gen_snap << I40E_OEM_SNAP_SHIFT) | release; - hw->nvm.eetrack = I40E_OEM_EETRACK_ID; + val = rd32(&pf->hw, I40E_PFQF_FDSTAT); + fcnt_prog = (val & I40E_PFQF_FDSTAT_GUARANT_CNT_MASK) + + ((val & I40E_PFQF_FDSTAT_BEST_CNT_MASK) >> + I40E_PFQF_FDSTAT_BEST_CNT_SHIFT); + return fcnt_prog; } /** - * i40e_reset - wait for core reset to finish reset, reset pf if corer not seen + * i40e_get_global_fd_count - Get total FD filters programmed on device * @pf: board private structure **/ -static int i40e_reset(struct i40e_pf *pf) +u32 i40e_get_global_fd_count(struct i40e_pf *pf) { - struct i40e_hw *hw = &pf->hw; - i40e_status ret; + u32 val, fcnt_prog; - ret = i40e_pf_reset(hw); - if (ret) { - dev_info(&pf->pdev->dev, "PF reset failed, %d\n", ret); - set_bit(__I40E_RESET_FAILED, pf->state); - clear_bit(__I40E_RESET_RECOVERY_PENDING, pf->state); - } else { - pf->pfr_count++; - } - return ret; + val = rd32(&pf->hw, I40E_GLQF_FDCNT_0); + fcnt_prog = (val & I40E_GLQF_FDCNT_0_GUARANT_CNT_MASK) + + ((val & I40E_GLQF_FDCNT_0_BESTCNT_MASK) >> + I40E_GLQF_FDCNT_0_BESTCNT_SHIFT); + return fcnt_prog; } /** - * i40e_rebuild - rebuild using a saved config + * i40e_reenable_fdir_sb - Restore FDir SB capability * @pf: board private structure - * @reinit: if the Main VSI needs to re-initialized. - * @lock_acquired: indicates whether or not the lock has been acquired - * before this function was called. **/ -static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) +static void i40e_reenable_fdir_sb(struct i40e_pf *pf) { - struct i40e_hw *hw = &pf->hw; - u8 set_fc_aq_fail = 0; - i40e_status ret; - u32 val; - int v; + if (test_and_clear_bit(__I40E_FD_SB_AUTO_DISABLED, pf->state)) + if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) && + (I40E_DEBUG_FD & pf->hw.debug_mask)) + dev_info(&pf->pdev->dev, "FD Sideband/ntuple is being enabled since we have space in the table now\n"); +} - if (test_bit(__I40E_DOWN, pf->state)) - goto clear_recovery; - dev_dbg(&pf->pdev->dev, "Rebuilding internal switch\n"); +/** + * i40e_reenable_fdir_atr - Restore FDir ATR capability + * @pf: board private structure + **/ +static void i40e_reenable_fdir_atr(struct i40e_pf *pf) +{ + if (test_and_clear_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state)) { + /* ATR uses the same filtering logic as SB rules. It only + * functions properly if the input set mask is at the default + * settings. It is safe to restore the default input set + * because there are no active TCPv4 filter rules. + */ + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_TCP, + I40E_L3_SRC_MASK | I40E_L3_DST_MASK | + I40E_L4_SRC_MASK | I40E_L4_DST_MASK); - /* rebuild the basics for the AdminQ, HMC, and initial HW switch */ - ret = i40e_init_adminq(&pf->hw); - if (ret) { - dev_info(&pf->pdev->dev, "Rebuild AdminQ failed, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - goto clear_recovery; + if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && + (I40E_DEBUG_FD & pf->hw.debug_mask)) + dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table and there are no conflicting ntuple rules\n"); } - i40e_get_oem_version(&pf->hw); - - /* re-verify the eeprom if we just had an EMP reset */ - if (test_and_clear_bit(__I40E_EMP_RESET_INTR_RECEIVED, pf->state)) - i40e_verify_eeprom(pf); +} - i40e_clear_pxe_mode(hw); - ret = i40e_get_capabilities(pf); - if (ret) - goto end_core_reset; +/** + * i40e_delete_invalid_filter - Delete an invalid FDIR filter + * @pf: board private structure + * @filter: FDir filter to remove + */ +static void i40e_delete_invalid_filter(struct i40e_pf *pf, + struct i40e_fdir_filter *filter) +{ + /* Update counters */ + pf->fdir_pf_active_filters--; + pf->fd_inv = 0; - ret = i40e_init_lan_hmc(hw, hw->func_caps.num_tx_qp, - hw->func_caps.num_rx_qp, 0, 0); - if (ret) { - dev_info(&pf->pdev->dev, "init_lan_hmc failed: %d\n", ret); - goto end_core_reset; - } - ret = i40e_configure_lan_hmc(hw, I40E_HMC_MODEL_DIRECT_ONLY); - if (ret) { - dev_info(&pf->pdev->dev, "configure_lan_hmc failed: %d\n", ret); - goto end_core_reset; + switch (filter->flow_type) { + case TCP_V4_FLOW: + pf->fd_tcp4_filter_cnt--; + break; + case UDP_V4_FLOW: + pf->fd_udp4_filter_cnt--; + break; + case SCTP_V4_FLOW: + pf->fd_sctp4_filter_cnt--; + break; + case IP_USER_FLOW: + switch (filter->ip4_proto) { + case IPPROTO_TCP: + pf->fd_tcp4_filter_cnt--; + break; + case IPPROTO_UDP: + pf->fd_udp4_filter_cnt--; + break; + case IPPROTO_SCTP: + pf->fd_sctp4_filter_cnt--; + break; + case IPPROTO_IP: + pf->fd_ip4_filter_cnt--; + break; + } + break; } -#ifdef CONFIG_I40E_DCB - ret = i40e_init_pf_dcb(pf); - if (ret) { - dev_info(&pf->pdev->dev, "DCB init failed %d, disabled\n", ret); - pf->flags &= ~I40E_FLAG_DCB_CAPABLE; - /* Continue without DCB enabled */ - } -#endif /* CONFIG_I40E_DCB */ - /* do basic switch setup */ - if (!lock_acquired) - rtnl_lock(); - ret = i40e_setup_pf_switch(pf, reinit); - if (ret) - goto end_unlock; + /* Remove the filter from the list and free memory */ + hlist_del(&filter->fdir_node); + kfree(filter); +} - /* The driver only wants link up/down and module qualification - * reports from firmware. Note the negative logic. - */ - ret = i40e_aq_set_phy_int_mask(&pf->hw, - ~(I40E_AQ_EVENT_LINK_UPDOWN | - I40E_AQ_EVENT_MEDIA_NA | - I40E_AQ_EVENT_MODULE_QUAL_FAIL), NULL); - if (ret) - dev_info(&pf->pdev->dev, "set phy mask fail, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - - /* make sure our flow control settings are restored */ - ret = i40e_set_fc(&pf->hw, &set_fc_aq_fail, true); - if (ret) - dev_dbg(&pf->pdev->dev, "setting flow control: ret = %s last_status = %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - - /* Rebuild the VSIs and VEBs that existed before reset. - * They are still in our local switch element arrays, so only - * need to rebuild the switch model in the HW. - * - * If there were VEBs but the reconstitution failed, we'll try - * try to recover minimal use by getting the basic PF VSI working. - */ - if (pf->vsi[pf->lan_vsi]->uplink_seid != pf->mac_seid) { - dev_dbg(&pf->pdev->dev, "attempting to rebuild switch\n"); - /* find the one VEB connected to the MAC, and find orphans */ - for (v = 0; v < I40E_MAX_VEB; v++) { - if (!pf->veb[v]) - continue; - - if (pf->veb[v]->uplink_seid == pf->mac_seid || - pf->veb[v]->uplink_seid == 0) { - ret = i40e_reconstitute_veb(pf->veb[v]); - - if (!ret) - continue; +/** + * i40e_fdir_check_and_reenable - Function to reenabe FD ATR or SB if disabled + * @pf: board private structure + **/ +void i40e_fdir_check_and_reenable(struct i40e_pf *pf) +{ + struct i40e_fdir_filter *filter; + u32 fcnt_prog, fcnt_avail; + struct hlist_node *node; - /* If Main VEB failed, we're in deep doodoo, - * so give up rebuilding the switch and set up - * for minimal rebuild of PF VSI. - * If orphan failed, we'll report the error - * but try to keep going. - */ - if (pf->veb[v]->uplink_seid == pf->mac_seid) { - dev_info(&pf->pdev->dev, - "rebuild of switch failed: %d, will try to set up simple PF connection\n", - ret); - pf->vsi[pf->lan_vsi]->uplink_seid - = pf->mac_seid; - break; - } else if (pf->veb[v]->uplink_seid == 0) { - dev_info(&pf->pdev->dev, - "rebuild of orphan VEB failed: %d\n", - ret); - } - } - } - } + if (test_bit(__I40E_FD_FLUSH_REQUESTED, pf->state)) + return; - if (pf->vsi[pf->lan_vsi]->uplink_seid == pf->mac_seid) { - dev_dbg(&pf->pdev->dev, "attempting to rebuild PF VSI\n"); - /* no VEB, so rebuild only the Main VSI */ - ret = i40e_add_vsi(pf->vsi[pf->lan_vsi]); - if (ret) { - dev_info(&pf->pdev->dev, - "rebuild of Main VSI failed: %d\n", ret); - goto end_unlock; - } - } + /* Check if we have enough room to re-enable FDir SB capability. */ + fcnt_prog = i40e_get_global_fd_count(pf); + fcnt_avail = pf->fdir_pf_filter_count; + if ((fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM)) || + (pf->fd_add_err == 0) || + (i40e_get_current_atr_cnt(pf) < pf->fd_atr_cnt)) + i40e_reenable_fdir_sb(pf); - /* Reconfigure hardware for allowing smaller MSS in the case - * of TSO, so that we avoid the MDD being fired and causing - * a reset in the case of small MSS+TSO. + /* We should wait for even more space before re-enabling ATR. + * Additionally, we cannot enable ATR as long as we still have TCP SB + * rules active. */ -#define I40E_REG_MSS 0x000E64DC -#define I40E_REG_MSS_MIN_MASK 0x3FF0000 -#define I40E_64BYTE_MSS 0x400000 - val = rd32(hw, I40E_REG_MSS); - if ((val & I40E_REG_MSS_MIN_MASK) > I40E_64BYTE_MSS) { - val &= ~I40E_REG_MSS_MIN_MASK; - val |= I40E_64BYTE_MSS; - wr32(hw, I40E_REG_MSS, val); - } + if ((fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) && + (pf->fd_tcp4_filter_cnt == 0)) + i40e_reenable_fdir_atr(pf); - if (pf->hw_features & I40E_HW_RESTART_AUTONEG) { - msleep(75); - ret = i40e_aq_set_link_restart_an(&pf->hw, true, NULL); - if (ret) - dev_info(&pf->pdev->dev, "link restart failed, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, - pf->hw.aq.asq_last_status)); + /* if hw had a problem adding a filter, delete it */ + if (pf->fd_inv > 0) { + hlist_for_each_entry_safe(filter, node, + &pf->fdir_filter_list, fdir_node) + if (filter->fd_id == pf->fd_inv) + i40e_delete_invalid_filter(pf, filter); } - /* reinit the misc interrupt */ - if (pf->flags & I40E_FLAG_MSIX_ENABLED) - ret = i40e_setup_misc_vector(pf); - - /* Add a filter to drop all Flow control frames from any VSI from being - * transmitted. By doing so we stop a malicious VF from sending out - * PAUSE or PFC frames and potentially controlling traffic for other - * PF/VF VSIs. - * The FW can still send Flow control frames if enabled. - */ - i40e_add_filter_to_drop_tx_flow_control_frames(&pf->hw, - pf->main_vsi_seid); - - /* restart the VSIs that were rebuilt and running before the reset */ - i40e_pf_unquiesce_all_vsi(pf); +} - /* Release the RTNL lock before we start resetting VFs */ - if (!lock_acquired) - rtnl_unlock(); +#define I40E_MIN_FD_FLUSH_INTERVAL 10 +#define I40E_MIN_FD_FLUSH_SB_ATR_UNSTABLE 30 +/** + * i40e_fdir_flush_and_replay - Function to flush all FD filters and replay SB + * @pf: board private structure + **/ +static void i40e_fdir_flush_and_replay(struct i40e_pf *pf) +{ + unsigned long min_flush_time; + int flush_wait_retry = 50; + bool disable_atr = false; + int fd_room; + int reg; - /* Restore promiscuous settings */ - ret = i40e_set_promiscuous(pf, pf->cur_promisc); - if (ret) - dev_warn(&pf->pdev->dev, - "Failed to restore promiscuous setting: %s, err %s aq_err %s\n", - pf->cur_promisc ? "on" : "off", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + if (!time_after(jiffies, pf->fd_flush_timestamp + + (I40E_MIN_FD_FLUSH_INTERVAL * HZ))) + return; - i40e_reset_all_vfs(pf, true); + /* If the flush is happening too quick and we have mostly SB rules we + * should not re-enable ATR for some time. + */ + min_flush_time = pf->fd_flush_timestamp + + (I40E_MIN_FD_FLUSH_SB_ATR_UNSTABLE * HZ); + fd_room = pf->fdir_pf_filter_count - pf->fdir_pf_active_filters; - /* tell the firmware that we're starting */ - i40e_send_version(pf); + if (!(time_after(jiffies, min_flush_time)) && + (fd_room < I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) { + if (I40E_DEBUG_FD & pf->hw.debug_mask) + dev_info(&pf->pdev->dev, "ATR disabled, not enough FD filter space.\n"); + disable_atr = true; + } - /* We've already released the lock, so don't do it again */ - goto end_core_reset; + pf->fd_flush_timestamp = jiffies; + set_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state); + /* flush all filters */ + wr32(&pf->hw, I40E_PFQF_CTL_1, + I40E_PFQF_CTL_1_CLEARFDTABLE_MASK); + i40e_flush(&pf->hw); + pf->fd_flush_cnt++; + pf->fd_add_err = 0; + do { + /* Check FD flush status every 5-6msec */ + usleep_range(5000, 6000); + reg = rd32(&pf->hw, I40E_PFQF_CTL_1); + if (!(reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK)) + break; + } while (flush_wait_retry--); -end_unlock: - if (!lock_acquired) - rtnl_unlock(); -end_core_reset: - clear_bit(__I40E_RESET_FAILED, pf->state); -clear_recovery: - clear_bit(__I40E_RESET_RECOVERY_PENDING, pf->state); + if (reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK) { + dev_warn(&pf->pdev->dev, "FD table did not flush, needs more time\n"); + } else { + /* replay sideband filters */ + i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); + if (!disable_atr && !pf->fd_tcp4_filter_cnt) + clear_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state); + clear_bit(__I40E_FD_FLUSH_REQUESTED, pf->state); + if (I40E_DEBUG_FD & pf->hw.debug_mask) + dev_info(&pf->pdev->dev, "FD Filter table flushed and FD-SB replayed.\n"); + } } /** - * i40e_reset_and_rebuild - reset and rebuild using a saved config + * i40e_get_current_atr_count - Get the count of total FD ATR filters programmed * @pf: board private structure - * @reinit: if the Main VSI needs to re-initialized. - * @lock_acquired: indicates whether or not the lock has been acquired - * before this function was called. **/ -static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit, - bool lock_acquired) +u32 i40e_get_current_atr_cnt(struct i40e_pf *pf) { - int ret; - /* Now we wait for GRST to settle out. - * We don't have to delete the VEBs or VSIs from the hw switch - * because the reset will make them disappear. - */ - ret = i40e_reset(pf); - if (!ret) - i40e_rebuild(pf, reinit, lock_acquired); + return i40e_get_current_fd_count(pf) - pf->fdir_pf_active_filters; } +/* We can see up to 256 filter programming desc in transit if the filters are + * being applied really fast; before we see the first + * filter miss error on Rx queue 0. Accumulating enough error messages before + * reacting will make sure we don't cause flush too often. + */ +#define I40E_MAX_FD_PROGRAM_ERROR 256 /** - * i40e_handle_reset_warning - prep for the PF to reset, reset and rebuild + * i40e_fdir_reinit_subtask - Worker thread to reinit FDIR filter table * @pf: board private structure - * - * Close up the VFs and other things in prep for a Core Reset, - * then get ready to rebuild the world. - * @lock_acquired: indicates whether or not the lock has been acquired - * before this function was called. **/ -static void i40e_handle_reset_warning(struct i40e_pf *pf, bool lock_acquired) +static void i40e_fdir_reinit_subtask(struct i40e_pf *pf) { - i40e_prep_for_reset(pf, lock_acquired); - i40e_reset_and_rebuild(pf, false, lock_acquired); + + /* if interface is down do nothing */ + if (test_bit(__I40E_DOWN, pf->state)) + return; + + if (test_bit(__I40E_FD_FLUSH_REQUESTED, pf->state)) + i40e_fdir_flush_and_replay(pf); + + i40e_fdir_check_and_reenable(pf); + } /** - * i40e_handle_mdd_event - * @pf: pointer to the PF structure - * - * Called from the MDD irq handler to identify possibly malicious vfs + * i40e_vsi_link_event - notify VSI of a link event + * @vsi: vsi to be notified + * @link_up: link up or down **/ -static void i40e_handle_mdd_event(struct i40e_pf *pf) +static void i40e_vsi_link_event(struct i40e_vsi *vsi, bool link_up) { - struct i40e_hw *hw = &pf->hw; - bool mdd_detected = false; - bool pf_mdd_detected = false; - struct i40e_vf *vf; - u32 reg; - int i; - - if (!test_bit(__I40E_MDD_EVENT_PENDING, pf->state)) + if (!vsi || (test_bit(__I40E_VSI_DOWN, vsi->state))) return; - /* find what triggered the MDD event */ - reg = rd32(hw, I40E_GL_MDET_TX); - if (reg & I40E_GL_MDET_TX_VALID_MASK) { - u8 pf_num = (reg & I40E_GL_MDET_TX_PF_NUM_MASK) >> - I40E_GL_MDET_TX_PF_NUM_SHIFT; - u16 vf_num = (reg & I40E_GL_MDET_TX_VF_NUM_MASK) >> - I40E_GL_MDET_TX_VF_NUM_SHIFT; - u8 event = (reg & I40E_GL_MDET_TX_EVENT_MASK) >> - I40E_GL_MDET_TX_EVENT_SHIFT; - u16 queue = ((reg & I40E_GL_MDET_TX_QUEUE_MASK) >> - I40E_GL_MDET_TX_QUEUE_SHIFT) - - pf->hw.func_caps.base_queue; - if (netif_msg_tx_err(pf)) - dev_info(&pf->pdev->dev, "Malicious Driver Detection event 0x%02x on TX queue %d PF number 0x%02x VF number 0x%02x\n", - event, queue, pf_num, vf_num); - wr32(hw, I40E_GL_MDET_TX, 0xffffffff); - mdd_detected = true; - } - reg = rd32(hw, I40E_GL_MDET_RX); - if (reg & I40E_GL_MDET_RX_VALID_MASK) { - u8 func = (reg & I40E_GL_MDET_RX_FUNCTION_MASK) >> - I40E_GL_MDET_RX_FUNCTION_SHIFT; - u8 event = (reg & I40E_GL_MDET_RX_EVENT_MASK) >> - I40E_GL_MDET_RX_EVENT_SHIFT; - u16 queue = ((reg & I40E_GL_MDET_RX_QUEUE_MASK) >> - I40E_GL_MDET_RX_QUEUE_SHIFT) - - pf->hw.func_caps.base_queue; - if (netif_msg_rx_err(pf)) - dev_info(&pf->pdev->dev, "Malicious Driver Detection event 0x%02x on RX queue %d of function 0x%02x\n", - event, queue, func); - wr32(hw, I40E_GL_MDET_RX, 0xffffffff); - mdd_detected = true; - } - - if (mdd_detected) { - reg = rd32(hw, I40E_PF_MDET_TX); - if (reg & I40E_PF_MDET_TX_VALID_MASK) { - wr32(hw, I40E_PF_MDET_TX, 0xFFFF); - dev_info(&pf->pdev->dev, "TX driver issue detected, PF reset issued\n"); - pf_mdd_detected = true; - } - reg = rd32(hw, I40E_PF_MDET_RX); - if (reg & I40E_PF_MDET_RX_VALID_MASK) { - wr32(hw, I40E_PF_MDET_RX, 0xFFFF); - dev_info(&pf->pdev->dev, "RX driver issue detected, PF reset issued\n"); - pf_mdd_detected = true; - } - /* Queue belongs to the PF, initiate a reset */ - if (pf_mdd_detected) { - set_bit(__I40E_PF_RESET_REQUESTED, pf->state); - i40e_service_event_schedule(pf); - } - } - - /* see if one of the VFs needs its hand slapped */ - for (i = 0; i < pf->num_alloc_vfs && mdd_detected; i++) { - vf = &(pf->vf[i]); - reg = rd32(hw, I40E_VP_MDET_TX(i)); - if (reg & I40E_VP_MDET_TX_VALID_MASK) { - wr32(hw, I40E_VP_MDET_TX(i), 0xFFFF); - vf->num_mdd_events++; - dev_info(&pf->pdev->dev, "TX driver issue detected on VF %d\n", - i); - } - - reg = rd32(hw, I40E_VP_MDET_RX(i)); - if (reg & I40E_VP_MDET_RX_VALID_MASK) { - wr32(hw, I40E_VP_MDET_RX(i), 0xFFFF); - vf->num_mdd_events++; - dev_info(&pf->pdev->dev, "RX driver issue detected on VF %d\n", - i); - } + switch (vsi->type) { + case I40E_VSI_MAIN: + if (!vsi->netdev || !vsi->netdev_registered) + break; - if (vf->num_mdd_events > I40E_DEFAULT_NUM_MDD_EVENTS_ALLOWED) { - dev_info(&pf->pdev->dev, - "Too many MDD events on VF %d, disabled\n", i); - dev_info(&pf->pdev->dev, - "Use PF Control I/F to re-enable the VF\n"); - set_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); + if (link_up) { + netif_carrier_on(vsi->netdev); + netif_tx_wake_all_queues(vsi->netdev); + } else { + netif_carrier_off(vsi->netdev); + netif_tx_stop_all_queues(vsi->netdev); } - } - - /* re-enable mdd interrupt cause */ - clear_bit(__I40E_MDD_EVENT_PENDING, pf->state); - reg = rd32(hw, I40E_PFINT_ICR0_ENA); - reg |= I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK; - wr32(hw, I40E_PFINT_ICR0_ENA, reg); - i40e_flush(hw); -} + break; -static const char *i40e_tunnel_name(struct i40e_udp_port_config *port) -{ - switch (port->type) { - case UDP_TUNNEL_TYPE_VXLAN: - return "vxlan"; - case UDP_TUNNEL_TYPE_GENEVE: - return "geneve"; + case I40E_VSI_SRIOV: + case I40E_VSI_VMDQ2: + case I40E_VSI_CTRL: + case I40E_VSI_IWARP: + case I40E_VSI_MIRROR: default: - return "unknown"; + /* there is no notification for other VSIs */ + break; } } /** - * i40e_sync_udp_filters - Trigger a sync event for existing UDP filters - * @pf: board private structure + * i40e_veb_link_event - notify elements on the veb of a link event + * @veb: veb to be notified + * @link_up: link up or down **/ -static void i40e_sync_udp_filters(struct i40e_pf *pf) +static void i40e_veb_link_event(struct i40e_veb *veb, bool link_up) { + struct i40e_pf *pf; int i; - /* loop through and set pending bit for all active UDP filters */ - for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { - if (pf->udp_ports[i].port) - pf->pending_udp_bitmap |= BIT_ULL(i); - } + if (!veb || !veb->pf) + return; + pf = veb->pf; + + /* depth first... */ + for (i = 0; i < I40E_MAX_VEB; i++) + if (pf->veb[i] && (pf->veb[i]->uplink_seid == veb->seid)) + i40e_veb_link_event(pf->veb[i], link_up); - pf->flags |= I40E_FLAG_UDP_FILTER_SYNC; + /* ... now the local VSIs */ + for (i = 0; i < pf->num_alloc_vsi; i++) + if (pf->vsi[i] && (pf->vsi[i]->uplink_seid == veb->seid)) + i40e_vsi_link_event(pf->vsi[i], link_up); } /** - * i40e_sync_udp_filters_subtask - Sync the VSI filter list with HW + * i40e_link_event - Update netif_carrier status * @pf: board private structure **/ -static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf) +static void i40e_link_event(struct i40e_pf *pf) { - struct i40e_hw *hw = &pf->hw; - i40e_status ret; - u16 port; - int i; + struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; + u8 new_link_speed, old_link_speed; + i40e_status status; + bool new_link, old_link; + + /* set this to force the get_link_status call to refresh state */ + pf->hw.phy.get_link_info = true; + old_link = (pf->hw.phy.link_info_old.link_info & I40E_AQ_LINK_UP); + status = i40e_get_link_status(&pf->hw, &new_link); - if (!(pf->flags & I40E_FLAG_UDP_FILTER_SYNC)) + /* On success, disable temp link polling */ + if (status == I40E_SUCCESS) { + clear_bit(__I40E_TEMP_LINK_POLLING, pf->state); + } else { + /* Enable link polling temporarily until i40e_get_link_status + * returns I40E_SUCCESS + */ + set_bit(__I40E_TEMP_LINK_POLLING, pf->state); + dev_dbg(&pf->pdev->dev, "couldn't get link state, status: %d\n", + status); return; + } - pf->flags &= ~I40E_FLAG_UDP_FILTER_SYNC; + old_link_speed = pf->hw.phy.link_info_old.link_speed; + new_link_speed = pf->hw.phy.link_info.link_speed; - for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { - if (pf->pending_udp_bitmap & BIT_ULL(i)) { - pf->pending_udp_bitmap &= ~BIT_ULL(i); - port = pf->udp_ports[i].port; - if (port) - ret = i40e_aq_add_udp_tunnel(hw, port, - pf->udp_ports[i].type, - NULL, NULL); - else - ret = i40e_aq_del_udp_tunnel(hw, i, NULL); + if (new_link == old_link && + new_link_speed == old_link_speed && + (test_bit(__I40E_VSI_DOWN, vsi->state) || + new_link == netif_carrier_ok(vsi->netdev))) + return; - if (ret) { - dev_info(&pf->pdev->dev, - "%s %s port %d, index %d failed, err %s aq_err %s\n", - i40e_tunnel_name(&pf->udp_ports[i]), - port ? "add" : "delete", - port, i, - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, - pf->hw.aq.asq_last_status)); - pf->udp_ports[i].port = 0; - } - } - } + i40e_print_link_message(vsi, new_link); + + /* Notify the base of the switch tree connected to + * the link. Floating VEBs are not notified. + */ + if (pf->lan_veb < I40E_MAX_VEB && pf->veb[pf->lan_veb]) + i40e_veb_link_event(pf->veb[pf->lan_veb], new_link); + else + i40e_vsi_link_event(vsi, new_link); + + if (pf->vf) + i40e_vc_notify_link_state(pf); +#ifdef HAVE_PTP_1588_CLOCK + + if (pf->flags & I40E_FLAG_PTP) + i40e_ptp_set_increment(pf); +#endif /* HAVE_PTP_1588_CLOCK */ } /** - * i40e_service_task - Run the driver's async subtasks - * @work: pointer to work_struct containing our data + * i40e_watchdog_subtask - periodic checks not using event driven response + * @pf: board private structure **/ -static void i40e_service_task(struct work_struct *work) +static void i40e_watchdog_subtask(struct i40e_pf *pf) { - struct i40e_pf *pf = container_of(work, - struct i40e_pf, - service_task); - unsigned long start_time = jiffies; + int i; - /* don't bother with service tasks if a reset is in progress */ - if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) + /* if interface is down do nothing */ + if (test_bit(__I40E_DOWN, pf->state) || + test_bit(__I40E_CONFIG_BUSY, pf->state)) return; - if (test_and_set_bit(__I40E_SERVICE_SCHED, pf->state)) + /* make sure we don't do these things too often */ + if (time_before(jiffies, (pf->service_timer_previous + + pf->service_timer_period))) return; + pf->service_timer_previous = jiffies; - i40e_detect_recover_hung(pf); - i40e_sync_filters_subtask(pf); - i40e_reset_subtask(pf); - i40e_handle_mdd_event(pf); - i40e_vc_process_vflr_event(pf); - i40e_watchdog_subtask(pf); - i40e_fdir_reinit_subtask(pf); - if (pf->flags & I40E_FLAG_CLIENT_RESET) { - /* Client subtask will reopen next time through. */ - i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], true); - pf->flags &= ~I40E_FLAG_CLIENT_RESET; - } else { - i40e_client_subtask(pf); - if (pf->flags & I40E_FLAG_CLIENT_L2_CHANGE) { - i40e_notify_client_of_l2_param_changes( - pf->vsi[pf->lan_vsi]); - pf->flags &= ~I40E_FLAG_CLIENT_L2_CHANGE; - } - } - i40e_sync_filters_subtask(pf); - i40e_sync_udp_filters_subtask(pf); - i40e_clean_adminq_subtask(pf); - - /* flush memory to make sure state is correct before next watchdog */ - smp_mb__before_atomic(); - clear_bit(__I40E_SERVICE_SCHED, pf->state); + if ((pf->flags & I40E_FLAG_LINK_POLLING_ENABLED) || + test_bit(__I40E_TEMP_LINK_POLLING, pf->state)) + i40e_link_event(pf); - /* If the tasks have taken longer than one timer cycle or there - * is more work to be done, reschedule the service task now - * rather than wait for the timer to tick again. + /* Update the stats for active netdevs so the network stack + * can look at updated numbers whenever it cares to */ - if (time_after(jiffies, (start_time + pf->service_timer_period)) || - test_bit(__I40E_ADMINQ_EVENT_PENDING, pf->state) || - test_bit(__I40E_MDD_EVENT_PENDING, pf->state) || - test_bit(__I40E_VFLR_EVENT_PENDING, pf->state)) - i40e_service_event_schedule(pf); -} + for (i = 0; i < pf->num_alloc_vsi; i++) + if (pf->vsi[i] && pf->vsi[i]->netdev) + i40e_update_stats(pf->vsi[i]); -/** - * i40e_service_timer - timer callback - * @data: pointer to PF struct - **/ -static void i40e_service_timer(unsigned long data) -{ - struct i40e_pf *pf = (struct i40e_pf *)data; + if (pf->flags & I40E_FLAG_VEB_STATS_ENABLED) { + /* Update the stats for the active switching components */ + for (i = 0; i < I40E_MAX_VEB; i++) + if (pf->veb[i]) + i40e_update_veb_stats(pf->veb[i]); + } +#ifdef HAVE_PTP_1588_CLOCK - mod_timer(&pf->service_timer, - round_jiffies(jiffies + pf->service_timer_period)); - i40e_service_event_schedule(pf); + i40e_ptp_rx_hang(pf); + i40e_ptp_tx_hang(pf); +#endif /* HAVE_PTP_1588_CLOCK */ } /** - * i40e_set_num_rings_in_vsi - Determine number of rings in the VSI - * @vsi: the VSI being configured + * i40e_reset_subtask - Set up for resetting the device and driver + * @pf: board private structure **/ -static int i40e_set_num_rings_in_vsi(struct i40e_vsi *vsi) +static void i40e_reset_subtask(struct i40e_pf *pf) { - struct i40e_pf *pf = vsi->back; - - switch (vsi->type) { - case I40E_VSI_MAIN: - vsi->alloc_queue_pairs = pf->num_lan_qps; - vsi->num_desc = ALIGN(I40E_DEFAULT_NUM_DESCRIPTORS, - I40E_REQ_DESCRIPTOR_MULTIPLE); - if (pf->flags & I40E_FLAG_MSIX_ENABLED) - vsi->num_q_vectors = pf->num_lan_msix; - else - vsi->num_q_vectors = 1; - - break; - - case I40E_VSI_FDIR: - vsi->alloc_queue_pairs = 1; - vsi->num_desc = ALIGN(I40E_FDIR_RING_COUNT, - I40E_REQ_DESCRIPTOR_MULTIPLE); - vsi->num_q_vectors = pf->num_fdsb_msix; - break; - - case I40E_VSI_VMDQ2: - vsi->alloc_queue_pairs = pf->num_vmdq_qps; - vsi->num_desc = ALIGN(I40E_DEFAULT_NUM_DESCRIPTORS, - I40E_REQ_DESCRIPTOR_MULTIPLE); - vsi->num_q_vectors = pf->num_vmdq_msix; - break; + u32 reset_flags = 0; - case I40E_VSI_SRIOV: - vsi->alloc_queue_pairs = pf->num_vf_qps; - vsi->num_desc = ALIGN(I40E_DEFAULT_NUM_DESCRIPTORS, - I40E_REQ_DESCRIPTOR_MULTIPLE); - break; + if (test_and_clear_bit(__I40E_REINIT_REQUESTED, pf->state)) + reset_flags |= BIT(__I40E_REINIT_REQUESTED); + if (test_and_clear_bit(__I40E_PF_RESET_REQUESTED, pf->state)) + reset_flags |= BIT(__I40E_PF_RESET_REQUESTED); + if (test_and_clear_bit(__I40E_CORE_RESET_REQUESTED, pf->state)) + reset_flags |= BIT(__I40E_CORE_RESET_REQUESTED); + if (test_and_clear_bit(__I40E_GLOBAL_RESET_REQUESTED, pf->state)) + reset_flags |= BIT(__I40E_GLOBAL_RESET_REQUESTED); + if (test_and_clear_bit(__I40E_DOWN_REQUESTED, pf->state)) + reset_flags |= BIT(__I40E_DOWN_REQUESTED); - default: - WARN_ON(1); - return -ENODATA; + /* If there's a recovery already waiting, it takes + * precedence before starting a new reset sequence. + */ + if (test_bit(__I40E_RESET_INTR_RECEIVED, pf->state)) { + i40e_prep_for_reset(pf, false); + i40e_reset(pf); + i40e_rebuild(pf, false, false); } - return 0; + /* If we're already down or resetting, just bail */ + if (reset_flags && + !test_bit(__I40E_DOWN, pf->state) && + !test_bit(__I40E_CONFIG_BUSY, pf->state)) { + i40e_do_reset(pf, reset_flags, false); + } } /** - * i40e_vsi_alloc_arrays - Allocate queue and vector pointer arrays for the vsi - * @type: VSI pointer - * @alloc_qvectors: a bool to specify if q_vectors need to be allocated. - * - * On error: returns error code (negative) - * On success: returns 0 + * i40e_handle_link_event - Handle link event + * @pf: board private structure + * @e: event info posted on ARQ **/ -static int i40e_vsi_alloc_arrays(struct i40e_vsi *vsi, bool alloc_qvectors) +static void i40e_handle_link_event(struct i40e_pf *pf, + struct i40e_arq_event_info *e) { - struct i40e_ring **next_rings; - int size; - int ret = 0; + struct i40e_aqc_get_link_status *status = + (struct i40e_aqc_get_link_status *)&e->desc.params.raw; - /* allocate memory for both Tx, XDP Tx and Rx ring pointers */ - size = sizeof(struct i40e_ring *) * vsi->alloc_queue_pairs * - (i40e_enabled_xdp_vsi(vsi) ? 3 : 2); - vsi->tx_rings = kzalloc(size, GFP_KERNEL); - if (!vsi->tx_rings) - return -ENOMEM; - next_rings = vsi->tx_rings + vsi->alloc_queue_pairs; - if (i40e_enabled_xdp_vsi(vsi)) { - vsi->xdp_rings = next_rings; - next_rings += vsi->alloc_queue_pairs; - } - vsi->rx_rings = next_rings; + /* Do a new status request to re-enable LSE reporting + * and load new status information into the hw struct + * This completely ignores any state information + * in the ARQ event info, instead choosing to always + * issue the AQ update link status command. + */ + i40e_link_event(pf); - if (alloc_qvectors) { - /* allocate memory for q_vector pointers */ - size = sizeof(struct i40e_q_vector *) * vsi->num_q_vectors; - vsi->q_vectors = kzalloc(size, GFP_KERNEL); - if (!vsi->q_vectors) { - ret = -ENOMEM; - goto err_vectors; + /* Check if module meets thermal requirements */ + if (status->phy_type == I40E_PHY_TYPE_NOT_SUPPORTED_HIGH_TEMP) { + dev_err(&pf->pdev->dev, + "Rx/Tx is disabled on this device because the module does not meet thermal requirements.\n"); + dev_err(&pf->pdev->dev, + "Refer to the Intel(R) Ethernet Adapters and Devices User Guide for a list of supported modules.\n"); + } else { + /* check for unqualified module, if link is down, suppress + * the message if link was forced to be down. + */ + if ((status->link_info & I40E_AQ_MEDIA_AVAILABLE) && + (!(status->an_info & I40E_AQ_QUALIFIED_MODULE)) && + (!(status->link_info & I40E_AQ_LINK_UP)) && + (!(pf->flags & I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED))) { + dev_err(&pf->pdev->dev, + "Rx/Tx is disabled on this device because an unsupported SFP+ module type was detected.\n"); + dev_err(&pf->pdev->dev, + "Refer to the Intel(R) Ethernet Adapters and Devices User Guide for a list of supported modules.\n"); } } - return ret; - -err_vectors: - kfree(vsi->tx_rings); - return ret; } /** - * i40e_vsi_mem_alloc - Allocates the next available struct vsi in the PF + * i40e_clean_adminq_subtask - Clean the AdminQ rings * @pf: board private structure - * @type: type of VSI - * - * On error: returns error code (negative) - * On success: returns vsi index in PF (positive) **/ -static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type) +static void i40e_clean_adminq_subtask(struct i40e_pf *pf) { - int ret = -ENODEV; - struct i40e_vsi *vsi; - int vsi_idx; - int i; + struct i40e_arq_event_info event; + struct i40e_hw *hw = &pf->hw; + u16 pending, i = 0; + i40e_status ret; + u16 opcode; + u32 oldval; + u32 val; - /* Need to protect the allocation of the VSIs at the PF level */ - mutex_lock(&pf->switch_mutex); + /* Do not run clean AQ when PF reset fails */ + if (test_bit(__I40E_RESET_FAILED, pf->state)) + return; - /* VSI list may be fragmented if VSI creation/destruction has - * been happening. We can afford to do a quick scan to look - * for any free VSIs in the list. - * - * find next empty vsi slot, looping back around if necessary - */ - i = pf->next_vsi; - while (i < pf->num_alloc_vsi && pf->vsi[i]) - i++; - if (i >= pf->num_alloc_vsi) { - i = 0; - while (i < pf->next_vsi && pf->vsi[i]) - i++; + /* check for error indications */ + val = rd32(&pf->hw, pf->hw.aq.arq.len); + oldval = val; + if (val & I40E_PF_ARQLEN_ARQVFE_MASK) { + if (hw->debug_mask & I40E_DEBUG_AQ) + dev_info(&pf->pdev->dev, "ARQ VF Error detected\n"); + val &= ~I40E_PF_ARQLEN_ARQVFE_MASK; } - - if (i < pf->num_alloc_vsi && !pf->vsi[i]) { - vsi_idx = i; /* Found one! */ - } else { - ret = -ENODEV; - goto unlock_pf; /* out of VSI slots! */ + if (val & I40E_PF_ARQLEN_ARQOVFL_MASK) { + if (hw->debug_mask & I40E_DEBUG_AQ) + dev_info(&pf->pdev->dev, "ARQ Overflow Error detected\n"); + val &= ~I40E_PF_ARQLEN_ARQOVFL_MASK; + pf->arq_overflows++; } - pf->next_vsi = ++i; + if (val & I40E_PF_ARQLEN_ARQCRIT_MASK) { + if (hw->debug_mask & I40E_DEBUG_AQ) + dev_info(&pf->pdev->dev, "ARQ Critical Error detected\n"); + val &= ~I40E_PF_ARQLEN_ARQCRIT_MASK; + } + if (oldval != val) + wr32(&pf->hw, pf->hw.aq.arq.len, val); - vsi = kzalloc(sizeof(*vsi), GFP_KERNEL); - if (!vsi) { - ret = -ENOMEM; - goto unlock_pf; + val = rd32(&pf->hw, pf->hw.aq.asq.len); + oldval = val; + if (val & I40E_PF_ATQLEN_ATQVFE_MASK) { + if (pf->hw.debug_mask & I40E_DEBUG_AQ) + dev_info(&pf->pdev->dev, "ASQ VF Error detected\n"); + val &= ~I40E_PF_ATQLEN_ATQVFE_MASK; } - vsi->type = type; - vsi->back = pf; - set_bit(__I40E_VSI_DOWN, vsi->state); - vsi->flags = 0; - vsi->idx = vsi_idx; - vsi->int_rate_limit = 0; - vsi->rss_table_size = (vsi->type == I40E_VSI_MAIN) ? - pf->rss_table_size : 64; - vsi->netdev_registered = false; - vsi->work_limit = I40E_DEFAULT_IRQ_WORK; - hash_init(vsi->mac_filter_hash); - vsi->irqs_ready = false; + if (val & I40E_PF_ATQLEN_ATQOVFL_MASK) { + if (pf->hw.debug_mask & I40E_DEBUG_AQ) + dev_info(&pf->pdev->dev, "ASQ Overflow Error detected\n"); + val &= ~I40E_PF_ATQLEN_ATQOVFL_MASK; + } + if (val & I40E_PF_ATQLEN_ATQCRIT_MASK) { + if (pf->hw.debug_mask & I40E_DEBUG_AQ) + dev_info(&pf->pdev->dev, "ASQ Critical Error detected\n"); + val &= ~I40E_PF_ATQLEN_ATQCRIT_MASK; + } + if (oldval != val) + wr32(&pf->hw, pf->hw.aq.asq.len, val); - ret = i40e_set_num_rings_in_vsi(vsi); - if (ret) - goto err_rings; + event.buf_len = I40E_MAX_AQ_BUF_SIZE; + event.msg_buf = kzalloc(event.buf_len, GFP_KERNEL); + if (!event.msg_buf) + return; - ret = i40e_vsi_alloc_arrays(vsi, true); - if (ret) - goto err_rings; + do { + ret = i40e_clean_arq_element(hw, &event, &pending); + if (ret == I40E_ERR_ADMIN_QUEUE_NO_WORK) + break; + else if (ret) { + dev_info(&pf->pdev->dev, "ARQ event error %d\n", ret); + break; + } - /* Setup default MSIX irq handler for VSI */ - i40e_vsi_setup_irqhandler(vsi, i40e_msix_clean_rings); + opcode = LE16_TO_CPU(event.desc.opcode); + switch (opcode) { - /* Initialize VSI lock */ - spin_lock_init(&vsi->mac_filter_hash_lock); - pf->vsi[vsi_idx] = vsi; - ret = vsi_idx; - goto unlock_pf; + case i40e_aqc_opc_get_link_status: + i40e_handle_link_event(pf, &event); + break; + case i40e_aqc_opc_send_msg_to_pf: + ret = i40e_vc_process_vf_msg(pf, + le16_to_cpu(event.desc.retval), + le32_to_cpu(event.desc.cookie_high), + le32_to_cpu(event.desc.cookie_low), + event.msg_buf, + event.msg_len); + break; + case i40e_aqc_opc_lldp_update_mib: + dev_dbg(&pf->pdev->dev, "ARQ: Update LLDP MIB event received\n"); +#ifdef CONFIG_DCB + rtnl_lock(); + ret = i40e_handle_lldp_event(pf, &event); + rtnl_unlock(); +#endif /* CONFIG_DCB */ + break; + case i40e_aqc_opc_event_lan_overflow: + dev_dbg(&pf->pdev->dev, "ARQ LAN queue overflow event received\n"); + i40e_handle_lan_overflow_event(pf, &event); + break; + case i40e_aqc_opc_send_msg_to_peer: + dev_info(&pf->pdev->dev, "ARQ: Msg from other pf\n"); + break; + case i40e_aqc_opc_nvm_erase: + case i40e_aqc_opc_nvm_update: + case i40e_aqc_opc_oem_post_update: + i40e_debug(&pf->hw, I40E_DEBUG_NVM, + "ARQ NVM operation 0x%04x completed\n", + opcode); + break; + default: + dev_info(&pf->pdev->dev, + "ARQ: Unknown event 0x%04x ignored\n", + opcode); + break; + } + } while (i++ < pf->adminq_work_limit); + + if (i < pf->adminq_work_limit) + clear_bit(__I40E_ADMINQ_EVENT_PENDING, pf->state); + + /* re-enable Admin queue interrupt cause */ + val = rd32(hw, I40E_PFINT_ICR0_ENA); + val |= I40E_PFINT_ICR0_ENA_ADMINQ_MASK; + wr32(hw, I40E_PFINT_ICR0_ENA, val); + i40e_flush(hw); + + kfree(event.msg_buf); +} + +/** + * i40e_verify_eeprom - make sure eeprom is good to use + * @pf: board private structure + **/ +static void i40e_verify_eeprom(struct i40e_pf *pf) +{ + int err; + + err = i40e_diag_eeprom_test(&pf->hw); + if (err) { + /* retry in case of garbage read */ + err = i40e_diag_eeprom_test(&pf->hw); + if (err) { + dev_info(&pf->pdev->dev, "eeprom check failed (%d), Tx/Rx traffic disabled\n", + err); + set_bit(__I40E_BAD_EEPROM, pf->state); + } + } + + if (!err && test_bit(__I40E_BAD_EEPROM, pf->state)) { + dev_info(&pf->pdev->dev, "eeprom check passed, Tx/Rx traffic enabled\n"); + clear_bit(__I40E_BAD_EEPROM, pf->state); + } +} + +/** + * i40e_enable_pf_switch_lb + * @pf: pointer to the PF structure + * + * enable switch loop back or die - no point in a return value + **/ +static void i40e_enable_pf_switch_lb(struct i40e_pf *pf) +{ + struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; + struct i40e_vsi_context ctxt; + int ret; + + ctxt.seid = pf->main_vsi_seid; + ctxt.pf_num = pf->hw.pf_id; + ctxt.vf_num = 0; + ret = i40e_aq_get_vsi_params(&pf->hw, &ctxt, NULL); + if (ret) { + dev_info(&pf->pdev->dev, + "couldn't get PF vsi config, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + return; + } + ctxt.flags = I40E_AQ_VSI_TYPE_PF; + ctxt.info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID); + ctxt.info.switch_id |= cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB); + + ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL); + if (ret) { + dev_info(&pf->pdev->dev, + "update vsi switch failed, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + } +} + +/** + * i40e_disable_pf_switch_lb + * @pf: pointer to the PF structure + * + * disable switch loop back or die - no point in a return value + **/ +static void i40e_disable_pf_switch_lb(struct i40e_pf *pf) +{ + struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; + struct i40e_vsi_context ctxt; + int ret; + + ctxt.seid = pf->main_vsi_seid; + ctxt.pf_num = pf->hw.pf_id; + ctxt.vf_num = 0; + ret = i40e_aq_get_vsi_params(&pf->hw, &ctxt, NULL); + if (ret) { + dev_info(&pf->pdev->dev, + "couldn't get PF vsi config, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + return; + } + ctxt.flags = I40E_AQ_VSI_TYPE_PF; + ctxt.info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID); + ctxt.info.switch_id &= ~cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB); + + ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL); + if (ret) { + dev_info(&pf->pdev->dev, + "update vsi switch failed, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + } +} + +/** + * i40e_config_bridge_mode - Configure the HW bridge mode + * @veb: pointer to the bridge instance + * + * Configure the loop back mode for the LAN VSI that is downlink to the + * specified HW bridge instance. It is expected this function is called + * when a new HW bridge is instantiated. + **/ +static void i40e_config_bridge_mode(struct i40e_veb *veb) +{ + struct i40e_pf *pf = veb->pf; + +#ifdef HAVE_BRIDGE_ATTRIBS + if (pf->hw.debug_mask & I40E_DEBUG_LAN) + dev_info(&pf->pdev->dev, "enabling bridge mode: %s\n", + veb->bridge_mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); + if (veb->bridge_mode & BRIDGE_MODE_VEPA) + i40e_disable_pf_switch_lb(pf); + else + i40e_enable_pf_switch_lb(pf); +#else + if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED) + i40e_enable_pf_switch_lb(pf); + else + i40e_disable_pf_switch_lb(pf); +#endif +} + +/** + * i40e_reconstitute_veb - rebuild the VEB and anything connected to it + * @veb: pointer to the VEB instance + * + * This is a recursive function that first builds the attached VSIs then + * recurses in to build the next layer of VEB. We track the connections + * through our own index numbers because the seid's from the HW could + * change across the reset. + **/ +static int i40e_reconstitute_veb(struct i40e_veb *veb) +{ + struct i40e_vsi *ctl_vsi = NULL; + struct i40e_pf *pf = veb->pf; + int v, veb_idx; + int ret; + + /* build VSI that owns this VEB, temporarily attached to base VEB */ + for (v = 0; v < pf->num_alloc_vsi && !ctl_vsi; v++) { + if (pf->vsi[v] && + pf->vsi[v]->veb_idx == veb->idx && + pf->vsi[v]->flags & I40E_VSI_FLAG_VEB_OWNER) { + ctl_vsi = pf->vsi[v]; + break; + } + } + if (!ctl_vsi) { + dev_info(&pf->pdev->dev, + "missing owner VSI for veb_idx %d\n", veb->idx); + ret = -ENOENT; + goto end_reconstitute; + } + if (ctl_vsi != pf->vsi[pf->lan_vsi]) + ctl_vsi->uplink_seid = pf->vsi[pf->lan_vsi]->uplink_seid; + ret = i40e_add_vsi(ctl_vsi); + if (ret) { + dev_info(&pf->pdev->dev, + "rebuild of veb_idx %d owner VSI failed: %d\n", + veb->idx, ret); + goto end_reconstitute; + } + i40e_vsi_reset_stats(ctl_vsi); + + /* create the VEB in the switch and move the VSI onto the VEB */ + ret = i40e_add_veb(veb, ctl_vsi); + if (ret) + goto end_reconstitute; + +#ifdef HAVE_BRIDGE_ATTRIBS + if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED) + veb->bridge_mode = BRIDGE_MODE_VEB; + else + veb->bridge_mode = BRIDGE_MODE_VEPA; +#endif + i40e_config_bridge_mode(veb); + + /* create the remaining VSIs attached to this VEB */ + for (v = 0; v < pf->num_alloc_vsi; v++) { + if (!pf->vsi[v] || pf->vsi[v] == ctl_vsi) + continue; + + if (pf->vsi[v]->veb_idx == veb->idx) { + struct i40e_vsi *vsi = pf->vsi[v]; + + vsi->uplink_seid = veb->seid; + ret = i40e_add_vsi(vsi); + if (ret) { + dev_info(&pf->pdev->dev, + "rebuild of vsi_idx %d failed: %d\n", + v, ret); + goto end_reconstitute; + } + i40e_vsi_reset_stats(vsi); + } + } + + /* create any VEBs attached to this VEB - RECURSION */ + for (veb_idx = 0; veb_idx < I40E_MAX_VEB; veb_idx++) { + if (pf->veb[veb_idx] && pf->veb[veb_idx]->veb_idx == veb->idx) { + pf->veb[veb_idx]->uplink_seid = veb->seid; + ret = i40e_reconstitute_veb(pf->veb[veb_idx]); + if (ret) + break; + } + } + +end_reconstitute: + return ret; +} + +/** + * i40e_get_capabilities - get info about the HW + * @pf: the PF struct + * @list_type: admin queue opcode list type + **/ +static int i40e_get_capabilities(struct i40e_pf *pf, + enum i40e_admin_queue_opc list_type) +{ + struct i40e_aqc_list_capabilities_element_resp *cap_buf; + u16 data_size; + int buf_len; + int err; + + buf_len = 40 * sizeof(struct i40e_aqc_list_capabilities_element_resp); + do { + cap_buf = kzalloc(buf_len, GFP_KERNEL); + if (!cap_buf) + return -ENOMEM; + + /* this loads the data into the hw struct for us */ + err = i40e_aq_discover_capabilities(&pf->hw, cap_buf, buf_len, + &data_size, list_type, + NULL); + /* data loaded, buffer no longer needed */ + kfree(cap_buf); + + if (pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOMEM) { + /* retry with a larger buffer */ + buf_len = data_size; + } else if (pf->hw.aq.asq_last_status != I40E_AQ_RC_OK) { + dev_info(&pf->pdev->dev, + "capability discovery failed, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, err), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); + return -ENODEV; + } + } while (err); + + if (pf->hw.debug_mask & I40E_DEBUG_USER) { + if (list_type == i40e_aqc_opc_list_func_capabilities) { + dev_info(&pf->pdev->dev, + "pf=%d, num_vfs=%d, msix_pf=%d, msix_vf=%d, fd_g=%d, fd_b=%d, pf_max_q=%d num_vsi=%d\n", + pf->hw.pf_id, pf->hw.func_caps.num_vfs, + pf->hw.func_caps.num_msix_vectors, + pf->hw.func_caps.num_msix_vectors_vf, + pf->hw.func_caps.fd_filters_guaranteed, + pf->hw.func_caps.fd_filters_best_effort, + pf->hw.func_caps.num_tx_qp, + pf->hw.func_caps.num_vsis); + } else if (list_type == i40e_aqc_opc_list_dev_capabilities) { + dev_info(&pf->pdev->dev, + "switch_mode=0x%04x, function_valid=0x%08x\n", + pf->hw.dev_caps.switch_mode, + pf->hw.dev_caps.valid_functions); + dev_info(&pf->pdev->dev, + "SR-IOV=%d, num_vfs for all function=%u\n", + pf->hw.dev_caps.sr_iov_1_1, + pf->hw.dev_caps.num_vfs); + dev_info(&pf->pdev->dev, + "num_vsis=%u, num_rx:%u, num_tx=%u\n", + pf->hw.dev_caps.num_vsis, + pf->hw.dev_caps.num_rx_qp, + pf->hw.dev_caps.num_tx_qp); + } + } + + return 0; +} + +static int i40e_vsi_clear(struct i40e_vsi *vsi); + +/** + * i40e_fdir_sb_setup - initialize the Flow Director resources for Sideband + * @pf: board private structure + **/ +static void i40e_fdir_sb_setup(struct i40e_pf *pf) +{ + struct i40e_vsi *vsi; + + if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED)) + return; + + /* find existing VSI and see if it needs configuring */ + vsi = i40e_find_vsi_by_type(pf, I40E_VSI_FDIR); + + /* create a new VSI if none exists */ + if (!vsi) { + vsi = i40e_vsi_setup(pf, I40E_VSI_FDIR, + pf->vsi[pf->lan_vsi]->seid, 0); + if (!vsi) { + dev_info(&pf->pdev->dev, "Couldn't create FDir VSI\n"); + pf->flags &= ~I40E_FLAG_FD_SB_ENABLED; + pf->flags |= I40E_FLAG_FD_SB_INACTIVE; + return; + } + } + + i40e_vsi_setup_irqhandler(vsi, i40e_fdir_clean_ring); +} + +/** + * i40e_fdir_teardown - release the Flow Director resources + * @pf: board private structure + **/ +static void i40e_fdir_teardown(struct i40e_pf *pf) +{ + struct i40e_vsi *vsi; + + i40e_fdir_filter_exit(pf); + i40e_cloud_filter_exit(pf); + vsi = i40e_find_vsi_by_type(pf, I40E_VSI_FDIR); + if (vsi) + i40e_vsi_release(vsi); +} + +/** + * i40e_prep_for_reset - prep for the core to reset + * @pf: board private structure + * @lock_acquired: indicates whether or not the lock has been acquired + * before this function was called. + * + * Close up the VFs and other things in prep for PF Reset. + **/ +static void i40e_prep_for_reset(struct i40e_pf *pf, bool lock_acquired) +{ + struct i40e_hw *hw = &pf->hw; + i40e_status ret = 0; + u32 v; + + clear_bit(__I40E_RESET_INTR_RECEIVED, pf->state); + if (test_and_set_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) + return; + if (i40e_check_asq_alive(&pf->hw)) + i40e_vc_notify_reset(pf); + + dev_dbg(&pf->pdev->dev, "Tearing down internal switch for reset\n"); + + /* TODO: warn any registered clients */ + /* quiesce the VSIs and their queues that are not already DOWN */ + /* pf_quiesce_all_vsi modifies netdev structures -rtnl_lock needed */ + if (!lock_acquired) + rtnl_lock(); + i40e_pf_quiesce_all_vsi(pf); + if (!lock_acquired) + rtnl_unlock(); + + for (v = 0; v < pf->num_alloc_vsi; v++) { + if (pf->vsi[v]) + pf->vsi[v]->seid = 0; + } + + i40e_shutdown_adminq(&pf->hw); + + /* call shutdown HMC */ + if (hw->hmc.hmc_obj) { + ret = i40e_shutdown_lan_hmc(hw); + if (ret) + dev_warn(&pf->pdev->dev, + "shutdown_lan_hmc failed: %d\n", ret); + } + +#ifdef HAVE_PTP_1588_CLOCK + /* Save the current PTP time so that we can restore the time after the + * reset completes. + */ + i40e_ptp_save_hw_time(pf); +#endif /* HAVE_PTP_1588_CLOCK */ +} + +/** + * i40e_send_version - update firmware with driver version + * @pf: PF struct + */ +static void i40e_send_version(struct i40e_pf *pf) +{ + struct i40e_driver_version dv; + + dv.major_version = DRV_VERSION_MAJOR; + dv.minor_version = DRV_VERSION_MINOR; + dv.build_version = DRV_VERSION_BUILD; + dv.subbuild_version = 0; + strlcpy(dv.driver_string, DRV_VERSION, sizeof(dv.driver_string)); + i40e_aq_send_driver_version(&pf->hw, &dv, NULL); +} + +/** + * i40e_get_oem_version - get OEM specific version information + * @hw: pointer to the hardware structure + **/ +static void i40e_get_oem_version(struct i40e_hw *hw) +{ + u16 block_offset = 0xffff; + u16 block_length = 0; + u16 capabilities = 0; + u16 gen_snap = 0; + u16 release = 0; + +#define I40E_SR_NVM_OEM_VERSION_PTR 0x1B +#define I40E_NVM_OEM_LENGTH_OFFSET 0x00 +#define I40E_NVM_OEM_CAPABILITIES_OFFSET 0x01 +#define I40E_NVM_OEM_GEN_OFFSET 0x02 +#define I40E_NVM_OEM_RELEASE_OFFSET 0x03 +#define I40E_NVM_OEM_CAPABILITIES_MASK 0x000F +#define I40E_NVM_OEM_LENGTH 3 + + /* Check if pointer to OEM version block is valid. */ + i40e_read_nvm_word(hw, I40E_SR_NVM_OEM_VERSION_PTR, &block_offset); + if ((block_offset & 0x7fff) == 0x7fff) + return; + + /* Check if OEM version block has correct length. */ + i40e_read_nvm_word(hw, block_offset + I40E_NVM_OEM_LENGTH_OFFSET, + &block_length); + if (block_length < I40E_NVM_OEM_LENGTH) + return; + + /* Check if OEM version format is as expected. */ + i40e_read_nvm_word(hw, block_offset + I40E_NVM_OEM_CAPABILITIES_OFFSET, + &capabilities); + if ((capabilities & I40E_NVM_OEM_CAPABILITIES_MASK) != 0) + return; + + i40e_read_nvm_word(hw, block_offset + I40E_NVM_OEM_GEN_OFFSET, + &gen_snap); + i40e_read_nvm_word(hw, block_offset + I40E_NVM_OEM_RELEASE_OFFSET, + &release); + hw->nvm.oem_ver = (gen_snap << I40E_OEM_SNAP_SHIFT) | release; + hw->nvm.eetrack = I40E_OEM_EETRACK_ID; +} + +/** + * i40e_reset - wait for core reset to finish reset, reset pf if corer not seen + * @pf: board private structure + **/ +static int i40e_reset(struct i40e_pf *pf) +{ + struct i40e_hw *hw = &pf->hw; + i40e_status ret; + + ret = i40e_pf_reset(hw); + if (ret) { + dev_info(&pf->pdev->dev, "PF reset failed, %d\n", ret); + set_bit(__I40E_RESET_FAILED, pf->state); + clear_bit(__I40E_RESET_RECOVERY_PENDING, pf->state); + } else { + pf->pfr_count++; + } + return ret; +} + +/** + * i40e_rebuild - rebuild using a saved config + * @pf: board private structure + * @reinit: if the Main VSI needs to re-initialized. + * @lock_acquired: indicates whether or not the lock has been acquired + * before this function was called. + **/ +static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) +{ + int old_recovery_mode_bit = test_bit(__I40E_RECOVERY_MODE, pf->state); + struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; + struct i40e_hw *hw = &pf->hw; + u8 set_fc_aq_fail = 0; + i40e_status ret; + u32 val; + int v; + + if (test_bit(__I40E_EMP_RESET_INTR_RECEIVED, pf->state) && + i40e_check_recovery_mode(pf)) { +#ifdef SIOCETHTOOL + i40e_set_ethtool_ops(pf->vsi[pf->lan_vsi]->netdev); +#endif + } + + if (test_bit(__I40E_DOWN, pf->state) && + !test_bit(__I40E_RECOVERY_MODE, pf->state) && + !old_recovery_mode_bit) + goto clear_recovery; + dev_dbg(&pf->pdev->dev, "Rebuilding internal switch\n"); + + /* rebuild the basics for the AdminQ, HMC, and initial HW switch */ + ret = i40e_init_adminq(&pf->hw); + if (ret) { + dev_info(&pf->pdev->dev, "Rebuild AdminQ failed, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + goto clear_recovery; + } + i40e_get_oem_version(&pf->hw); + + if (test_bit(__I40E_EMP_RESET_INTR_RECEIVED, pf->state) && + ((hw->aq.fw_maj_ver == 4 && hw->aq.fw_min_ver <= 33) || + hw->aq.fw_maj_ver < 4) && hw->mac.type == I40E_MAC_XL710) { + /* The following delay is necessary for 4.33 firmware and older + * to recover after EMP reset. 200 ms should suffice but we + * put here 300 ms to be sure that FW is ready to operate + * after reset. + */ + mdelay(300); + } + + /* re-verify the eeprom if we just had an EMP reset */ + if (test_and_clear_bit(__I40E_EMP_RESET_INTR_RECEIVED, pf->state)) { + i40e_verify_eeprom(pf); + } + + /* if we are going out of or into recovery mode we have to act + * accordingly with regard to resources initialization + * and deinitialization + */ + if (test_bit(__I40E_RECOVERY_MODE, pf->state) || + old_recovery_mode_bit) { + if (i40e_get_capabilities(pf, + i40e_aqc_opc_list_func_capabilities)) + goto end_unlock; + + if (test_bit(__I40E_RECOVERY_MODE, pf->state)) { + /* we're staying in recovery mode so we'll reinitialize + * misc vector here + */ + if (i40e_setup_misc_vector_for_recovery_mode(pf)) + goto end_unlock; + } else { + if (!lock_acquired) + rtnl_lock(); + /* we're going out of recovery mode so we'll free + * the IRQ allocated specifically for recovery mode + * and restore the interrupt scheme + */ + free_irq(pf->pdev->irq, pf); + i40e_clear_interrupt_scheme(pf); + if (i40e_restore_interrupt_scheme(pf)) + goto end_unlock; + } + + /* tell the firmware that we're starting */ + i40e_send_version(pf); + + /* bail out in case recovery mode was detected, as there is + * no need for further configuration. + */ + goto end_unlock; + } + + i40e_clear_pxe_mode(hw); + ret = i40e_get_capabilities(pf, i40e_aqc_opc_list_func_capabilities); + if (ret) + goto end_core_reset; + + ret = i40e_init_lan_hmc(hw, hw->func_caps.num_tx_qp, + hw->func_caps.num_rx_qp, 0, 0); + if (ret) { + dev_info(&pf->pdev->dev, "init_lan_hmc failed: %d\n", ret); + goto end_core_reset; + } + ret = i40e_configure_lan_hmc(hw, I40E_HMC_MODEL_DIRECT_ONLY); + if (ret) { + dev_info(&pf->pdev->dev, "configure_lan_hmc failed: %d\n", ret); + goto end_core_reset; + } + + /* Enable FW to write a default DCB config on link-up */ + i40e_aq_set_dcb_parameters(hw, true, NULL); + +#ifdef CONFIG_DCB + ret = i40e_init_pf_dcb(pf); + if (ret) { + dev_info(&pf->pdev->dev, "DCB init failed %d, disabled\n", ret); + pf->flags &= ~I40E_FLAG_DCB_CAPABLE; + /* Continue without DCB enabled */ + } + +#endif /* CONFIG_DCB */ + /* do basic switch setup */ + if (!lock_acquired) + rtnl_lock(); + ret = i40e_setup_pf_switch(pf, reinit); + if (ret) + goto end_unlock; + + /* The driver only wants link up/down and module qualification + * reports from firmware. Note the negative logic. + */ + ret = i40e_aq_set_phy_int_mask(&pf->hw, + ~(I40E_AQ_EVENT_LINK_UPDOWN | + I40E_AQ_EVENT_MEDIA_NA | + I40E_AQ_EVENT_MODULE_QUAL_FAIL), NULL); + if (ret) + dev_info(&pf->pdev->dev, "set phy mask fail, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + + /* make sure our flow control settings are restored */ + ret = i40e_set_fc(&pf->hw, &set_fc_aq_fail, true); + if (ret) + dev_dbg(&pf->pdev->dev, "setting flow control: ret = %s last_status = %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + + /* Rebuild the VSIs and VEBs that existed before reset. + * They are still in our local switch element arrays, so only + * need to rebuild the switch model in the HW. + * + * If there were VEBs but the reconstitution failed, we'll try + * try to recover minimal use by getting the basic PF VSI working. + */ + if (vsi->uplink_seid != pf->mac_seid) { + dev_dbg(&pf->pdev->dev, "attempting to rebuild switch\n"); + /* find the one VEB connected to the MAC, and find orphans */ + for (v = 0; v < I40E_MAX_VEB; v++) { + if (!pf->veb[v]) + continue; + + if (pf->veb[v]->uplink_seid == pf->mac_seid || + pf->veb[v]->uplink_seid == 0) { + ret = i40e_reconstitute_veb(pf->veb[v]); + + if (!ret) + continue; + + /* If Main VEB failed, we're in deep doodoo, + * so give up rebuilding the switch and set up + * for minimal rebuild of PF VSI. + * If orphan failed, we'll report the error + * but try to keep going. + */ + if (pf->veb[v]->uplink_seid == pf->mac_seid) { + dev_info(&pf->pdev->dev, + "rebuild of switch failed: %d, will try to set up simple PF connection\n", + ret); + vsi->uplink_seid = pf->mac_seid; + break; + } else if (pf->veb[v]->uplink_seid == 0) { + dev_info(&pf->pdev->dev, + "rebuild of orphan VEB failed: %d\n", + ret); + } + } + } + } + + if (vsi->uplink_seid == pf->mac_seid) { + dev_dbg(&pf->pdev->dev, "attempting to rebuild PF VSI\n"); + /* no VEB, so rebuild only the Main VSI */ + ret = i40e_add_vsi(vsi); + if (ret) { + dev_info(&pf->pdev->dev, + "rebuild of Main VSI failed: %d\n", ret); + goto end_unlock; + } + } + +#ifdef __TC_MQPRIO_MODE_MAX + if (vsi->mqprio_qopt.max_rate[0]) { + u64 max_tx_rate = vsi->mqprio_qopt.max_rate[0] / (1000000 / 8); + + ret = i40e_set_bw_limit(vsi, vsi->seid, max_tx_rate); + if (!ret) + dev_dbg(&vsi->back->pdev->dev, + "Set tx rate of %llu Mbps (count of 50Mbps %llu) for vsi->seid %u\n", + max_tx_rate, + max_tx_rate / I40E_BW_CREDIT_DIVISOR, + vsi->seid); + else + goto end_unlock; + } +#endif + +#ifdef __TC_MQPRIO_MODE_MAX + /* Not going to support channel VSI in L4 cloud filter mode */ + if (!i40e_is_l4mode_enabled()) { + /* PF Main VSI is rebuild by now, go ahead and + * rebuild channel VSIs for this main VSI if they exist + */ + ret = i40e_rebuild_channels(vsi); + if (ret) + goto end_unlock; + } +#endif + + /* Reconfigure hardware for allowing smaller MSS in the case + * of TSO, so that we avoid the MDD being fired and causing + * a reset in the case of small MSS+TSO. + */ +#define I40E_REG_MSS 0x000E64DC +#define I40E_REG_MSS_MIN_MASK 0x3FF0000 +#define I40E_64BYTE_MSS 0x400000 + val = rd32(hw, I40E_REG_MSS); + if ((val & I40E_REG_MSS_MIN_MASK) > I40E_64BYTE_MSS) { + val &= ~I40E_REG_MSS_MIN_MASK; + val |= I40E_64BYTE_MSS; + wr32(hw, I40E_REG_MSS, val); + } + + if (pf->hw_features & I40E_HW_RESTART_AUTONEG) { + msleep(75); + ret = i40e_aq_set_link_restart_an(&pf->hw, true, NULL); + if (ret) + dev_info(&pf->pdev->dev, "link restart failed, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); + } + /* reinit the misc interrupt */ + if (pf->flags & I40E_FLAG_MSIX_ENABLED) + ret = i40e_setup_misc_vector(pf); + + /* Add a filter to drop all Flow control frames from any VSI from being + * transmitted. By doing so we stop a malicious VF from sending out + * PAUSE or PFC frames and potentially controlling traffic for other + * PF/VF VSIs. + * The FW can still send Flow control frames if enabled. + */ + i40e_add_filter_to_drop_tx_flow_control_frames(&pf->hw, + pf->main_vsi_seid); + + /* restart the VSIs that were rebuilt and running before the reset */ + i40e_pf_unquiesce_all_vsi(pf); + + /* Release the RTNL lock before we start resetting VFs */ + if (!lock_acquired) + rtnl_unlock(); + + /* Restore promiscuous settings */ + ret = i40e_set_promiscuous(pf, pf->cur_promisc); + if (ret) + dev_warn(&pf->pdev->dev, + "Failed to restore promiscuous setting: %s, err %s aq_err %s\n", + pf->cur_promisc ? "on" : "off", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + /* Restore all VF-d config */ + + /* If uncommitted changes were made to the BW share settings, then warn + * the user that the configuration may not be restored correctly. + */ + if (!pf->vf_bw_applied) + dev_info(&pf->pdev->dev, "VF BW shares not restored\n"); + + if (I40E_IS_MIRROR_VLAN_ID_VALID(pf->ingress_vlan)) { + u16 rule_type; + + /* The Admin Queue mirroring rules refer to the traffic + * directions from the perspective of the switch, not the VSI + * we apply the mirroring rule on - so the behaviour of a VSI + * ingress mirror is classified as an egress rule + */ + rule_type = I40E_AQC_MIRROR_RULE_TYPE_VPORT_EGRESS; + ret = i40e_restore_ingress_egress_mirror(vsi, pf->ingress_vlan, + rule_type, + &pf->ingress_rule_id); + if (ret) + pf->ingress_vlan = I40E_NO_VF_MIRROR; + } + + if (I40E_IS_MIRROR_VLAN_ID_VALID(pf->egress_vlan)) { + u16 rule_type; + + /* The Admin Queue mirroring rules refer to the traffic + * directions from the perspective of the switch, not the VSI + * we apply the mirroring rule on - so the behaviour of a VSI + * egress mirror is classified as an ingress rule + */ + rule_type = I40E_AQC_MIRROR_RULE_TYPE_VPORT_INGRESS; + ret = i40e_restore_ingress_egress_mirror(vsi, pf->egress_vlan, + rule_type, + &pf->egress_rule_id); + if (ret) + pf->egress_vlan = I40E_NO_VF_MIRROR; + } + + i40e_reset_all_vfs(pf, true); + + /* TODO: restart clients */ + /* tell the firmware that we're starting */ + i40e_send_version(pf); + + /* We've already released the lock, so don't do it again */ + goto end_core_reset; + +end_unlock: + if (!lock_acquired) + rtnl_unlock(); +end_core_reset: + clear_bit(__I40E_RESET_FAILED, pf->state); +clear_recovery: + clear_bit(__I40E_RESET_RECOVERY_PENDING, pf->state); + clear_bit(__I40E_TIMEOUT_RECOVERY_PENDING, pf->state); +} + +/** + * i40e_reset_and_rebuild - reset and rebuild using a saved config + * @pf: board private structure + * @reinit: if the Main VSI needs to re-initialized. + * @lock_acquired: indicates whether or not the lock has been acquired + * before this function was called. + **/ +static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit, + bool lock_acquired) +{ + int ret; + /* Now we wait for GRST to settle out. + * We don't have to delete the VEBs or VSIs from the hw switch + * because the reset will make them disappear. + */ + ret = i40e_reset(pf); + if (!ret) + i40e_rebuild(pf, reinit, lock_acquired); +} + +/** + * i40e_handle_reset_warning - prep for the PF to reset, reset and rebuild + * @pf: board private structure + * + * Close up the VFs and other things in prep for a Core Reset, + * then get ready to rebuild the world. + * @lock_acquired: indicates whether or not the lock has been acquired + * before this function was called. + **/ +static void i40e_handle_reset_warning(struct i40e_pf *pf, bool lock_acquired) +{ + i40e_prep_for_reset(pf, lock_acquired); + i40e_reset_and_rebuild(pf, false, lock_acquired); +} + +/** + * i40e_handle_mdd_event + * @pf: pointer to the PF structure + * + * Called from the MDD irq handler to identify possibly malicious vfs + **/ +static void i40e_handle_mdd_event(struct i40e_pf *pf) +{ + struct i40e_hw *hw = &pf->hw; + bool mdd_detected = false; + struct i40e_vf *vf; + u32 reg; + int i; + + if (!test_bit(__I40E_MDD_EVENT_PENDING, pf->state)) + return; + + /* find what triggered the MDD event */ + reg = rd32(hw, I40E_GL_MDET_TX); + if (reg & I40E_GL_MDET_TX_VALID_MASK) { + u8 pf_num = (reg & I40E_GL_MDET_TX_PF_NUM_MASK) >> + I40E_GL_MDET_TX_PF_NUM_SHIFT; + u16 vf_num = (reg & I40E_GL_MDET_TX_VF_NUM_MASK) >> + I40E_GL_MDET_TX_VF_NUM_SHIFT; + u8 event = (reg & I40E_GL_MDET_TX_EVENT_MASK) >> + I40E_GL_MDET_TX_EVENT_SHIFT; + u16 queue = ((reg & I40E_GL_MDET_TX_QUEUE_MASK) >> + I40E_GL_MDET_TX_QUEUE_SHIFT) - + pf->hw.func_caps.base_queue; + if (netif_msg_tx_err(pf)) + dev_info(&pf->pdev->dev, "Malicious Driver Detection event 0x%02x on TX queue %d PF number 0x%02x VF number 0x%02x\n", + event, queue, pf_num, vf_num); + wr32(hw, I40E_GL_MDET_TX, 0xffffffff); + mdd_detected = true; + } + reg = rd32(hw, I40E_GL_MDET_RX); + if (reg & I40E_GL_MDET_RX_VALID_MASK) { + u8 func = (reg & I40E_GL_MDET_RX_FUNCTION_MASK) >> + I40E_GL_MDET_RX_FUNCTION_SHIFT; + u8 event = (reg & I40E_GL_MDET_RX_EVENT_MASK) >> + I40E_GL_MDET_RX_EVENT_SHIFT; + u16 queue = ((reg & I40E_GL_MDET_RX_QUEUE_MASK) >> + I40E_GL_MDET_RX_QUEUE_SHIFT) - + pf->hw.func_caps.base_queue; + if (netif_msg_rx_err(pf)) + dev_info(&pf->pdev->dev, "Malicious Driver Detection event 0x%02x on RX queue %d of function 0x%02x\n", + event, queue, func); + wr32(hw, I40E_GL_MDET_RX, 0xffffffff); + mdd_detected = true; + } + + if (mdd_detected) { + reg = rd32(hw, I40E_PF_MDET_TX); + if (reg & I40E_PF_MDET_TX_VALID_MASK) { + wr32(hw, I40E_PF_MDET_TX, 0xFFFF); + dev_dbg(&pf->pdev->dev, "TX driver issue detected on PF\n"); + } + reg = rd32(hw, I40E_PF_MDET_RX); + if (reg & I40E_PF_MDET_RX_VALID_MASK) { + wr32(hw, I40E_PF_MDET_RX, 0xFFFF); + dev_dbg(&pf->pdev->dev, "RX driver issue detected on PF\n"); + } + } + + /* see if one of the VFs needs its hand slapped */ + for (i = 0; i < pf->num_alloc_vfs && mdd_detected; i++) { + vf = &(pf->vf[i]); + reg = rd32(hw, I40E_VP_MDET_TX(i)); + if (reg & I40E_VP_MDET_TX_VALID_MASK) { + wr32(hw, I40E_VP_MDET_TX(i), 0xFFFF); + vf->num_mdd_events++; + dev_info(&pf->pdev->dev, "TX driver issue detected on VF %d\n", + i); + dev_info(&pf->pdev->dev, + "Use PF Control I/F to re-enable the VF\n"); + set_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); + } + + reg = rd32(hw, I40E_VP_MDET_RX(i)); + if (reg & I40E_VP_MDET_RX_VALID_MASK) { + wr32(hw, I40E_VP_MDET_RX(i), 0xFFFF); + vf->num_mdd_events++; + dev_info(&pf->pdev->dev, "RX driver issue detected on VF %d\n", + i); + dev_info(&pf->pdev->dev, + "Use PF Control I/F to re-enable the VF\n"); + set_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); + } + } + + /* re-enable mdd interrupt cause */ + clear_bit(__I40E_MDD_EVENT_PENDING, pf->state); + reg = rd32(hw, I40E_PFINT_ICR0_ENA); + reg |= I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK; + wr32(hw, I40E_PFINT_ICR0_ENA, reg); + i40e_flush(hw); +} + +#if defined(HAVE_VXLAN_RX_OFFLOAD) || defined(HAVE_UDP_ENC_RX_OFFLOAD) +#if defined(HAVE_UDP_ENC_TUNNEL) || defined(HAVE_UDP_ENC_RX_OFFLOAD) +static const char *i40e_tunnel_name(u8 type) +{ + switch (type) { + case UDP_TUNNEL_TYPE_VXLAN: + return "vxlan"; + case UDP_TUNNEL_TYPE_GENEVE: + return "geneve"; + default: + return "unknown"; + } +} + +/** + * i40e_sync_udp_filters - Trigger a sync event for existing UDP filters + * @pf: board private structure + **/ +static void i40e_sync_udp_filters(struct i40e_pf *pf) +{ + int i; + + /* loop through and set pending bit for all active UDP filters */ + for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { + if (pf->udp_ports[i].port) + pf->pending_udp_bitmap |= BIT_ULL(i); + } + + set_bit(__I40E_UDP_FILTER_SYNC_PENDING, pf->state); +} + +/** + * i40e_sync_udp_filters_subtask - Sync the VSI filter list with HW + * @pf: board private structure + **/ +static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf) +{ + struct i40e_hw *hw = &pf->hw; + u8 filter_index, type; + u16 port; + int i; + + if (!test_and_clear_bit(__I40E_UDP_FILTER_SYNC_PENDING, pf->state)) + return; + + /* acquire RTNL to maintain state of flags and port requests */ + rtnl_lock(); + + for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { + if (pf->pending_udp_bitmap & BIT_ULL(i)) { + struct i40e_udp_port_config *udp_port; + i40e_status ret = 0; + + udp_port = &pf->udp_ports[i]; + pf->pending_udp_bitmap &= ~BIT_ULL(i); + + port = READ_ONCE(udp_port->port); + type = READ_ONCE(udp_port->type); + filter_index = READ_ONCE(udp_port->filter_index); + + /* release RTNL while we wait on AQ command */ + rtnl_unlock(); + + if (port) + ret = i40e_aq_add_udp_tunnel(hw, port, + type, + &filter_index, + NULL); + else if (filter_index != I40E_UDP_PORT_INDEX_UNUSED) + ret = i40e_aq_del_udp_tunnel(hw, filter_index, + NULL); + + /* reacquire RTNL so we can update filter_index */ + rtnl_lock(); + + if (ret) { + dev_info(&pf->pdev->dev, + "%s %s port %d, index %d failed, err %s aq_err %s\n", + i40e_tunnel_name(type), + port ? "add" : "delete", + port, + filter_index, + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); + if (port) { + /* failed to add, just reset port, + * drop pending bit for any deletion + */ + udp_port->port = 0; + pf->pending_udp_bitmap &= ~BIT_ULL(i); + } + } else if (port) { + /* record filter index on success */ + udp_port->filter_index = filter_index; + } + } + } + + rtnl_unlock(); +} + +#endif /* HAVE_UDP_ENC_TUNNEL || HAVE_UDP_ENC_RX_OFFLOAD */ +#endif /* HAVE_VXLAN_RX_OFFLOAD || HAVE_UDP_ENC_RX_OFFLOAD */ + +/** + * i40e_service_task - Run the driver's async subtasks + * @work: pointer to work_struct containing our data + **/ +static void i40e_service_task(struct work_struct *work) +{ + struct i40e_pf *pf = container_of(work, + struct i40e_pf, + service_task); + unsigned long start_time = jiffies; + + /* don't bother with service tasks if a reset is in progress */ + if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) || + test_bit(__I40E_SUSPENDED, pf->state)) { + return; + } + + if (test_and_set_bit(__I40E_SERVICE_SCHED, pf->state)) + return; + + if (!test_bit(__I40E_RECOVERY_MODE, pf->state)) { + i40e_detect_recover_hung(pf->vsi[pf->lan_vsi]); + i40e_sync_filters_subtask(pf); + i40e_reset_subtask(pf); + i40e_handle_mdd_event(pf); + i40e_vc_process_vflr_event(pf); + i40e_watchdog_subtask(pf); + i40e_fdir_reinit_subtask(pf); + if (test_and_clear_bit(__I40E_CLIENT_RESET, pf->state)) { + /* Client subtask will reopen next time through. */ + i40e_notify_client_of_netdev_close( + pf->vsi[pf->lan_vsi], true); + } else { + i40e_client_subtask(pf); + if (test_and_clear_bit(__I40E_CLIENT_L2_CHANGE, + pf->state)) + i40e_notify_client_of_l2_param_changes( + pf->vsi[pf->lan_vsi]); + } +#if defined(HAVE_VXLAN_RX_OFFLOAD) || defined(HAVE_UDP_ENC_RX_OFFLOAD) +#if defined(HAVE_UDP_ENC_TUNNEL) || defined(HAVE_UDP_ENC_RX_OFFLOAD) + i40e_sync_udp_filters_subtask(pf); + +#endif +#endif /* HAVE_VXLAN_RX_OFFLOAD || HAVE_UDP_ENC_RX_OFFLOAD */ + } else { + i40e_reset_subtask(pf); + } + + i40e_clean_adminq_subtask(pf); + + /* flush memory to make sure state is correct before next watchdog */ + smp_mb__before_atomic(); + clear_bit(__I40E_SERVICE_SCHED, pf->state); + + /* If the tasks have taken longer than one timer cycle or there + * is more work to be done, reschedule the service task now + * rather than wait for the timer to tick again. + */ + if (time_after(jiffies, (start_time + pf->service_timer_period)) || + test_bit(__I40E_ADMINQ_EVENT_PENDING, pf->state) || + test_bit(__I40E_MDD_EVENT_PENDING, pf->state) || + test_bit(__I40E_VFLR_EVENT_PENDING, pf->state)) + i40e_service_event_schedule(pf); +} + +/** + * i40e_service_timer - timer callback + * @t: pointer to timer_list struct + **/ +static void i40e_service_timer(struct timer_list *t) +{ + struct i40e_pf *pf = from_timer(pf, t, service_timer); + + mod_timer(&pf->service_timer, + round_jiffies(jiffies + pf->service_timer_period)); + i40e_service_event_schedule(pf); +} + +/** + * i40e_set_num_rings_in_vsi - Determine number of rings in the VSI + * @vsi: the VSI being configured + **/ +static int i40e_set_num_rings_in_vsi(struct i40e_vsi *vsi) +{ + struct i40e_pf *pf = vsi->back; + + switch (vsi->type) { + case I40E_VSI_MAIN: + vsi->alloc_queue_pairs = pf->num_lan_qps; + if (!vsi->num_tx_desc) + vsi->num_tx_desc = ALIGN(I40E_DEFAULT_NUM_DESCRIPTORS, + I40E_REQ_DESCRIPTOR_MULTIPLE); + if (!vsi->num_rx_desc) + vsi->num_rx_desc = ALIGN(I40E_DEFAULT_NUM_DESCRIPTORS, + I40E_REQ_DESCRIPTOR_MULTIPLE); + if (pf->flags & I40E_FLAG_MSIX_ENABLED) { + vsi->num_q_vectors = pf->num_lan_msix; + } + else + vsi->num_q_vectors = 1; + + break; + + case I40E_VSI_FDIR: + vsi->alloc_queue_pairs = 1; + vsi->num_tx_desc = ALIGN(I40E_FDIR_RING_COUNT, + I40E_REQ_DESCRIPTOR_MULTIPLE); + vsi->num_rx_desc = ALIGN(I40E_FDIR_RING_COUNT, + I40E_REQ_DESCRIPTOR_MULTIPLE); + vsi->num_q_vectors = pf->num_fdsb_msix; + break; + + case I40E_VSI_VMDQ2: + vsi->alloc_queue_pairs = pf->num_vmdq_qps; + if (!vsi->num_tx_desc) + vsi->num_tx_desc = ALIGN(I40E_DEFAULT_NUM_DESCRIPTORS, + I40E_REQ_DESCRIPTOR_MULTIPLE); + if (!vsi->num_rx_desc) + vsi->num_rx_desc = ALIGN(I40E_DEFAULT_NUM_DESCRIPTORS, + I40E_REQ_DESCRIPTOR_MULTIPLE); + vsi->num_q_vectors = pf->num_vmdq_msix; + break; + + case I40E_VSI_SRIOV: + vsi->alloc_queue_pairs = pf->num_vf_qps; + if (!vsi->num_tx_desc) + vsi->num_tx_desc = ALIGN(I40E_DEFAULT_NUM_DESCRIPTORS, + I40E_REQ_DESCRIPTOR_MULTIPLE); + if (!vsi->num_rx_desc) + vsi->num_rx_desc = ALIGN(I40E_DEFAULT_NUM_DESCRIPTORS, + I40E_REQ_DESCRIPTOR_MULTIPLE); + break; + + default: + WARN_ON(1); + return -ENODATA; + } + + return 0; +} + +/** + * i40e_vsi_alloc_arrays - Allocate queue and vector pointer arrays for the vsi + * @vsi: VSI pointer + * @alloc_qvectors: a bool to specify if q_vectors need to be allocated. + * + * On error: returns error code (negative) + * On success: returns 0 + **/ +static int i40e_vsi_alloc_arrays(struct i40e_vsi *vsi, bool alloc_qvectors) +{ + struct i40e_ring **next_rings; + int size; + int ret = 0; + + /* allocate memory for both Tx, XDP Tx and Rx ring pointers */ + size = sizeof(struct i40e_ring *) * vsi->alloc_queue_pairs * + (i40e_enabled_xdp_vsi(vsi) ? 3 : 2); + + vsi->tx_rings = kzalloc(size, GFP_KERNEL); + if (!vsi->tx_rings) + return -ENOMEM; + next_rings = vsi->tx_rings + vsi->alloc_queue_pairs; + if (i40e_enabled_xdp_vsi(vsi)) { + vsi->xdp_rings = next_rings; + next_rings += vsi->alloc_queue_pairs; + } + vsi->rx_rings = next_rings; + + if (alloc_qvectors) { + /* allocate memory for q_vector pointers */ + size = sizeof(struct i40e_q_vector *) * vsi->num_q_vectors; + vsi->q_vectors = kzalloc(size, GFP_KERNEL); + if (!vsi->q_vectors) { + ret = -ENOMEM; + goto err_vectors; + } + } + return ret; + +err_vectors: + kfree(vsi->tx_rings); + return ret; +} + +/** + * i40e_vsi_mem_alloc - Allocates the next available struct vsi in the PF + * @pf: board private structure + * @type: type of VSI + * + * On error: returns error code (negative) + * On success: returns vsi index in PF (positive) + **/ +int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type) +{ + int ret = -ENODEV; + struct i40e_vsi *vsi; + int vsi_idx; + int i; + + /* Need to protect the allocation of the VSIs at the PF level */ + mutex_lock(&pf->switch_mutex); + + /* VSI list may be fragmented if VSI creation/destruction has + * been happening. We can afford to do a quick scan to look + * for any free VSIs in the list. + * + * find next empty vsi slot, looping back around if necessary + */ + i = pf->next_vsi; + while (i < pf->num_alloc_vsi && pf->vsi[i]) + i++; + if (i >= pf->num_alloc_vsi) { + i = 0; + while (i < pf->next_vsi && pf->vsi[i]) + i++; + } + + if (i < pf->num_alloc_vsi && !pf->vsi[i]) { + vsi_idx = i; /* Found one! */ + } else { + ret = -ENODEV; + goto unlock_pf; /* out of VSI slots! */ + } + pf->next_vsi = ++i; + + vsi = kzalloc(sizeof(*vsi), GFP_KERNEL); + if (!vsi) { + ret = -ENOMEM; + goto unlock_pf; + } + vsi->type = type; + vsi->back = pf; + set_bit(__I40E_VSI_DOWN, vsi->state); + vsi->flags = 0; + vsi->idx = vsi_idx; + vsi->int_rate_limit = 0; + vsi->rss_table_size = (vsi->type == I40E_VSI_MAIN) ? + pf->rss_table_size : 64; + vsi->netdev_registered = false; + vsi->work_limit = I40E_DEFAULT_IRQ_WORK; + hash_init(vsi->mac_filter_hash); + vsi->irqs_ready = false; + + ret = i40e_set_num_rings_in_vsi(vsi); + if (ret) + goto err_rings; + + vsi->block_tx_timeout = false; + + ret = i40e_vsi_alloc_arrays(vsi, true); + if (ret) + goto err_rings; + + /* Setup default MSIX irq handler for VSI */ + i40e_vsi_setup_irqhandler(vsi, i40e_msix_clean_rings); + + /* Initialize VSI lock */ + spin_lock_init(&vsi->mac_filter_hash_lock); + pf->vsi[vsi_idx] = vsi; + ret = vsi_idx; + + goto unlock_pf; + +err_rings: + pf->next_vsi = i - 1; + kfree(vsi); +unlock_pf: + mutex_unlock(&pf->switch_mutex); + return ret; +} + +/** + * i40e_vsi_free_arrays - Free queue and vector pointer arrays for the VSI + * @vsi: VSI pointer + * @free_qvectors: a bool to specify if q_vectors need to be freed. + * + * On error: returns error code (negative) + * On success: returns 0 + **/ +static void i40e_vsi_free_arrays(struct i40e_vsi *vsi, bool free_qvectors) +{ + /* free the ring and vector containers */ + if (free_qvectors) { + kfree(vsi->q_vectors); + vsi->q_vectors = NULL; + } + kfree(vsi->tx_rings); + vsi->tx_rings = NULL; + vsi->rx_rings = NULL; + vsi->xdp_rings = NULL; +} + +/** + * i40e_vsi_clear - Deallocate the VSI provided + * @vsi: the VSI being un-configured + **/ +static int i40e_vsi_clear(struct i40e_vsi *vsi) +{ + struct i40e_pf *pf; + + if (!vsi) + return 0; + + if (!vsi->back) + goto free_vsi; + pf = vsi->back; + + mutex_lock(&pf->switch_mutex); + if (!pf->vsi[vsi->idx]) { + dev_err(&pf->pdev->dev, "pf->vsi[%d] is NULL, just free vsi[%d](type %d)\n", + vsi->idx, vsi->idx, vsi->type); + goto unlock_vsi; + } + + if (pf->vsi[vsi->idx] != vsi) { + dev_err(&pf->pdev->dev, + "pf->vsi[%d](type %d) != vsi[%d](type %d): no free!\n", + pf->vsi[vsi->idx]->idx, + pf->vsi[vsi->idx]->type, + vsi->idx, vsi->type); + goto unlock_vsi; + } + + /* updates the PF for this cleared vsi */ + i40e_put_lump(pf->qp_pile, vsi->base_queue, vsi->idx); + i40e_put_lump(pf->irq_pile, vsi->base_vector, vsi->idx); + + i40e_vsi_free_arrays(vsi, true); + i40e_clear_rss_config_user(vsi); + + pf->vsi[vsi->idx] = NULL; + if (vsi->idx < pf->next_vsi) + pf->next_vsi = vsi->idx; + +unlock_vsi: + mutex_unlock(&pf->switch_mutex); +free_vsi: + kfree(vsi); + + return 0; +} + +/** + * i40e_vsi_clear_rings - Deallocates the Rx and Tx rings for the provided VSI + * @vsi: the VSI being cleaned + **/ +static void i40e_vsi_clear_rings(struct i40e_vsi *vsi) +{ + int i; + + if (vsi->tx_rings && vsi->tx_rings[0]) { + for (i = 0; i < vsi->alloc_queue_pairs; i++) { + kfree_rcu(vsi->tx_rings[i], rcu); + vsi->tx_rings[i] = NULL; + vsi->rx_rings[i] = NULL; + if (vsi->xdp_rings) + vsi->xdp_rings[i] = NULL; + } + } +} + +/** + * i40e_alloc_rings - Allocates the Rx and Tx rings for the provided VSI + * @vsi: the VSI being configured + **/ +static int i40e_alloc_rings(struct i40e_vsi *vsi) +{ + int i, qpv = i40e_enabled_xdp_vsi(vsi) ? 3 : 2; + struct i40e_pf *pf = vsi->back; + struct i40e_ring *ring; + + /* Set basic values in the rings to be used later during open() */ + for (i = 0; i < vsi->alloc_queue_pairs; i++) { + /* allocate space for both Tx and Rx in one shot */ + ring = kcalloc(qpv, sizeof(struct i40e_ring), GFP_KERNEL); + if (!ring) + goto err_out; + + ring->queue_index = i; + ring->reg_idx = vsi->base_queue + i; + ring->ring_active = false; + ring->vsi = vsi; + ring->netdev = vsi->netdev; + ring->dev = &pf->pdev->dev; + ring->count = vsi->num_tx_desc; + ring->size = 0; + ring->dcb_tc = 0; + + if (vsi->back->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) + ring->flags = I40E_TXR_FLAGS_WB_ON_ITR; + ring->itr_setting = pf->tx_itr_default; + vsi->tx_rings[i] = ring++; + + if (!i40e_enabled_xdp_vsi(vsi)) + goto setup_rx; + + ring->queue_index = vsi->alloc_queue_pairs + i; + ring->reg_idx = vsi->base_queue + ring->queue_index; + ring->ring_active = false; + ring->vsi = vsi; + ring->netdev = NULL; + ring->dev = &pf->pdev->dev; + ring->count = vsi->num_tx_desc; + ring->size = 0; + ring->dcb_tc = 0; + if (vsi->back->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) + ring->flags = I40E_TXR_FLAGS_WB_ON_ITR; + set_ring_xdp(ring); + ring->itr_setting = pf->tx_itr_default; + vsi->xdp_rings[i] = ring++; + +setup_rx: + ring->queue_index = i; + ring->reg_idx = vsi->base_queue + i; + ring->ring_active = false; + ring->vsi = vsi; + ring->netdev = vsi->netdev; + ring->dev = &pf->pdev->dev; + ring->count = vsi->num_rx_desc; + ring->size = 0; + ring->dcb_tc = 0; + ring->itr_setting = pf->rx_itr_default; + vsi->rx_rings[i] = ring; + } + + return 0; + +err_out: + i40e_vsi_clear_rings(vsi); + return -ENOMEM; +} +#if !defined(I40E_LEGACY_INTERRUPT) && !defined(I40E_MSI_INTERRUPT) + +/** + * i40e_reserve_msix_vectors - Reserve MSI-X vectors in the kernel + * @pf: board private structure + * @v_budget: the number of MSI-X vectors to request + * + * Returns the number of vectors reserved, or error + **/ +static int i40e_reserve_msix_vectors(struct i40e_pf *pf, int v_budget) +{ + int v_actual = 0; + + v_actual = pci_enable_msix_range(pf->pdev, + pf->msix_entries, + I40E_MIN_MSIX, + v_budget); + if (v_actual < 0) + dev_info(&pf->pdev->dev, + "MSI-X vector reservation failed: %d\n", v_actual); + + return v_actual; +} + +/** + * i40e_init_msix - Setup the MSIX capability + * @pf: board private structure + * + * Work with the OS to set up the MSIX vectors needed. + * + * Returns the number of vectors reserved or negative on failure + **/ +static int i40e_init_msix(struct i40e_pf *pf) +{ + struct i40e_hw *hw = &pf->hw; + int cpus, extra_vectors; + int vectors_left; + int v_budget, i; + int v_actual; + int iwarp_requested = 0; + + if (!(pf->flags & I40E_FLAG_MSIX_ENABLED)) + return -ENODEV; + + /* The number of vectors we'll request will be comprised of: + * - Add 1 for "other" cause for Admin Queue events, etc. + * - The number of LAN queue pairs + * - This takes into account queues for each TC in DCB mode. + * - Queues being used for RSS. + * We don't need as many as max_rss_size vectors. + * use rss_size instead in the calculation since that + * is governed by number of cpus in the system. + * - assumes symmetric Tx/Rx pairing + * - The number of VMDq pairs + * - The CPU count within the NUMA node if iWARP is enabled + * Once we count this up, try the request. + * + * If we can't get what we want, we'll simplify to nearly nothing + * and try again. If that still fails, we punt. + */ + vectors_left = hw->func_caps.num_msix_vectors; + v_budget = 0; + + /* reserve one vector for miscellaneous handler */ + if (vectors_left) { + v_budget++; + vectors_left--; + } + + /* reserve some vectors for the main PF traffic queues. Initially we + * only reserve at most 50% of the available vectors, in the case that + * the number of online CPUs is large. This ensures that we can enable + * extra features as well. Once we've enabled the other features, we + * will use any remaining vectors to reach as close as we can to the + * number of online CPUs. + */ + cpus = num_online_cpus(); + pf->num_lan_msix = min_t(int, cpus, vectors_left / 2); + vectors_left -= pf->num_lan_msix; + + /* reserve one vector for sideband flow director */ + if (pf->flags & I40E_FLAG_FD_SB_ENABLED) { + if (vectors_left) { + pf->num_fdsb_msix = 1; + v_budget++; + vectors_left--; + } else { + pf->num_fdsb_msix = 0; + } + } + + /* can we reserve enough for iWARP? */ + if (pf->flags & I40E_FLAG_IWARP_ENABLED) { + iwarp_requested = pf->num_iwarp_msix; + + if (!vectors_left) + pf->num_iwarp_msix = 0; + else if (vectors_left < pf->num_iwarp_msix) + pf->num_iwarp_msix = 1; + v_budget += pf->num_iwarp_msix; + vectors_left -= pf->num_iwarp_msix; + } + + /* any vectors left over go for VMDq support */ + if (pf->flags & I40E_FLAG_VMDQ_ENABLED) { + if (!vectors_left) { + pf->num_vmdq_msix = 0; + pf->num_vmdq_qps = 0; + } else { + int vmdq_vecs_wanted = + pf->num_vmdq_vsis * pf->num_vmdq_qps; + int vmdq_vecs = + min_t(int, vectors_left, vmdq_vecs_wanted); + + /* if we're short on vectors for what's desired, we + * limit the queues per vmdq. If this is still more + * than are available, the user will need to change + * the number of queues/vectors used by the PF later + * with the ethtool channels command + */ + if (vectors_left < vmdq_vecs_wanted) { + pf->num_vmdq_qps = 1; + vmdq_vecs_wanted = pf->num_vmdq_vsis; + vmdq_vecs = min_t(int, + vectors_left, + vmdq_vecs_wanted); + } + pf->num_vmdq_msix = pf->num_vmdq_qps; + + v_budget += vmdq_vecs; + vectors_left -= vmdq_vecs; + } + } + + /* On systems with a large number of SMP cores, we previously limited + * the number of vectors for num_lan_msix to be at most 50% of the + * available vectors, to allow for other features. Now, we add back + * the remaining vectors. However, we ensure that the total + * num_lan_msix will not exceed num_online_cpus(). To do this, we + * calculate the number of vectors we can add without going over the + * cap of CPUs. For systems with a small number of CPUs this will be + * zero. + */ + extra_vectors = min_t(int, cpus - pf->num_lan_msix, vectors_left); + pf->num_lan_msix += extra_vectors; + vectors_left -= extra_vectors; + + WARN(vectors_left < 0, + "Calculation of remaining vectors underflowed. This is an accounting bug when determining total MSI-X vectors.\n"); + + v_budget += pf->num_lan_msix; + pf->msix_entries = kcalloc(v_budget, sizeof(struct msix_entry), + GFP_KERNEL); + if (!pf->msix_entries) + return -ENOMEM; + + for (i = 0; i < v_budget; i++) + pf->msix_entries[i].entry = i; + v_actual = i40e_reserve_msix_vectors(pf, v_budget); + if (v_actual < I40E_MIN_MSIX) { + pf->flags &= ~I40E_FLAG_MSIX_ENABLED; + kfree(pf->msix_entries); + pf->msix_entries = NULL; + pci_disable_msix(pf->pdev); + return -ENODEV; + } + + if (v_actual == I40E_MIN_MSIX) { + /* Adjust for minimal MSIX use */ + pf->num_vmdq_vsis = 0; + pf->num_vmdq_qps = 0; + pf->num_lan_qps = 1; + pf->num_lan_msix = 1; + + } else if (!vectors_left) { + /* If we have limited resources, we will start with no vectors + * for the special features and then allocate vectors to some + * of these features based on the policy and at the end disable + * the features that did not get any vectors. + */ + int vec; + + dev_info(&pf->pdev->dev, + "MSI-X vector limit reached, attempting to redistribute vectors\n"); + /* reserve the misc vector */ + vec = v_actual - 1; + + /* Scale vector usage down */ + pf->num_vmdq_msix = 1; /* force VMDqs to only one vector */ + pf->num_vmdq_vsis = 1; + pf->num_vmdq_qps = 1; + + /* partition out the remaining vectors */ + switch (vec) { + case 2: + pf->num_lan_msix = 1; + break; + case 3: + if (pf->flags & I40E_FLAG_IWARP_ENABLED) { + pf->num_lan_msix = 1; + pf->num_iwarp_msix = 1; + } + break; + default: + if (pf->flags & I40E_FLAG_IWARP_ENABLED) { + pf->num_iwarp_msix = min_t(int, (vec / 3), + iwarp_requested); + pf->num_vmdq_vsis = min_t(int, (vec / 3), + I40E_DEFAULT_NUM_VMDQ_VSI); + } else { + pf->num_vmdq_vsis = min_t(int, (vec / 2), + I40E_DEFAULT_NUM_VMDQ_VSI); + } + if (pf->flags & I40E_FLAG_FD_SB_ENABLED) { + pf->num_fdsb_msix = 1; + vec--; + } + pf->num_lan_msix = min_t(int, + (vec - (pf->num_iwarp_msix + pf->num_vmdq_vsis)), + pf->num_lan_msix); + break; + } + } + + if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) && + (pf->num_fdsb_msix == 0)) { + dev_info(&pf->pdev->dev, "Sideband Flowdir disabled, not enough MSI-X vectors\n"); + pf->flags &= ~I40E_FLAG_FD_SB_ENABLED; + pf->flags |= I40E_FLAG_FD_SB_INACTIVE; + } + if ((pf->flags & I40E_FLAG_VMDQ_ENABLED) && + (pf->num_vmdq_msix == 0)) { + dev_info(&pf->pdev->dev, "VMDq disabled, not enough MSI-X vectors\n"); + pf->flags &= ~I40E_FLAG_VMDQ_ENABLED; + } + + if ((pf->flags & I40E_FLAG_IWARP_ENABLED) && + (pf->num_iwarp_msix == 0)) { + dev_info(&pf->pdev->dev, "IWARP disabled, not enough MSI-X vectors\n"); + pf->flags &= ~I40E_FLAG_IWARP_ENABLED; + } + return v_actual; +} +#endif + +/** + * i40e_vsi_alloc_q_vector - Allocate memory for a single interrupt vector + * @vsi: the VSI being configured + * @v_idx: index of the vector in the vsi struct + * + * We allocate one q_vector. If allocation fails we return -ENOMEM. + **/ +static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx) +{ + struct i40e_q_vector *q_vector; + + /* allocate q_vector */ + q_vector = kzalloc(sizeof(struct i40e_q_vector), GFP_KERNEL); + if (!q_vector) + return -ENOMEM; + + q_vector->vsi = vsi; + q_vector->v_idx = v_idx; +#ifdef HAVE_IRQ_AFFINITY_NOTIFY + cpumask_copy(&q_vector->affinity_mask, cpu_possible_mask); +#endif + if (vsi->netdev) + netif_napi_add(vsi->netdev, &q_vector->napi, + i40e_napi_poll, NAPI_POLL_WEIGHT); + + /* tie q_vector and vsi together */ + vsi->q_vectors[v_idx] = q_vector; -err_rings: - pf->next_vsi = i - 1; - kfree(vsi); -unlock_pf: - mutex_unlock(&pf->switch_mutex); - return ret; + return 0; } /** - * i40e_vsi_free_arrays - Free queue and vector pointer arrays for the VSI - * @type: VSI pointer - * @free_qvectors: a bool to specify if q_vectors need to be freed. + * i40e_vsi_alloc_q_vectors - Allocate memory for interrupt vectors + * @vsi: the VSI being configured * - * On error: returns error code (negative) - * On success: returns 0 + * We allocate one q_vector per queue interrupt. If allocation fails we + * return -ENOMEM. **/ -static void i40e_vsi_free_arrays(struct i40e_vsi *vsi, bool free_qvectors) +static int i40e_vsi_alloc_q_vectors(struct i40e_vsi *vsi) { - /* free the ring and vector containers */ - if (free_qvectors) { - kfree(vsi->q_vectors); - vsi->q_vectors = NULL; + struct i40e_pf *pf = vsi->back; + int v_idx, num_q_vectors; + int err; + + /* if not MSIX, give the one vector only to the LAN VSI */ + if (pf->flags & I40E_FLAG_MSIX_ENABLED) + num_q_vectors = vsi->num_q_vectors; + else if (vsi == pf->vsi[pf->lan_vsi]) + num_q_vectors = 1; + else + return -EINVAL; + + for (v_idx = 0; v_idx < num_q_vectors; v_idx++) { + err = i40e_vsi_alloc_q_vector(vsi, v_idx); + if (err) + goto err_out; } - kfree(vsi->tx_rings); - vsi->tx_rings = NULL; - vsi->rx_rings = NULL; - vsi->xdp_rings = NULL; -} -/** - * i40e_clear_rss_config_user - clear the user configured RSS hash keys - * and lookup table - * @vsi: Pointer to VSI structure - */ -static void i40e_clear_rss_config_user(struct i40e_vsi *vsi) -{ - if (!vsi) - return; + return 0; - kfree(vsi->rss_hkey_user); - vsi->rss_hkey_user = NULL; +err_out: + while (v_idx--) + i40e_free_q_vector(vsi, v_idx); - kfree(vsi->rss_lut_user); - vsi->rss_lut_user = NULL; + return err; } /** - * i40e_vsi_clear - Deallocate the VSI provided - * @vsi: the VSI being un-configured + * i40e_init_interrupt_scheme - Determine proper interrupt scheme + * @pf: board private structure to initialize **/ -static int i40e_vsi_clear(struct i40e_vsi *vsi) +static int i40e_init_interrupt_scheme(struct i40e_pf *pf) { - struct i40e_pf *pf; - - if (!vsi) - return 0; + int vectors = 0; + ssize_t size; - if (!vsi->back) - goto free_vsi; - pf = vsi->back; + if (pf->flags & I40E_FLAG_MSIX_ENABLED) { +#if !defined(I40E_LEGACY_INTERRUPT) && !defined(I40E_MSI_INTERRUPT) + vectors = i40e_init_msix(pf); +#else + vectors = -1; +#endif + if (vectors < 0) { + pf->flags &= ~(I40E_FLAG_MSIX_ENABLED | + I40E_FLAG_IWARP_ENABLED | + I40E_FLAG_RSS_ENABLED | + I40E_FLAG_DCB_CAPABLE | + I40E_FLAG_DCB_ENABLED | + I40E_FLAG_SRIOV_ENABLED | + I40E_FLAG_FD_SB_ENABLED | + I40E_FLAG_FD_ATR_ENABLED | + I40E_FLAG_VMDQ_ENABLED); + pf->flags |= I40E_FLAG_FD_SB_INACTIVE; - mutex_lock(&pf->switch_mutex); - if (!pf->vsi[vsi->idx]) { - dev_err(&pf->pdev->dev, "pf->vsi[%d] is NULL, just free vsi[%d](%p,type %d)\n", - vsi->idx, vsi->idx, vsi, vsi->type); - goto unlock_vsi; + /* rework the queue expectations without MSIX */ + i40e_determine_queue_usage(pf); + } } - if (pf->vsi[vsi->idx] != vsi) { - dev_err(&pf->pdev->dev, - "pf->vsi[%d](%p, type %d) != vsi[%d](%p,type %d): no free!\n", - pf->vsi[vsi->idx]->idx, - pf->vsi[vsi->idx], - pf->vsi[vsi->idx]->type, - vsi->idx, vsi, vsi->type); - goto unlock_vsi; + if (!(pf->flags & I40E_FLAG_MSIX_ENABLED) && + (pf->flags & I40E_FLAG_MSI_ENABLED)) { + dev_info(&pf->pdev->dev, "MSI-X not available, trying MSI\n"); +#ifndef I40E_LEGACY_INTERRUPT + vectors = pci_enable_msi(pf->pdev); +#else + vectors = -1; +#endif + if (vectors < 0) { + dev_info(&pf->pdev->dev, "MSI init failed - %d\n", + vectors); + pf->flags &= ~I40E_FLAG_MSI_ENABLED; + } + vectors = 1; /* one MSI or Legacy vector */ } - /* updates the PF for this cleared vsi */ - i40e_put_lump(pf->qp_pile, vsi->base_queue, vsi->idx); - i40e_put_lump(pf->irq_pile, vsi->base_vector, vsi->idx); - - i40e_vsi_free_arrays(vsi, true); - i40e_clear_rss_config_user(vsi); + if (!(pf->flags & (I40E_FLAG_MSIX_ENABLED | I40E_FLAG_MSI_ENABLED))) + dev_info(&pf->pdev->dev, "MSI-X and MSI not available, falling back to Legacy IRQ\n"); - pf->vsi[vsi->idx] = NULL; - if (vsi->idx < pf->next_vsi) - pf->next_vsi = vsi->idx; + /* set up vector assignment tracking */ + size = sizeof(struct i40e_lump_tracking) + (sizeof(u16) * vectors); + pf->irq_pile = kzalloc(size, GFP_KERNEL); + if (!pf->irq_pile) { + dev_err(&pf->pdev->dev, "error allocating irq_pile memory\n"); + return -ENOMEM; + } + pf->irq_pile->num_entries = vectors; + pf->irq_pile->search_hint = 0; -unlock_vsi: - mutex_unlock(&pf->switch_mutex); -free_vsi: - kfree(vsi); + /* track first vector for misc interrupts, ignore return */ + (void)i40e_get_lump(pf, pf->irq_pile, 1, I40E_PILE_VALID_BIT-1); return 0; } /** - * i40e_vsi_clear_rings - Deallocates the Rx and Tx rings for the provided VSI - * @vsi: the VSI being cleaned + * i40e_setup_misc_vector_for_recovery_mode - Setup the misc vector to handle + * non queue events in recovery mode + * @pf: board private structure + * + * This sets up the handler for MSIX 0 or MSI/legacy, which is used to manage + * the non-queue interrupts, e.g. AdminQ and errors in recovery mode. + * This is handled differently than in recovery mode since no Tx/Rx resources + * are being allocated. **/ -static void i40e_vsi_clear_rings(struct i40e_vsi *vsi) +static int i40e_setup_misc_vector_for_recovery_mode(struct i40e_pf *pf) { - int i; + int err; - if (vsi->tx_rings && vsi->tx_rings[0]) { - for (i = 0; i < vsi->alloc_queue_pairs; i++) { - kfree_rcu(vsi->tx_rings[i], rcu); - vsi->tx_rings[i] = NULL; - vsi->rx_rings[i] = NULL; - if (vsi->xdp_rings) - vsi->xdp_rings[i] = NULL; + if (pf->flags & I40E_FLAG_MSIX_ENABLED) { + err = i40e_setup_misc_vector(pf); + + if (err) { + dev_info(&pf->pdev->dev, + "MSI-X misc vector request failed, error %d\n", + err); + return err; + } + } else { + u32 flags = pf->flags & I40E_FLAG_MSI_ENABLED ? 0 : IRQF_SHARED; + + err = request_irq(pf->pdev->irq, i40e_intr, flags, + pf->int_name, pf); + + if (err) { + dev_info(&pf->pdev->dev, + "MSI/legacy misc vector request failed, error %d\n", + err); + return err; } + i40e_enable_misc_int_causes(pf); + i40e_irq_dynamic_enable_icr0(pf); } + + return 0; } /** - * i40e_alloc_rings - Allocates the Rx and Tx rings for the provided VSI - * @vsi: the VSI being configured + * i40e_setup_misc_vector - Setup the misc vector to handle non queue events + * @pf: board private structure + * + * This sets up the handler for MSIX 0, which is used to manage the + * non-queue interrupts, e.g. AdminQ and errors. This is not used + * when in MSI or Legacy interrupt mode. **/ -static int i40e_alloc_rings(struct i40e_vsi *vsi) +static int i40e_setup_misc_vector(struct i40e_pf *pf) { - int i, qpv = i40e_enabled_xdp_vsi(vsi) ? 3 : 2; - struct i40e_pf *pf = vsi->back; - struct i40e_ring *ring; + struct i40e_hw *hw = &pf->hw; + int err = 0; - /* Set basic values in the rings to be used later during open() */ - for (i = 0; i < vsi->alloc_queue_pairs; i++) { - /* allocate space for both Tx and Rx in one shot */ - ring = kcalloc(qpv, sizeof(struct i40e_ring), GFP_KERNEL); - if (!ring) - goto err_out; + /* Only request the IRQ once, the first time through. */ + if (!test_and_set_bit(__I40E_MISC_IRQ_REQUESTED, pf->state)) { + err = request_irq(pf->msix_entries[0].vector, + i40e_intr, 0, pf->int_name, pf); + if (err) { + clear_bit(__I40E_MISC_IRQ_REQUESTED, pf->state); + dev_info(&pf->pdev->dev, + "request_irq for %s failed: %d\n", + pf->int_name, err); + return -EFAULT; + } + } - ring->queue_index = i; - ring->reg_idx = vsi->base_queue + i; - ring->ring_active = false; - ring->vsi = vsi; - ring->netdev = vsi->netdev; - ring->dev = &pf->pdev->dev; - ring->count = vsi->num_desc; - ring->size = 0; - ring->dcb_tc = 0; - if (vsi->back->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) - ring->flags = I40E_TXR_FLAGS_WB_ON_ITR; - ring->tx_itr_setting = pf->tx_itr_default; - vsi->tx_rings[i] = ring++; + i40e_enable_misc_int_causes(pf); - if (!i40e_enabled_xdp_vsi(vsi)) - goto setup_rx; + /* associate no queues to the misc vector */ + wr32(hw, I40E_PFINT_LNKLST0, I40E_QUEUE_END_OF_LIST); + wr32(hw, I40E_PFINT_ITR0(I40E_RX_ITR), I40E_ITR_8K >> 1); - ring->queue_index = vsi->alloc_queue_pairs + i; - ring->reg_idx = vsi->base_queue + ring->queue_index; - ring->ring_active = false; - ring->vsi = vsi; - ring->netdev = NULL; - ring->dev = &pf->pdev->dev; - ring->count = vsi->num_desc; - ring->size = 0; - ring->dcb_tc = 0; - if (vsi->back->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) - ring->flags = I40E_TXR_FLAGS_WB_ON_ITR; - set_ring_xdp(ring); - ring->tx_itr_setting = pf->tx_itr_default; - vsi->xdp_rings[i] = ring++; + i40e_flush(hw); -setup_rx: - ring->queue_index = i; - ring->reg_idx = vsi->base_queue + i; - ring->ring_active = false; - ring->vsi = vsi; - ring->netdev = vsi->netdev; - ring->dev = &pf->pdev->dev; - ring->count = vsi->num_desc; - ring->size = 0; - ring->dcb_tc = 0; - ring->rx_itr_setting = pf->rx_itr_default; - vsi->rx_rings[i] = ring; + i40e_irq_dynamic_enable_icr0(pf); + + return err; +} + +/** + * i40e_restore_interrupt_scheme - Restore the interrupt scheme + * @pf: private board data structure + * + * Restore the interrupt scheme that was cleared when we suspended the + * device. This should be called during resume to re-allocate the q_vectors + * and reacquire IRQs. + */ +static int i40e_restore_interrupt_scheme(struct i40e_pf *pf) +{ + int err, i; + + /* We cleared the MSI and MSI-X flags when disabling the old interrupt + * scheme. We need to re-enabled them here in order to attempt to + * re-acquire the MSI or MSI-X vectors + */ + pf->flags |= (I40E_FLAG_MSIX_ENABLED | I40E_FLAG_MSI_ENABLED); + + err = i40e_init_interrupt_scheme(pf); + if (err) + return err; + + /* Now that we've re-acquired IRQs, we need to remap the vectors and + * rings together again. + */ + for (i = 0; i < pf->num_alloc_vsi; i++) { + if (pf->vsi[i]) { + err = i40e_vsi_alloc_q_vectors(pf->vsi[i]); + if (err) + goto err_unwind; + i40e_vsi_map_rings_to_vectors(pf->vsi[i]); + } } + err = i40e_setup_misc_vector(pf); + if (err) + goto err_unwind; + + if (pf->flags & I40E_FLAG_IWARP_ENABLED) + i40e_client_update_msix_info(pf); + return 0; -err_out: - i40e_vsi_clear_rings(vsi); - return -ENOMEM; +err_unwind: + while (i--) { + if (pf->vsi[i]) + i40e_vsi_free_q_vectors(pf->vsi[i]); + } + + return err; } /** - * i40e_reserve_msix_vectors - Reserve MSI-X vectors in the kernel - * @pf: board private structure - * @vectors: the number of MSI-X vectors to request + * i40e_get_rss_aq - Get RSS keys and lut by using AQ commands + * @vsi: Pointer to vsi structure + * @seed: Buffter to store the hash keys + * @lut: Buffer to store the lookup table entries + * @lut_size: Size of buffer to store the lookup table entries * - * Returns the number of vectors reserved, or error - **/ -static int i40e_reserve_msix_vectors(struct i40e_pf *pf, int vectors) + * Return 0 on success, negative on failure + */ +static int i40e_get_rss_aq(struct i40e_vsi *vsi, const u8 *seed, + u8 *lut, u16 lut_size) { - vectors = pci_enable_msix_range(pf->pdev, pf->msix_entries, - I40E_MIN_MSIX, vectors); - if (vectors < 0) { - dev_info(&pf->pdev->dev, - "MSI-X vector reservation failed: %d\n", vectors); - vectors = 0; + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; + int ret = 0; + + if (seed) { + ret = i40e_aq_get_rss_key(hw, vsi->id, + (struct i40e_aqc_get_set_rss_key_data *)seed); + if (ret) { + dev_info(&pf->pdev->dev, + "Cannot get RSS key, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); + return ret; + } } - return vectors; + if (lut) { + bool pf_lut = vsi->type == I40E_VSI_MAIN ? true : false; + + ret = i40e_aq_get_rss_lut(hw, vsi->id, pf_lut, lut, lut_size); + if (ret) { + dev_info(&pf->pdev->dev, + "Cannot get RSS lut, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); + return ret; + } + } + return ret; } /** - * i40e_init_msix - Setup the MSIX capability - * @pf: board private structure - * - * Work with the OS to set up the MSIX vectors needed. + * i40e_config_rss_reg - Configure RSS keys and lut by writing registers + * @vsi: Pointer to vsi structure + * @seed: RSS hash seed + * @lut: Lookup table + * @lut_size: Lookup table size * - * Returns the number of vectors reserved or negative on failure + * Returns 0 on success, negative on failure **/ -static int i40e_init_msix(struct i40e_pf *pf) +static int i40e_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed, + const u8 *lut, u16 lut_size) { + struct i40e_pf *pf = vsi->back; struct i40e_hw *hw = &pf->hw; - int cpus, extra_vectors; - int vectors_left; - int v_budget, i; - int v_actual; - int iwarp_requested = 0; - - if (!(pf->flags & I40E_FLAG_MSIX_ENABLED)) - return -ENODEV; - - /* The number of vectors we'll request will be comprised of: - * - Add 1 for "other" cause for Admin Queue events, etc. - * - The number of LAN queue pairs - * - Queues being used for RSS. - * We don't need as many as max_rss_size vectors. - * use rss_size instead in the calculation since that - * is governed by number of cpus in the system. - * - assumes symmetric Tx/Rx pairing - * - The number of VMDq pairs - * - The CPU count within the NUMA node if iWARP is enabled - * Once we count this up, try the request. - * - * If we can't get what we want, we'll simplify to nearly nothing - * and try again. If that still fails, we punt. - */ - vectors_left = hw->func_caps.num_msix_vectors; - v_budget = 0; + u16 vf_id = vsi->vf_id; + u8 i; - /* reserve one vector for miscellaneous handler */ - if (vectors_left) { - v_budget++; - vectors_left--; + /* Fill out hash function seed */ + if (seed) { + u32 *seed_dw = (u32 *)seed; + if (vsi->type == I40E_VSI_MAIN) { + for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) + wr32(hw, I40E_PFQF_HKEY(i), seed_dw[i]); + } else if (vsi->type == I40E_VSI_SRIOV) { + for (i = 0; i <= I40E_VFQF_HKEY1_MAX_INDEX; i++) + wr32(hw, I40E_VFQF_HKEY1(i, vf_id), seed_dw[i]); + } else { + dev_err(&pf->pdev->dev, "Cannot set RSS seed - invalid VSI type\n"); + } } - /* reserve some vectors for the main PF traffic queues. Initially we - * only reserve at most 50% of the available vectors, in the case that - * the number of online CPUs is large. This ensures that we can enable - * extra features as well. Once we've enabled the other features, we - * will use any remaining vectors to reach as close as we can to the - * number of online CPUs. - */ - cpus = num_online_cpus(); - pf->num_lan_msix = min_t(int, cpus, vectors_left / 2); - vectors_left -= pf->num_lan_msix; + if (lut) { + u32 *lut_dw = (u32 *)lut; - /* reserve one vector for sideband flow director */ - if (pf->flags & I40E_FLAG_FD_SB_ENABLED) { - if (vectors_left) { - pf->num_fdsb_msix = 1; - v_budget++; - vectors_left--; + if (vsi->type == I40E_VSI_MAIN) { + if (lut_size != I40E_HLUT_ARRAY_SIZE) + return -EINVAL; + for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) + wr32(hw, I40E_PFQF_HLUT(i), lut_dw[i]); + } else if (vsi->type == I40E_VSI_SRIOV) { + if (lut_size != I40E_VF_HLUT_ARRAY_SIZE) + return -EINVAL; + for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) + wr32(hw, I40E_VFQF_HLUT1(i, vf_id), lut_dw[i]); } else { - pf->num_fdsb_msix = 0; + dev_err(&pf->pdev->dev, "Cannot set RSS LUT - invalid VSI type\n"); } } + i40e_flush(hw); - /* can we reserve enough for iWARP? */ - if (pf->flags & I40E_FLAG_IWARP_ENABLED) { - iwarp_requested = pf->num_iwarp_msix; + return 0; +} - if (!vectors_left) - pf->num_iwarp_msix = 0; - else if (vectors_left < pf->num_iwarp_msix) - pf->num_iwarp_msix = 1; - v_budget += pf->num_iwarp_msix; - vectors_left -= pf->num_iwarp_msix; - } +/** + * i40e_get_rss_reg - Get the RSS keys and lut by reading registers + * @vsi: Pointer to VSI structure + * @seed: Buffer to store the keys + * @lut: Buffer to store the lookup table entries + * @lut_size: Size of buffer to store the lookup table entries + * + * Returns 0 on success, negative on failure + */ +static int i40e_get_rss_reg(struct i40e_vsi *vsi, u8 *seed, + u8 *lut, u16 lut_size) +{ + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; + u16 i; - /* any vectors left over go for VMDq support */ - if (pf->flags & I40E_FLAG_VMDQ_ENABLED) { - int vmdq_vecs_wanted = pf->num_vmdq_vsis * pf->num_vmdq_qps; - int vmdq_vecs = min_t(int, vectors_left, vmdq_vecs_wanted); + if (seed) { + u32 *seed_dw = (u32 *)seed; - if (!vectors_left) { - pf->num_vmdq_msix = 0; - pf->num_vmdq_qps = 0; - } else { - /* if we're short on vectors for what's desired, we limit - * the queues per vmdq. If this is still more than are - * available, the user will need to change the number of - * queues/vectors used by the PF later with the ethtool - * channels command - */ - if (vmdq_vecs < vmdq_vecs_wanted) - pf->num_vmdq_qps = 1; - pf->num_vmdq_msix = pf->num_vmdq_qps; + for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) + seed_dw[i] = i40e_read_rx_ctl(hw, I40E_PFQF_HKEY(i)); + } + if (lut) { + u32 *lut_dw = (u32 *)lut; - v_budget += vmdq_vecs; - vectors_left -= vmdq_vecs; - } + if (lut_size != I40E_HLUT_ARRAY_SIZE) + return -EINVAL; + for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) + lut_dw[i] = rd32(hw, I40E_PFQF_HLUT(i)); } - /* On systems with a large number of SMP cores, we previously limited - * the number of vectors for num_lan_msix to be at most 50% of the - * available vectors, to allow for other features. Now, we add back - * the remaining vectors. However, we ensure that the total - * num_lan_msix will not exceed num_online_cpus(). To do this, we - * calculate the number of vectors we can add without going over the - * cap of CPUs. For systems with a small number of CPUs this will be - * zero. - */ - extra_vectors = min_t(int, cpus - pf->num_lan_msix, vectors_left); - pf->num_lan_msix += extra_vectors; - vectors_left -= extra_vectors; + return 0; +} + +/** + * i40e_config_rss - Configure RSS keys and lut + * @vsi: Pointer to VSI structure + * @seed: RSS hash seed + * @lut: Lookup table + * @lut_size: Lookup table size + * + * Returns 0 on success, negative on failure + */ +int i40e_config_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size) +{ + struct i40e_pf *pf = vsi->back; + + if (pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) + return i40e_config_rss_aq(vsi, seed, lut, lut_size); + else + return i40e_config_rss_reg(vsi, seed, lut, lut_size); +} + +/** + * i40e_get_rss - Get RSS keys and lut + * @vsi: Pointer to VSI structure + * @seed: Buffer to store the keys + * @lut: Buffer to store the lookup table entries + * @lut_size: Size of buffer to store the lookup table entries + * + * Returns 0 on success, negative on failure + */ +int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size) +{ + struct i40e_pf *pf = vsi->back; + + if (pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) + return i40e_get_rss_aq(vsi, seed, lut, lut_size); + else + return i40e_get_rss_reg(vsi, seed, lut, lut_size); +} + +/** + * i40e_fill_rss_lut - Fill the RSS lookup table with default values + * @pf: Pointer to board private structure + * @lut: Lookup table + * @rss_table_size: Lookup table size + * @rss_size: Range of queue number for hashing + */ +void i40e_fill_rss_lut(struct i40e_pf *pf, u8 *lut, + u16 rss_table_size, u16 rss_size) +{ + u16 i; - WARN(vectors_left < 0, - "Calculation of remaining vectors underflowed. This is an accounting bug when determining total MSI-X vectors.\n"); + for (i = 0; i < rss_table_size; i++) { + lut[i] = i % rss_size; + } +} - v_budget += pf->num_lan_msix; - pf->msix_entries = kcalloc(v_budget, sizeof(struct msix_entry), - GFP_KERNEL); - if (!pf->msix_entries) - return -ENOMEM; +/** + * i40e_pf_config_rss - Prepare for RSS if used + * @pf: board private structure + **/ +static int i40e_pf_config_rss(struct i40e_pf *pf) +{ + struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; + u8 seed[I40E_HKEY_ARRAY_SIZE]; + struct i40e_hw *hw = &pf->hw; + u32 reg_val; + u64 hena; + u8 *lut; + int ret; - for (i = 0; i < v_budget; i++) - pf->msix_entries[i].entry = i; - v_actual = i40e_reserve_msix_vectors(pf, v_budget); + /* By default we enable TCP/UDP with IPv4/IPv6 ptypes */ + hena = (u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(0)) | + ((u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(1)) << 32); + hena |= i40e_pf_get_default_rss_hena(pf); - if (v_actual < I40E_MIN_MSIX) { - pf->flags &= ~I40E_FLAG_MSIX_ENABLED; - kfree(pf->msix_entries); - pf->msix_entries = NULL; - pci_disable_msix(pf->pdev); - return -ENODEV; + i40e_write_rx_ctl(hw, I40E_PFQF_HENA(0), (u32)hena); + i40e_write_rx_ctl(hw, I40E_PFQF_HENA(1), (u32)(hena >> 32)); - } else if (v_actual == I40E_MIN_MSIX) { - /* Adjust for minimal MSIX use */ - pf->num_vmdq_vsis = 0; - pf->num_vmdq_qps = 0; - pf->num_lan_qps = 1; - pf->num_lan_msix = 1; + /* Determine the RSS table size based on the hardware capabilities */ + reg_val = i40e_read_rx_ctl(hw, I40E_PFQF_CTL_0); + reg_val = (pf->rss_table_size == 512) ? + (reg_val | I40E_PFQF_CTL_0_HASHLUTSIZE_512) : + (reg_val & ~I40E_PFQF_CTL_0_HASHLUTSIZE_512); + i40e_write_rx_ctl(hw, I40E_PFQF_CTL_0, reg_val); - } else if (!vectors_left) { - /* If we have limited resources, we will start with no vectors - * for the special features and then allocate vectors to some - * of these features based on the policy and at the end disable - * the features that did not get any vectors. + /* Determine the RSS size of the VSI */ + if (!vsi->rss_size) { + u16 qcount; + /* If the firmware does something weird during VSI init, we + * could end up with zero TCs. Check for that to avoid + * divide-by-zero. It probably won't pass traffic, but it also + * won't panic. */ - int vec; + qcount = vsi->num_queue_pairs / + (vsi->tc_config.numtc ? vsi->tc_config.numtc : 1); + vsi->rss_size = min_t(int, pf->alloc_rss_size, qcount); + } + if (!vsi->rss_size) + return -EINVAL; - dev_info(&pf->pdev->dev, - "MSI-X vector limit reached, attempting to redistribute vectors\n"); - /* reserve the misc vector */ - vec = v_actual - 1; + lut = kzalloc(vsi->rss_table_size, GFP_KERNEL); + if (!lut) + return -ENOMEM; - /* Scale vector usage down */ - pf->num_vmdq_msix = 1; /* force VMDqs to only one vector */ - pf->num_vmdq_vsis = 1; - pf->num_vmdq_qps = 1; + /* Use user configured lut if there is one, otherwise use default */ + if (vsi->rss_lut_user) + memcpy(lut, vsi->rss_lut_user, vsi->rss_table_size); + else + i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size); - /* partition out the remaining vectors */ - switch (vec) { - case 2: - pf->num_lan_msix = 1; - break; - case 3: - if (pf->flags & I40E_FLAG_IWARP_ENABLED) { - pf->num_lan_msix = 1; - pf->num_iwarp_msix = 1; - } else { - pf->num_lan_msix = 2; - } - break; - default: - if (pf->flags & I40E_FLAG_IWARP_ENABLED) { - pf->num_iwarp_msix = min_t(int, (vec / 3), - iwarp_requested); - pf->num_vmdq_vsis = min_t(int, (vec / 3), - I40E_DEFAULT_NUM_VMDQ_VSI); - } else { - pf->num_vmdq_vsis = min_t(int, (vec / 2), - I40E_DEFAULT_NUM_VMDQ_VSI); - } - if (pf->flags & I40E_FLAG_FD_SB_ENABLED) { - pf->num_fdsb_msix = 1; - vec--; - } - pf->num_lan_msix = min_t(int, - (vec - (pf->num_iwarp_msix + pf->num_vmdq_vsis)), - pf->num_lan_msix); - pf->num_lan_qps = pf->num_lan_msix; - break; - } - } + /* Use user configured hash key if there is one, otherwise + * use default. + */ + if (vsi->rss_hkey_user) + memcpy(seed, vsi->rss_hkey_user, I40E_HKEY_ARRAY_SIZE); + else + netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE); + ret = i40e_config_rss(vsi, seed, lut, vsi->rss_table_size); + kfree(lut); - if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) && - (pf->num_fdsb_msix == 0)) { - dev_info(&pf->pdev->dev, "Sideband Flowdir disabled, not enough MSI-X vectors\n"); - pf->flags &= ~I40E_FLAG_FD_SB_ENABLED; - } - if ((pf->flags & I40E_FLAG_VMDQ_ENABLED) && - (pf->num_vmdq_msix == 0)) { - dev_info(&pf->pdev->dev, "VMDq disabled, not enough MSI-X vectors\n"); - pf->flags &= ~I40E_FLAG_VMDQ_ENABLED; - } + return ret; +} - if ((pf->flags & I40E_FLAG_IWARP_ENABLED) && - (pf->num_iwarp_msix == 0)) { - dev_info(&pf->pdev->dev, "IWARP disabled, not enough MSI-X vectors\n"); - pf->flags &= ~I40E_FLAG_IWARP_ENABLED; - } - i40e_debug(&pf->hw, I40E_DEBUG_INIT, - "MSI-X vector distribution: PF %d, VMDq %d, FDSB %d, iWARP %d\n", - pf->num_lan_msix, - pf->num_vmdq_msix * pf->num_vmdq_vsis, - pf->num_fdsb_msix, - pf->num_iwarp_msix); +/** + * i40e_clear_rss_config_user - clear the user configured RSS hash keys + * and lookup table + * @vsi: Pointer to VSI structure + */ +static void i40e_clear_rss_config_user(struct i40e_vsi *vsi) +{ + if (!vsi) + return; - return v_actual; + kfree(vsi->rss_hkey_user); + vsi->rss_hkey_user = NULL; + + kfree(vsi->rss_lut_user); + vsi->rss_lut_user = NULL; } /** - * i40e_vsi_alloc_q_vector - Allocate memory for a single interrupt vector - * @vsi: the VSI being configured - * @v_idx: index of the vector in the vsi struct - * @cpu: cpu to be used on affinity_mask + * i40e_reconfig_rss_queues - change number of queues for rss and rebuild + * @pf: board private structure + * @queue_count: the requested queue count for rss. * - * We allocate one q_vector. If allocation fails we return -ENOMEM. + * returns 0 if rss is not enabled, if enabled returns the final rss queue + * count which may be different from the requested queue count. + * Note: expects to be called while under rtnl_lock() **/ -static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx, int cpu) +int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count) { - struct i40e_q_vector *q_vector; + struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; + int new_rss_size; - /* allocate q_vector */ - q_vector = kzalloc(sizeof(struct i40e_q_vector), GFP_KERNEL); - if (!q_vector) - return -ENOMEM; + if (!(pf->flags & I40E_FLAG_RSS_ENABLED)) + return 0; - q_vector->vsi = vsi; - q_vector->v_idx = v_idx; - cpumask_copy(&q_vector->affinity_mask, cpu_possible_mask); + new_rss_size = min_t(int, queue_count, pf->rss_size_max); - if (vsi->netdev) - netif_napi_add(vsi->netdev, &q_vector->napi, - i40e_napi_poll, NAPI_POLL_WEIGHT); + if (queue_count != vsi->num_queue_pairs) { + u16 qcount; - q_vector->rx.latency_range = I40E_LOW_LATENCY; - q_vector->tx.latency_range = I40E_LOW_LATENCY; + vsi->req_queue_pairs = queue_count; + i40e_prep_for_reset(pf, true); - /* tie q_vector and vsi together */ - vsi->q_vectors[v_idx] = q_vector; + pf->alloc_rss_size = new_rss_size; - return 0; + i40e_reset_and_rebuild(pf, true, true); + + /* Discard the user configured hash keys and lut, if less + * queues are enabled. + */ + if (queue_count < vsi->rss_size) { + i40e_clear_rss_config_user(vsi); + dev_dbg(&pf->pdev->dev, + "discard user configured hash keys and lut\n"); + } + + /* Reset vsi->rss_size, as number of enabled queues changed */ + qcount = vsi->num_queue_pairs / vsi->tc_config.numtc; + vsi->rss_size = min_t(int, pf->alloc_rss_size, qcount); + + i40e_pf_config_rss(pf); + } + dev_info(&pf->pdev->dev, "User requested queue count/HW max RSS count: %d/%d\n", + vsi->req_queue_pairs, pf->rss_size_max); + return pf->alloc_rss_size; } /** - * i40e_vsi_alloc_q_vectors - Allocate memory for interrupt vectors - * @vsi: the VSI being configured - * - * We allocate one q_vector per queue interrupt. If allocation fails we - * return -ENOMEM. + * i40e_get_partition_bw_setting - Retrieve BW settings for this PF partition + * @pf: board private structure **/ -static int i40e_vsi_alloc_q_vectors(struct i40e_vsi *vsi) +i40e_status i40e_get_partition_bw_setting(struct i40e_pf *pf) { - struct i40e_pf *pf = vsi->back; - int err, v_idx, num_q_vectors, current_cpu; - - /* if not MSIX, give the one vector only to the LAN VSI */ - if (pf->flags & I40E_FLAG_MSIX_ENABLED) - num_q_vectors = vsi->num_q_vectors; - else if (vsi == pf->vsi[pf->lan_vsi]) - num_q_vectors = 1; - else - return -EINVAL; + i40e_status status; + bool min_valid, max_valid; + u32 max_bw, min_bw; - current_cpu = cpumask_first(cpu_online_mask); + status = i40e_read_bw_from_alt_ram(&pf->hw, &max_bw, &min_bw, + &min_valid, &max_valid); - for (v_idx = 0; v_idx < num_q_vectors; v_idx++) { - err = i40e_vsi_alloc_q_vector(vsi, v_idx, current_cpu); - if (err) - goto err_out; - current_cpu = cpumask_next(current_cpu, cpu_online_mask); - if (unlikely(current_cpu >= nr_cpu_ids)) - current_cpu = cpumask_first(cpu_online_mask); + if (!status) { + if (min_valid) + pf->min_bw = min_bw; + if (max_valid) + pf->max_bw = max_bw; } - return 0; + return status; +} -err_out: - while (v_idx--) - i40e_free_q_vector(vsi, v_idx); +/** + * i40e_set_partition_bw_setting - Set BW settings for this PF partition + * @pf: board private structure + **/ +i40e_status i40e_set_partition_bw_setting(struct i40e_pf *pf) +{ + struct i40e_aqc_configure_partition_bw_data bw_data; + i40e_status status; + + /* Set the valid bit for this PF */ + bw_data.pf_valid_bits = cpu_to_le16(BIT(pf->hw.pf_id)); + bw_data.max_bw[pf->hw.pf_id] = pf->max_bw & I40E_ALT_BW_VALUE_MASK; + bw_data.min_bw[pf->hw.pf_id] = pf->min_bw & I40E_ALT_BW_VALUE_MASK; + + /* Set the new bandwidths */ + status = i40e_aq_configure_partition_bw(&pf->hw, &bw_data, NULL); - return err; + return status; } /** - * i40e_init_interrupt_scheme - Determine proper interrupt scheme - * @pf: board private structure to initialize + * i40e_commit_partition_bw_setting - Commit BW settings for this PF partition + * @pf: board private structure **/ -static int i40e_init_interrupt_scheme(struct i40e_pf *pf) +i40e_status i40e_commit_partition_bw_setting(struct i40e_pf *pf) { - int vectors = 0; - ssize_t size; + /* Commit temporary BW setting to permanent NVM image */ + enum i40e_admin_queue_err last_aq_status; + i40e_status ret; + u16 nvm_word; - if (pf->flags & I40E_FLAG_MSIX_ENABLED) { - vectors = i40e_init_msix(pf); - if (vectors < 0) { - pf->flags &= ~(I40E_FLAG_MSIX_ENABLED | - I40E_FLAG_IWARP_ENABLED | - I40E_FLAG_RSS_ENABLED | - I40E_FLAG_DCB_CAPABLE | - I40E_FLAG_DCB_ENABLED | - I40E_FLAG_SRIOV_ENABLED | - I40E_FLAG_FD_SB_ENABLED | - I40E_FLAG_FD_ATR_ENABLED | - I40E_FLAG_VMDQ_ENABLED); + if (pf->hw.partition_id != 1) { + dev_info(&pf->pdev->dev, + "Commit BW only works on partition 1! This is partition %d", + pf->hw.partition_id); + ret = I40E_NOT_SUPPORTED; + goto bw_commit_out; + } - /* rework the queue expectations without MSIX */ - i40e_determine_queue_usage(pf); - } + /* Acquire NVM for read access */ + ret = i40e_acquire_nvm(&pf->hw, I40E_RESOURCE_READ); + last_aq_status = pf->hw.aq.asq_last_status; + if (ret) { + dev_info(&pf->pdev->dev, + "Cannot acquire NVM for read access, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, last_aq_status)); + goto bw_commit_out; } - if (!(pf->flags & I40E_FLAG_MSIX_ENABLED) && - (pf->flags & I40E_FLAG_MSI_ENABLED)) { - dev_info(&pf->pdev->dev, "MSI-X not available, trying MSI\n"); - vectors = pci_enable_msi(pf->pdev); - if (vectors < 0) { - dev_info(&pf->pdev->dev, "MSI init failed - %d\n", - vectors); - pf->flags &= ~I40E_FLAG_MSI_ENABLED; - } - vectors = 1; /* one MSI or Legacy vector */ + /* Read word 0x10 of NVM - SW compatibility word 1 */ + ret = i40e_aq_read_nvm(&pf->hw, + I40E_SR_NVM_CONTROL_WORD, + 0x10, sizeof(nvm_word), &nvm_word, + false, NULL); + /* Save off last admin queue command status before releasing + * the NVM + */ + last_aq_status = pf->hw.aq.asq_last_status; + i40e_release_nvm(&pf->hw); + if (ret) { + dev_info(&pf->pdev->dev, "NVM read error, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, last_aq_status)); + goto bw_commit_out; } - if (!(pf->flags & (I40E_FLAG_MSIX_ENABLED | I40E_FLAG_MSI_ENABLED))) - dev_info(&pf->pdev->dev, "MSI-X and MSI not available, falling back to Legacy IRQ\n"); + /* Wait a bit for NVM release to complete */ + msleep(50); - /* set up vector assignment tracking */ - size = sizeof(struct i40e_lump_tracking) + (sizeof(u16) * vectors); - pf->irq_pile = kzalloc(size, GFP_KERNEL); - if (!pf->irq_pile) { - dev_err(&pf->pdev->dev, "error allocating irq_pile memory\n"); - return -ENOMEM; + /* Acquire NVM for write access */ + ret = i40e_acquire_nvm(&pf->hw, I40E_RESOURCE_WRITE); + last_aq_status = pf->hw.aq.asq_last_status; + if (ret) { + dev_info(&pf->pdev->dev, + "Cannot acquire NVM for write access, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, last_aq_status)); + goto bw_commit_out; } - pf->irq_pile->num_entries = vectors; - pf->irq_pile->search_hint = 0; - - /* track first vector for misc interrupts, ignore return */ - (void)i40e_get_lump(pf, pf->irq_pile, 1, I40E_PILE_VALID_BIT - 1); + /* Write it back out unchanged to initiate update NVM, + * which will force a write of the shadow (alt) RAM to + * the NVM - thus storing the bandwidth values permanently. + */ + ret = i40e_aq_update_nvm(&pf->hw, + I40E_SR_NVM_CONTROL_WORD, + 0x10, sizeof(nvm_word), + &nvm_word, true, 0, NULL); + /* Save off last admin queue command status before releasing + * the NVM + */ + last_aq_status = pf->hw.aq.asq_last_status; + i40e_release_nvm(&pf->hw); + if (ret) + dev_info(&pf->pdev->dev, + "BW settings NOT SAVED, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, last_aq_status)); +bw_commit_out: - return 0; + return ret; } /** - * i40e_setup_misc_vector - Setup the misc vector to handle non queue events + * i40e_is_total_port_shutdown_enabled - read nvm and return value + * if total port shutdown feature is enabled for this pf * @pf: board private structure + **/ +static bool i40e_is_total_port_shutdown_enabled(struct i40e_pf *pf) +{ +#define I40E_TOTAL_PORT_SHUTDOWN_ENABLED BIT(4) +#define I40E_FEATURES_ENABLE_PTR 0x2A +#define I40E_CURRENT_SETTING_PTR 0x2B +#define I40E_LINK_BEHAVIOR_WORD_OFFSET 0x2D +#define I40E_LINK_BEHAVIOR_WORD_LENGTH 0x1 +#define I40E_LINK_BEHAVIOR_OS_FORCED_ENABLED BIT(0) +#define I40E_LINK_BEHAVIOR_PORT_BIT_LENGTH 4 + i40e_status read_status = I40E_SUCCESS; + u16 sr_emp_sr_settings_ptr = 0; + u16 features_enable = 0; + u16 link_behavior = 0; + bool ret = false; + + read_status = i40e_read_nvm_word(&pf->hw, + I40E_SR_EMP_SR_SETTINGS_PTR, + &sr_emp_sr_settings_ptr); + if (read_status) + goto err_nvm; + read_status = i40e_read_nvm_word(&pf->hw, + sr_emp_sr_settings_ptr + + I40E_FEATURES_ENABLE_PTR, + &features_enable); + if (read_status) + goto err_nvm; + if (I40E_TOTAL_PORT_SHUTDOWN_ENABLED & features_enable) { + read_status = + i40e_read_nvm_module_data(&pf->hw, + I40E_SR_EMP_SR_SETTINGS_PTR, + I40E_CURRENT_SETTING_PTR, + I40E_LINK_BEHAVIOR_WORD_OFFSET, + I40E_LINK_BEHAVIOR_WORD_LENGTH, + &link_behavior); + if (read_status) + goto err_nvm; + link_behavior >>= + (pf->hw.port * I40E_LINK_BEHAVIOR_PORT_BIT_LENGTH); + ret = I40E_LINK_BEHAVIOR_OS_FORCED_ENABLED & link_behavior; + } + return ret; + +err_nvm: + dev_warn(&pf->pdev->dev, + "Total Port Shutdown feature is off due to read nvm error:%d\n", + read_status); + return ret; +} + +/** + * i40e_sw_init - Initialize general software structures (struct i40e_pf) + * @pf: board private structure to initialize * - * This sets up the handler for MSIX 0, which is used to manage the - * non-queue interrupts, e.g. AdminQ and errors. This is not used - * when in MSI or Legacy interrupt mode. + * i40e_sw_init initializes the Adapter private data structure. + * Fields are initialized based on PCI device information and + * OS network device settings (MTU size). **/ -static int i40e_setup_misc_vector(struct i40e_pf *pf) +static int i40e_sw_init(struct i40e_pf *pf) { - struct i40e_hw *hw = &pf->hw; int err = 0; + int size; + u16 pow; - /* Only request the irq if this is the first time through, and - * not when we're rebuilding after a Reset + pf->msg_enable = netif_msg_init(I40E_DEFAULT_MSG_ENABLE, + (NETIF_MSG_DRV|NETIF_MSG_PROBE|NETIF_MSG_LINK)); + if (debug != -1 && debug != I40E_DEFAULT_MSG_ENABLE) { + if (I40E_DEBUG_USER & debug) + pf->hw.debug_mask = debug; + pf->msg_enable = netif_msg_init((debug & ~I40E_DEBUG_USER), + I40E_DEFAULT_MSG_ENABLE); + } + + /* Set default capability flags */ + pf->flags = I40E_FLAG_RX_CSUM_ENABLED | + I40E_FLAG_MSI_ENABLED | + I40E_FLAG_MSIX_ENABLED; + + /* Set default ITR */ + pf->rx_itr_default = I40E_ITR_RX_DEF; + pf->tx_itr_default = I40E_ITR_TX_DEF; + /* Depending on PF configurations, it is possible that the RSS + * maximum might end up larger than the available queues */ - if (!test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) { - err = request_irq(pf->msix_entries[0].vector, - i40e_intr, 0, pf->int_name, pf); - if (err) { + pf->rss_size_max = BIT(pf->hw.func_caps.rss_table_entry_width); + pf->alloc_rss_size = 1; + pf->rss_table_size = pf->hw.func_caps.rss_table_size; + pf->rss_size_max = min_t(int, pf->rss_size_max, + pf->hw.func_caps.num_tx_qp); + + /* find the next higher power-of-2 of num cpus */ + pow = roundup_pow_of_two(num_online_cpus()); + pf->rss_size_max = min_t(int, pf->rss_size_max, pow); + + if (pf->hw.func_caps.rss) { + pf->flags |= I40E_FLAG_RSS_ENABLED; + pf->alloc_rss_size = min_t(int, pf->rss_size_max, + num_online_cpus()); + } + /* MFP mode enabled */ + if (pf->hw.func_caps.npar_enable || pf->hw.func_caps.flex10_enable) { + pf->flags |= I40E_FLAG_MFP_ENABLED; + + dev_info(&pf->pdev->dev, "MFP mode Enabled\n"); + if (i40e_get_partition_bw_setting(pf)) { + dev_warn(&pf->pdev->dev, + "Could not get partition bw settings\n"); + } else { dev_info(&pf->pdev->dev, - "request_irq for %s failed: %d\n", - pf->int_name, err); - return -EFAULT; + "Partition BW Min = %8.8x, Max = %8.8x\n", + pf->min_bw, pf->max_bw); + + /* nudge the Tx scheduler */ + i40e_set_partition_bw_setting(pf); } } - i40e_enable_misc_int_causes(pf); + if ((pf->hw.func_caps.fd_filters_guaranteed > 0) || + (pf->hw.func_caps.fd_filters_best_effort > 0)) { + pf->flags |= I40E_FLAG_FD_ATR_ENABLED; + pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE; + if (pf->flags & I40E_FLAG_MFP_ENABLED && + pf->hw.num_partitions > 1) + dev_info(&pf->pdev->dev, + "Flow Director Sideband mode Disabled in MFP mode\n"); + else + pf->flags |= I40E_FLAG_FD_SB_ENABLED; + pf->fdir_pf_filter_count = + pf->hw.func_caps.fd_filters_guaranteed; + pf->hw.fdir_shared_filter_count = + pf->hw.func_caps.fd_filters_best_effort; + } - /* associate no queues to the misc vector */ - wr32(hw, I40E_PFINT_LNKLST0, I40E_QUEUE_END_OF_LIST); - wr32(hw, I40E_PFINT_ITR0(I40E_RX_ITR), I40E_ITR_8K); + if (pf->hw.mac.type == I40E_MAC_X722) { + pf->hw_features |= (I40E_HW_RSS_AQ_CAPABLE | + I40E_HW_128_QP_RSS_CAPABLE | + I40E_HW_ATR_EVICT_CAPABLE | + I40E_HW_WB_ON_ITR_CAPABLE | + I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE | + I40E_HW_NO_PCI_LINK_CHECK | + I40E_HW_USE_SET_LLDP_MIB | + I40E_HW_GENEVE_OFFLOAD_CAPABLE | + I40E_HW_PTP_L4_CAPABLE | + I40E_HW_WOL_MC_MAGIC_PKT_WAKE | + I40E_HW_OUTER_UDP_CSUM_CAPABLE); - i40e_flush(hw); +#define I40E_FDEVICT_PCTYPE_DEFAULT 0xc03 + if (rd32(&pf->hw, I40E_GLQF_FDEVICTENA(1)) != + I40E_FDEVICT_PCTYPE_DEFAULT) { + dev_warn(&pf->pdev->dev, "FD EVICT PCTYPES are not right, disable FD HW EVICT\n"); + pf->hw_features &= ~I40E_HW_ATR_EVICT_CAPABLE; + } + } else if ((pf->hw.aq.api_maj_ver > 1) || + ((pf->hw.aq.api_maj_ver == 1) && + (pf->hw.aq.api_min_ver > 4))) { + /* Supported in FW API version higher than 1.4 */ + pf->hw_features |= I40E_HW_GENEVE_OFFLOAD_CAPABLE; - i40e_irq_dynamic_enable_icr0(pf, true); + /* supports mpls header skip and csum for following headers */ + pf->hw_features |= I40E_HW_MPLS_HDR_OFFLOAD_CAPABLE; + } - return err; -} + /* Enable HW ATR eviction if possible */ + if (pf->hw_features & I40E_HW_ATR_EVICT_CAPABLE) + pf->flags |= I40E_FLAG_HW_ATR_EVICT_ENABLED; -/** - * i40e_config_rss_aq - Prepare for RSS using AQ commands - * @vsi: vsi structure - * @seed: RSS hash seed - **/ -static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed, - u8 *lut, u16 lut_size) -{ - struct i40e_pf *pf = vsi->back; - struct i40e_hw *hw = &pf->hw; - int ret = 0; + if ((pf->hw.mac.type == I40E_MAC_XL710) && + (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 33)) || + (pf->hw.aq.fw_maj_ver < 4))) { + pf->hw_features |= I40E_HW_RESTART_AUTONEG; + /* No DCB support for FW < v4.33 */ + pf->hw_features |= I40E_HW_NO_DCB_SUPPORT; + } + + /* Disable FW LLDP if FW < v4.3 */ + if ((pf->hw.mac.type == I40E_MAC_XL710) && + (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 3)) || + (pf->hw.aq.fw_maj_ver < 4))) + pf->hw_features |= I40E_HW_STOP_FW_LLDP; + + /* Use the FW Set LLDP MIB API if FW > v4.40 */ + if ((pf->hw.mac.type == I40E_MAC_XL710) && + (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver >= 40)) || + (pf->hw.aq.fw_maj_ver >= 5))) + pf->hw_features |= I40E_HW_USE_SET_LLDP_MIB; + + /* Enable PTP L4 if FW > v6.0 */ + if ((pf->hw.mac.type == I40E_MAC_XL710) && + (pf->hw.aq.fw_maj_ver >= 6)) + pf->hw_features |= I40E_HW_PTP_L4_CAPABLE; - if (seed) { - struct i40e_aqc_get_set_rss_key_data *seed_dw = - (struct i40e_aqc_get_set_rss_key_data *)seed; - ret = i40e_aq_set_rss_key(hw, vsi->id, seed_dw); - if (ret) { - dev_info(&pf->pdev->dev, - "Cannot set RSS key, err %s aq_err %s\n", - i40e_stat_str(hw, ret), - i40e_aq_str(hw, hw->aq.asq_last_status)); - return ret; - } + if (pf->hw.func_caps.vmdq && num_online_cpus() != 1) { + pf->num_vmdq_vsis = I40E_DEFAULT_NUM_VMDQ_VSI; + pf->flags |= I40E_FLAG_VMDQ_ENABLED; + pf->num_vmdq_qps = i40e_default_queues_per_vmdq(pf); } - if (lut) { - bool pf_lut = vsi->type == I40E_VSI_MAIN ? true : false; - ret = i40e_aq_set_rss_lut(hw, vsi->id, pf_lut, lut, lut_size); - if (ret) { + if (pf->hw.func_caps.iwarp && num_online_cpus() != 1) { + pf->flags |= I40E_FLAG_IWARP_ENABLED; + /* IWARP needs one extra vector for CQP just like MISC.*/ + pf->num_iwarp_msix = (int)num_online_cpus() + 1; + } + /* Stopping FW LLDP engine is supported on XL710 and X722 + * starting from FW versions determined in i40e_init_adminq. + * Stopping the FW LLDP engine is not supported on XL710 + * if NPAR is functioning so unset this hw flag in this case. + */ + if (pf->hw.mac.type == I40E_MAC_XL710 && + pf->hw.func_caps.npar_enable && + (pf->hw.flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE)) + pf->hw.flags &= ~I40E_HW_FLAG_FW_LLDP_STOPPABLE; + +#ifndef HAVE_SWIOTLB_SKIP_CPU_SYNC + /* force legacy Rx if SKIP_CPU_SYNC is not supported */ + pf->flags |= I40E_FLAG_LEGACY_RX; +#endif +#ifdef CONFIG_PCI_IOV + if (pf->hw.func_caps.num_vfs && pf->hw.partition_id == 1) { +#if !defined(HAVE_SRIOV_CONFIGURE) && !defined(HAVE_RHEL6_SRIOV_CONFIGURE) + pf->num_req_vfs = 0; + if (max_vfs[pf->instance] > 0 && + max_vfs[pf->instance] <= pf->hw.func_caps.num_vfs) { + pf->flags |= I40E_FLAG_SRIOV_ENABLED; + /* assign number of SR-IOV VFs */ + pf->num_vf_qps = I40E_DEFAULT_QUEUES_PER_VF; + pf->num_req_vfs = max_vfs[pf->instance]; + } else if (max_vfs[pf->instance] == 0) { dev_info(&pf->pdev->dev, - "Cannot set RSS lut, err %s aq_err %s\n", - i40e_stat_str(hw, ret), - i40e_aq_str(hw, hw->aq.asq_last_status)); - return ret; + " SR-IOV is disabled, Module Parameter max_vfs value %d = disabled\n", + max_vfs[pf->instance]); + } else if (max_vfs[pf->instance] != -1) { + dev_err(&pf->pdev->dev, + "Module Parameter max_vfs value %d is out of range. Maximum value for the device: %d - resetting to zero\n", + max_vfs[pf->instance], + pf->hw.func_caps.num_vfs); } +#else + pf->flags |= I40E_FLAG_SRIOV_ENABLED; + pf->num_vf_qps = I40E_DEFAULT_QUEUES_PER_VF; + pf->num_req_vfs = min_t(int, + pf->hw.func_caps.num_vfs, + I40E_MAX_VF_COUNT); +#endif /* HAVE_SRIOV_CONFIGURE */ } - return ret; -} +#endif /* CONFIG_PCI_IOV */ + pf->eeprom_version = 0xDEAD; + pf->lan_veb = I40E_NO_VEB; + pf->lan_vsi = I40E_NO_VSI; -/** - * i40e_get_rss_aq - Get RSS keys and lut by using AQ commands - * @vsi: Pointer to vsi structure - * @seed: Buffter to store the hash keys - * @lut: Buffer to store the lookup table entries - * @lut_size: Size of buffer to store the lookup table entries - * - * Return 0 on success, negative on failure - */ -static int i40e_get_rss_aq(struct i40e_vsi *vsi, const u8 *seed, - u8 *lut, u16 lut_size) -{ - struct i40e_pf *pf = vsi->back; - struct i40e_hw *hw = &pf->hw; - int ret = 0; + /* By default FW has this off for performance reasons */ + pf->flags &= ~I40E_FLAG_VEB_STATS_ENABLED; - if (seed) { - ret = i40e_aq_get_rss_key(hw, vsi->id, - (struct i40e_aqc_get_set_rss_key_data *)seed); - if (ret) { - dev_info(&pf->pdev->dev, - "Cannot get RSS key, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, - pf->hw.aq.asq_last_status)); - return ret; - } + /* set up queue assignment tracking */ + size = sizeof(struct i40e_lump_tracking) + + (sizeof(u16) * pf->hw.func_caps.num_tx_qp); + pf->qp_pile = kzalloc(size, GFP_KERNEL); + if (!pf->qp_pile) { + err = -ENOMEM; + goto sw_init_done; } + pf->qp_pile->num_entries = pf->hw.func_caps.num_tx_qp; + pf->qp_pile->search_hint = 0; - if (lut) { - bool pf_lut = vsi->type == I40E_VSI_MAIN ? true : false; + pf->tx_timeout_recovery_level = 1; - ret = i40e_aq_get_rss_lut(hw, vsi->id, pf_lut, lut, lut_size); - if (ret) { - dev_info(&pf->pdev->dev, - "Cannot get RSS lut, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, - pf->hw.aq.asq_last_status)); - return ret; - } + if (pf->hw.mac.type != I40E_MAC_X722 && + i40e_is_total_port_shutdown_enabled(pf)) { + /* Link down on close must be on when total port shutdown + * is enabled for a given port + */ + pf->flags |= (I40E_FLAG_TOTAL_PORT_SHUTDOWN + | I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED); + dev_info(&pf->pdev->dev, + "Total Port Shutdown is enabled, link-down-on-close forced on\n"); } - return ret; + /* Add default values for ingress and egress vlan */ + pf->ingress_vlan = I40E_NO_VF_MIRROR; + pf->egress_vlan = I40E_NO_VF_MIRROR; + + mutex_init(&pf->switch_mutex); + +sw_init_done: + return err; } +#ifdef HAVE_NDO_SET_FEATURES +#endif /** - * i40e_vsi_config_rss - Prepare for VSI(VMDq) RSS if used - * @vsi: VSI structure + * i40e_set_ntuple - set the ntuple feature flag and take action + * @pf: board private structure to initialize + * @features: the feature set that the stack is suggesting + * + * returns a bool to indicate if reset needs to happen **/ -static int i40e_vsi_config_rss(struct i40e_vsi *vsi) +bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features) { - u8 seed[I40E_HKEY_ARRAY_SIZE]; - struct i40e_pf *pf = vsi->back; - u8 *lut; - int ret; - - if (!(pf->hw_features & I40E_HW_RSS_AQ_CAPABLE)) - return 0; - - if (!vsi->rss_size) - vsi->rss_size = min_t(int, pf->alloc_rss_size, - vsi->num_queue_pairs); - if (!vsi->rss_size) - return -EINVAL; + bool need_reset = false; - lut = kzalloc(vsi->rss_table_size, GFP_KERNEL); - if (!lut) - return -ENOMEM; - /* Use the user configured hash keys and lookup table if there is one, - * otherwise use default + /* Check if Flow Director n-tuple support was enabled or disabled. If + * the state changed, we need to reset. */ - if (vsi->rss_lut_user) - memcpy(lut, vsi->rss_lut_user, vsi->rss_table_size); - else - i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size); - if (vsi->rss_hkey_user) - memcpy(seed, vsi->rss_hkey_user, I40E_HKEY_ARRAY_SIZE); - else - netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE); - ret = i40e_config_rss_aq(vsi, seed, lut, vsi->rss_table_size); - kfree(lut); - - return ret; + if (features & NETIF_F_NTUPLE) { + /* Enable filters and mark for reset */ + if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED)) + need_reset = true; + /* enable FD_SB only if there is MSI-X vector and no cloud + * filters exist + */ + if (pf->num_fdsb_msix > 0 && !pf->num_cloud_filters) { + pf->flags |= I40E_FLAG_FD_SB_ENABLED; + pf->flags &= ~I40E_FLAG_FD_SB_INACTIVE; + } + } else { + /* turn off filters, mark for reset and clear SW filter list */ + if (pf->flags & I40E_FLAG_FD_SB_ENABLED) { + need_reset = true; + i40e_fdir_filter_exit(pf); + i40e_cloud_filter_exit(pf); + } + pf->flags &= ~I40E_FLAG_FD_SB_ENABLED; + clear_bit(__I40E_FD_SB_AUTO_DISABLED, pf->state); + pf->flags |= I40E_FLAG_FD_SB_INACTIVE; + /* reset fd counters */ + pf->fd_add_err = pf->fd_atr_cnt = 0; + /* if ATR was auto disabled it can be re-enabled. */ + if (test_and_clear_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state)) + if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && + (I40E_DEBUG_FD & pf->hw.debug_mask)) + dev_info(&pf->pdev->dev, "ATR re-enabled.\n"); + } + return need_reset; } +#ifdef HAVE_NDO_SET_FEATURES /** - * i40e_config_rss_reg - Configure RSS keys and lut by writing registers - * @vsi: Pointer to vsi structure - * @seed: RSS hash seed - * @lut: Lookup table - * @lut_size: Lookup table size - * - * Returns 0 on success, negative on failure + * i40e_clear_rss_lut - clear the rx hash lookup table + * @vsi: the VSI being configured **/ -static int i40e_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed, - const u8 *lut, u16 lut_size) +static void i40e_clear_rss_lut(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; struct i40e_hw *hw = &pf->hw; u16 vf_id = vsi->vf_id; u8 i; - /* Fill out hash function seed */ - if (seed) { - u32 *seed_dw = (u32 *)seed; - - if (vsi->type == I40E_VSI_MAIN) { - for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) - wr32(hw, I40E_PFQF_HKEY(i), seed_dw[i]); - } else if (vsi->type == I40E_VSI_SRIOV) { - for (i = 0; i <= I40E_VFQF_HKEY1_MAX_INDEX; i++) - wr32(hw, I40E_VFQF_HKEY1(i, vf_id), seed_dw[i]); - } else { - dev_err(&pf->pdev->dev, "Cannot set RSS seed - invalid VSI type\n"); - } - } - - if (lut) { - u32 *lut_dw = (u32 *)lut; - - if (vsi->type == I40E_VSI_MAIN) { - if (lut_size != I40E_HLUT_ARRAY_SIZE) - return -EINVAL; - for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) - wr32(hw, I40E_PFQF_HLUT(i), lut_dw[i]); - } else if (vsi->type == I40E_VSI_SRIOV) { - if (lut_size != I40E_VF_HLUT_ARRAY_SIZE) - return -EINVAL; - for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) - wr32(hw, I40E_VFQF_HLUT1(i, vf_id), lut_dw[i]); - } else { - dev_err(&pf->pdev->dev, "Cannot set RSS LUT - invalid VSI type\n"); - } + if (vsi->type == I40E_VSI_MAIN) { + for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) + wr32(hw, I40E_PFQF_HLUT(i), 0); + } else if (vsi->type == I40E_VSI_SRIOV) { + for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) + i40e_write_rx_ctl(hw, I40E_VFQF_HLUT1(i, vf_id), 0); + } else { + dev_err(&pf->pdev->dev, "Cannot set RSS LUT - invalid VSI type\n"); } - i40e_flush(hw); - - return 0; } /** - * i40e_get_rss_reg - Get the RSS keys and lut by reading registers - * @vsi: Pointer to VSI structure - * @seed: Buffer to store the keys - * @lut: Buffer to store the lookup table entries - * @lut_size: Size of buffer to store the lookup table entries - * - * Returns 0 on success, negative on failure - */ -static int i40e_get_rss_reg(struct i40e_vsi *vsi, u8 *seed, - u8 *lut, u16 lut_size) + * i40e_set_features - set the netdev feature flags + * @netdev: ptr to the netdev being adjusted + * @features: the feature set that the stack is suggesting + * Note: expects to be called while under rtnl_lock() + **/ +#ifdef HAVE_RHEL6_NET_DEVICE_OPS_EXT +static int i40e_set_features(struct net_device *netdev, u32 features) +#else +static int i40e_set_features(struct net_device *netdev, + netdev_features_t features) +#endif { + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - struct i40e_hw *hw = &pf->hw; - u16 i; + bool need_reset; - if (seed) { - u32 *seed_dw = (u32 *)seed; + if (features & NETIF_F_RXHASH && !(netdev->features & NETIF_F_RXHASH)) + i40e_pf_config_rss(pf); + else if (!(features & NETIF_F_RXHASH) && + netdev->features & NETIF_F_RXHASH) + i40e_clear_rss_lut(vsi); - for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) - seed_dw[i] = i40e_read_rx_ctl(hw, I40E_PFQF_HKEY(i)); - } - if (lut) { - u32 *lut_dw = (u32 *)lut; +#ifdef NETIF_F_HW_VLAN_CTAG_RX + if (features & NETIF_F_HW_VLAN_CTAG_RX) +#else + if (features & NETIF_F_HW_VLAN_RX) +#endif + i40e_vlan_stripping_enable(vsi); + else + i40e_vlan_stripping_disable(vsi); - if (lut_size != I40E_HLUT_ARRAY_SIZE) - return -EINVAL; - for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) - lut_dw[i] = rd32(hw, I40E_PFQF_HLUT(i)); +#ifdef NETIF_F_HW_TC + if (!(features & NETIF_F_HW_TC) && pf->num_cloud_filters) { + dev_err(&pf->pdev->dev, + "Offloaded tc filters active, can't turn hw_tc_offload off"); + return -EINVAL; } +#endif + need_reset = i40e_set_ntuple(pf, features); + + if (need_reset) + i40e_do_reset(pf, I40E_PF_RESET_FLAG, true); return 0; } +#endif /* HAVE_NDO_SET_FEATURES */ /** - * i40e_config_rss - Configure RSS keys and lut - * @vsi: Pointer to VSI structure - * @seed: RSS hash seed - * @lut: Lookup table - * @lut_size: Lookup table size + * i40e_get_udp_port_idx - Lookup a possibly offloaded for Rx UDP port + * @pf: board private structure + * @port: The UDP port to look up * - * Returns 0 on success, negative on failure - */ -int i40e_config_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size) + * Returns the index number or I40E_MAX_PF_UDP_OFFLOAD_PORTS if port not found + **/ +static u8 i40e_get_udp_port_idx(struct i40e_pf *pf, u16 port) { - struct i40e_pf *pf = vsi->back; + u8 i; - if (pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) - return i40e_config_rss_aq(vsi, seed, lut, lut_size); - else - return i40e_config_rss_reg(vsi, seed, lut, lut_size); + for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { + /* Do not report ports with pending deletions as + * being available. + */ + if (!port && (pf->pending_udp_bitmap & BIT_ULL(i))) + continue; + if (pf->udp_ports[i].port == port) + return i; + } + + return i; } /** - * i40e_get_rss - Get RSS keys and lut - * @vsi: Pointer to VSI structure - * @seed: Buffer to store the keys - * @lut: Buffer to store the lookup table entries - * lut_size: Size of buffer to store the lookup table entries - * - * Returns 0 on success, negative on failure - */ -int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size) + * i40e_udp_tunnel_add - Get notifications about UDP tunnel ports that come up + * @netdev: This physical port's netdev + * @ti: Tunnel endpoint information + **/ +__maybe_unused +static void i40e_udp_tunnel_add(struct net_device *netdev, + struct udp_tunnel_info *ti) { + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; + u16 port = ntohs(ti->port); + u8 next_idx; + u8 idx; - if (pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) - return i40e_get_rss_aq(vsi, seed, lut, lut_size); - else - return i40e_get_rss_reg(vsi, seed, lut, lut_size); -} + idx = i40e_get_udp_port_idx(pf, port); -/** - * i40e_fill_rss_lut - Fill the RSS lookup table with default values - * @pf: Pointer to board private structure - * @lut: Lookup table - * @rss_table_size: Lookup table size - * @rss_size: Range of queue number for hashing - */ -void i40e_fill_rss_lut(struct i40e_pf *pf, u8 *lut, - u16 rss_table_size, u16 rss_size) -{ - u16 i; + /* Check if port already exists */ + if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) { + netdev_info(netdev, "port %d already offloaded\n", port); + return; + } - for (i = 0; i < rss_table_size; i++) - lut[i] = i % rss_size; + /* Now check if there is space to add the new port */ + next_idx = i40e_get_udp_port_idx(pf, 0); + + if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) { + netdev_info(netdev, "maximum number of offloaded UDP ports reached, not adding port %d\n", + port); + return; + } + + switch (ti->type) { + case UDP_TUNNEL_TYPE_VXLAN: + pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_VXLAN; + break; + case UDP_TUNNEL_TYPE_GENEVE: + if (!(pf->hw_features & I40E_HW_GENEVE_OFFLOAD_CAPABLE)) + return; + pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_NGE; + break; + default: + return; + } + + /* New port: add it and mark its index in the bitmap */ + pf->udp_ports[next_idx].port = port; + pf->udp_ports[next_idx].filter_index = I40E_UDP_PORT_INDEX_UNUSED; + pf->pending_udp_bitmap |= BIT_ULL(next_idx); + set_bit(__I40E_UDP_FILTER_SYNC_PENDING, pf->state); } /** - * i40e_pf_config_rss - Prepare for RSS if used - * @pf: board private structure + * i40e_udp_tunnel_del - Get notifications about UDP tunnel ports that go away + * @netdev: This physical port's netdev + * @ti: Tunnel endpoint information **/ -static int i40e_pf_config_rss(struct i40e_pf *pf) +__maybe_unused +static void i40e_udp_tunnel_del(struct net_device *netdev, + struct udp_tunnel_info *ti) { - struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; - u8 seed[I40E_HKEY_ARRAY_SIZE]; - u8 *lut; - struct i40e_hw *hw = &pf->hw; - u32 reg_val; - u64 hena; - int ret; - - /* By default we enable TCP/UDP with IPv4/IPv6 ptypes */ - hena = (u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(0)) | - ((u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(1)) << 32); - hena |= i40e_pf_get_default_rss_hena(pf); - - i40e_write_rx_ctl(hw, I40E_PFQF_HENA(0), (u32)hena); - i40e_write_rx_ctl(hw, I40E_PFQF_HENA(1), (u32)(hena >> 32)); + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + u16 port = ntohs(ti->port); + u8 idx; - /* Determine the RSS table size based on the hardware capabilities */ - reg_val = i40e_read_rx_ctl(hw, I40E_PFQF_CTL_0); - reg_val = (pf->rss_table_size == 512) ? - (reg_val | I40E_PFQF_CTL_0_HASHLUTSIZE_512) : - (reg_val & ~I40E_PFQF_CTL_0_HASHLUTSIZE_512); - i40e_write_rx_ctl(hw, I40E_PFQF_CTL_0, reg_val); + idx = i40e_get_udp_port_idx(pf, port); - /* Determine the RSS size of the VSI */ - if (!vsi->rss_size) { - u16 qcount; + /* Check if port already exists */ + if (idx >= I40E_MAX_PF_UDP_OFFLOAD_PORTS) + goto not_found; - qcount = vsi->num_queue_pairs / vsi->tc_config.numtc; - vsi->rss_size = min_t(int, pf->alloc_rss_size, qcount); + switch (ti->type) { + case UDP_TUNNEL_TYPE_VXLAN: + if (pf->udp_ports[idx].type != I40E_AQC_TUNNEL_TYPE_VXLAN) + goto not_found; + break; + case UDP_TUNNEL_TYPE_GENEVE: + if (!(pf->hw_features & I40E_HW_GENEVE_OFFLOAD_CAPABLE)) + return; + if (pf->udp_ports[idx].type != I40E_AQC_TUNNEL_TYPE_NGE) + goto not_found; + break; + default: + goto not_found; } - if (!vsi->rss_size) - return -EINVAL; - - lut = kzalloc(vsi->rss_table_size, GFP_KERNEL); - if (!lut) - return -ENOMEM; - /* Use user configured lut if there is one, otherwise use default */ - if (vsi->rss_lut_user) - memcpy(lut, vsi->rss_lut_user, vsi->rss_table_size); - else - i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size); + /* if port exists, set it to 0 (mark for deletion) + * and make it pending + */ + pf->udp_ports[idx].port = 0; - /* Use user configured hash key if there is one, otherwise - * use default. + /* Toggle pending bit instead of setting it. This way if we are + * deleting a port that has yet to be added we just clear the pending + * bit and don't have to worry about it. */ - if (vsi->rss_hkey_user) - memcpy(seed, vsi->rss_hkey_user, I40E_HKEY_ARRAY_SIZE); - else - netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE); - ret = i40e_config_rss(vsi, seed, lut, vsi->rss_table_size); - kfree(lut); + pf->pending_udp_bitmap ^= BIT_ULL(idx); + set_bit(__I40E_UDP_FILTER_SYNC_PENDING, pf->state); - return ret; + return; +not_found: + netdev_warn(netdev, "UDP port %d was not found, not deleting\n", + port); } +#if defined(HAVE_VXLAN_RX_OFFLOAD) && !defined(HAVE_UDP_ENC_RX_OFFLOAD) +#if IS_ENABLED(CONFIG_VXLAN) /** - * i40e_reconfig_rss_queues - change number of queues for rss and rebuild - * @pf: board private structure - * @queue_count: the requested queue count for rss. - * - * returns 0 if rss is not enabled, if enabled returns the final rss queue - * count which may be different from the requested queue count. - * Note: expects to be called while under rtnl_lock() + * i40e_add_vxlan_port - Get notifications about vxlan ports that come up + * @netdev: This physical port's netdev + * @sa_family: Socket Family that vxlan is notifying us about + * @port: New UDP port number that vxlan started listening to **/ -int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count) +static void i40e_add_vxlan_port(struct net_device *netdev, + sa_family_t sa_family, __be16 port) { - struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; - int new_rss_size; - - if (!(pf->flags & I40E_FLAG_RSS_ENABLED)) - return 0; - - new_rss_size = min_t(int, queue_count, pf->rss_size_max); - - if (queue_count != vsi->num_queue_pairs) { - u16 qcount; + struct udp_tunnel_info ti = { + .type = UDP_TUNNEL_TYPE_VXLAN, + .sa_family = sa_family, + .port = port, + }; - vsi->req_queue_pairs = queue_count; - i40e_prep_for_reset(pf, true); - - pf->alloc_rss_size = new_rss_size; - - i40e_reset_and_rebuild(pf, true, true); - - /* Discard the user configured hash keys and lut, if less - * queues are enabled. - */ - if (queue_count < vsi->rss_size) { - i40e_clear_rss_config_user(vsi); - dev_dbg(&pf->pdev->dev, - "discard user configured hash keys and lut\n"); - } + i40e_udp_tunnel_add(netdev, &ti); +} - /* Reset vsi->rss_size, as number of enabled queues changed */ - qcount = vsi->num_queue_pairs / vsi->tc_config.numtc; - vsi->rss_size = min_t(int, pf->alloc_rss_size, qcount); +/** + * i40e_del_vxlan_port - Get notifications about vxlan ports that go away + * @netdev: This physical port's netdev + * @sa_family: Socket Family that vxlan is notifying us about + * @port: UDP port number that vxlan stopped listening to + **/ +static void i40e_del_vxlan_port(struct net_device *netdev, + sa_family_t sa_family, __be16 port) +{ + struct udp_tunnel_info ti = { + .type = UDP_TUNNEL_TYPE_VXLAN, + .sa_family = sa_family, + .port = port, + }; - i40e_pf_config_rss(pf); - } - dev_info(&pf->pdev->dev, "User requested queue count/HW max RSS count: %d/%d\n", - vsi->req_queue_pairs, pf->rss_size_max); - return pf->alloc_rss_size; + i40e_udp_tunnel_del(netdev, &ti); } - +#endif /* CONFIG_VXLAN */ +#endif /* HAVE_VXLAN_RX_OFFLOAD && !HAVE_UDP_ENC_RX_OFFLOAD */ +#if defined(HAVE_GENEVE_RX_OFFLOAD) && !defined(HAVE_UDP_ENC_RX_OFFLOAD) +#if IS_ENABLED(CONFIG_GENEVE) /** - * i40e_get_partition_bw_setting - Retrieve BW settings for this PF partition - * @pf: board private structure + * i40e_add_geneve_port - Get notifications about GENEVE ports that come up + * @netdev: This physical port's netdev + * @sa_family: Socket Family that GENEVE is notifying us about + * @port: New UDP port number that GENEVE started listening to **/ -i40e_status i40e_get_partition_bw_setting(struct i40e_pf *pf) +static void i40e_add_geneve_port(struct net_device *netdev, + sa_family_t sa_family, __be16 port) { - i40e_status status; - bool min_valid, max_valid; - u32 max_bw, min_bw; + struct udp_tunnel_info ti = { + .type = UDP_TUNNEL_TYPE_GENEVE, + .sa_family = sa_family, + .port = port, + }; - status = i40e_read_bw_from_alt_ram(&pf->hw, &max_bw, &min_bw, - &min_valid, &max_valid); + i40e_udp_tunnel_add(netdev, &ti); +} - if (!status) { - if (min_valid) - pf->min_bw = min_bw; - if (max_valid) - pf->max_bw = max_bw; - } +/* + * i40e_del_geneve_port - Get notifications about GENEVE ports that go away + * @netdev: This physical port's netdev + * @sa_family: Socket Family that GENEVE is notifying us about + * @port: UDP port number that GENEVE stopped listening to + **/ +static void i40e_del_geneve_port(struct net_device *netdev, + sa_family_t sa_family, __be16 port) +{ + struct udp_tunnel_info ti = { + .type = UDP_TUNNEL_TYPE_GENEVE, + .sa_family = sa_family, + .port = port, + }; - return status; + i40e_udp_tunnel_del(netdev, &ti); } -/** - * i40e_set_partition_bw_setting - Set BW settings for this PF partition - * @pf: board private structure - **/ -i40e_status i40e_set_partition_bw_setting(struct i40e_pf *pf) +#endif /* CONFIG_GENEVE */ +#endif /* HAVE_GENEVE_RX_OFFLOAD && !HAVE_UDP_ENC_RX_OFFLOAD */ +#ifdef HAVE_NDO_GET_PHYS_PORT_ID +static int i40e_get_phys_port_id(struct net_device *netdev, + struct netdev_phys_item_id *ppid) { - struct i40e_aqc_configure_partition_bw_data bw_data; - i40e_status status; + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_pf *pf = np->vsi->back; + struct i40e_hw *hw = &pf->hw; - /* Set the valid bit for this PF */ - bw_data.pf_valid_bits = cpu_to_le16(BIT(pf->hw.pf_id)); - bw_data.max_bw[pf->hw.pf_id] = pf->max_bw & I40E_ALT_BW_VALUE_MASK; - bw_data.min_bw[pf->hw.pf_id] = pf->min_bw & I40E_ALT_BW_VALUE_MASK; + if (!(pf->hw_features & I40E_HW_PORT_ID_VALID)) + return -EOPNOTSUPP; - /* Set the new bandwidths */ - status = i40e_aq_configure_partition_bw(&pf->hw, &bw_data, NULL); + ppid->id_len = min_t(int, sizeof(hw->mac.port_addr), + sizeof(ppid->id)); + memcpy(ppid->id, hw->mac.port_addr, ppid->id_len); - return status; + return 0; } +#endif /* HAVE_NDO_GET_PHYS_PORT_ID */ /** - * i40e_commit_partition_bw_setting - Commit BW settings for this PF partition - * @pf: board private structure - **/ -i40e_status i40e_commit_partition_bw_setting(struct i40e_pf *pf) + * i40e_ndo_fdb_add - add an entry to the hardware database + * @ndm: the input from the stack + * @tb: pointer to array of nladdr (unused) + * @dev: the net device pointer + * @addr: the MAC address entry being added + * @vid: VLAN ID + * @flags: instructions from stack about fdb operation + * @extack: netdev extended ack structure + */ +#ifdef HAVE_FDB_OPS +#if defined(HAVE_NDO_FDB_ADD_EXTACK) +static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, const unsigned char *addr, + u16 vid, u16 flags, struct netlink_ext_ack *extack) +#elif defined(HAVE_NDO_FDB_ADD_VID) +static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, const unsigned char *addr, + u16 vid, u16 flags) +#elif defined(HAVE_NDO_FDB_ADD_NLATTR) +static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, const unsigned char *addr, + u16 flags) +#elif defined(USE_CONST_DEV_UC_CHAR) +static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct net_device *dev, + const unsigned char *addr, u16 flags) +#else +static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct net_device *dev, + unsigned char *addr, u16 flags) +#endif { - /* Commit temporary BW setting to permanent NVM image */ - enum i40e_admin_queue_err last_aq_status; - i40e_status ret; - u16 nvm_word; - - if (pf->hw.partition_id != 1) { - dev_info(&pf->pdev->dev, - "Commit BW only works on partition 1! This is partition %d", - pf->hw.partition_id); - ret = I40E_NOT_SUPPORTED; - goto bw_commit_out; - } + struct i40e_netdev_priv *np = netdev_priv(dev); + struct i40e_pf *pf = np->vsi->back; + int err = 0; - /* Acquire NVM for read access */ - ret = i40e_acquire_nvm(&pf->hw, I40E_RESOURCE_READ); - last_aq_status = pf->hw.aq.asq_last_status; - if (ret) { - dev_info(&pf->pdev->dev, - "Cannot acquire NVM for read access, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, last_aq_status)); - goto bw_commit_out; - } + if (!(pf->flags & I40E_FLAG_SRIOV_ENABLED)) + return -EOPNOTSUPP; - /* Read word 0x10 of NVM - SW compatibility word 1 */ - ret = i40e_aq_read_nvm(&pf->hw, - I40E_SR_NVM_CONTROL_WORD, - 0x10, sizeof(nvm_word), &nvm_word, - false, NULL); - /* Save off last admin queue command status before releasing - * the NVM + /* Hardware does not support aging addresses so if a + * ndm_state is given only allow permanent addresses */ - last_aq_status = pf->hw.aq.asq_last_status; - i40e_release_nvm(&pf->hw); - if (ret) { - dev_info(&pf->pdev->dev, "NVM read error, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, last_aq_status)); - goto bw_commit_out; + if (ndm->ndm_state && !(ndm->ndm_state & NUD_PERMANENT)) { + netdev_info(dev, "FDB only supports static addresses\n"); + return -EINVAL; } - /* Wait a bit for NVM release to complete */ - msleep(50); + if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) + err = dev_uc_add_excl(dev, addr); + else if (is_multicast_ether_addr(addr)) + err = dev_mc_add_excl(dev, addr); + else + err = -EINVAL; - /* Acquire NVM for write access */ - ret = i40e_acquire_nvm(&pf->hw, I40E_RESOURCE_WRITE); - last_aq_status = pf->hw.aq.asq_last_status; - if (ret) { - dev_info(&pf->pdev->dev, - "Cannot acquire NVM for write access, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, last_aq_status)); - goto bw_commit_out; - } - /* Write it back out unchanged to initiate update NVM, - * which will force a write of the shadow (alt) RAM to - * the NVM - thus storing the bandwidth values permanently. - */ - ret = i40e_aq_update_nvm(&pf->hw, - I40E_SR_NVM_CONTROL_WORD, - 0x10, sizeof(nvm_word), - &nvm_word, true, NULL); - /* Save off last admin queue command status before releasing - * the NVM - */ - last_aq_status = pf->hw.aq.asq_last_status; - i40e_release_nvm(&pf->hw); - if (ret) - dev_info(&pf->pdev->dev, - "BW settings NOT SAVED, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, last_aq_status)); -bw_commit_out: + /* Only return duplicate errors if NLM_F_EXCL is set */ + if (err == -EEXIST && !(flags & NLM_F_EXCL)) + err = 0; - return ret; + return err; } +#ifdef HAVE_NDO_FEATURES_CHECK /** - * i40e_sw_init - Initialize general software structures (struct i40e_pf) - * @pf: board private structure to initialize - * - * i40e_sw_init initializes the Adapter private data structure. - * Fields are initialized based on PCI device information and - * OS network device settings (MTU size). + * i40e_features_check - Validate encapsulated packet conforms to limits + * @skb: skb buff + * @dev: This physical port's netdev + * @features: Offload features that the stack believes apply **/ -static int i40e_sw_init(struct i40e_pf *pf) +static netdev_features_t i40e_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) { - int err = 0; - int size; - - /* Set default capability flags */ - pf->flags = I40E_FLAG_RX_CSUM_ENABLED | - I40E_FLAG_MSI_ENABLED | - I40E_FLAG_MSIX_ENABLED; - - /* Set default ITR */ - pf->rx_itr_default = I40E_ITR_DYNAMIC | I40E_ITR_RX_DEF; - pf->tx_itr_default = I40E_ITR_DYNAMIC | I40E_ITR_TX_DEF; + size_t len; - /* Depending on PF configurations, it is possible that the RSS - * maximum might end up larger than the available queues + /* No point in doing any of this if neither checksum nor GSO are + * being requested for this frame. We can rule out both by just + * checking for CHECKSUM_PARTIAL */ - pf->rss_size_max = BIT(pf->hw.func_caps.rss_table_entry_width); - pf->alloc_rss_size = 1; - pf->rss_table_size = pf->hw.func_caps.rss_table_size; - pf->rss_size_max = min_t(int, pf->rss_size_max, - pf->hw.func_caps.num_tx_qp); - if (pf->hw.func_caps.rss) { - pf->flags |= I40E_FLAG_RSS_ENABLED; - pf->alloc_rss_size = min_t(int, pf->rss_size_max, - num_online_cpus()); - } + if (skb->ip_summed != CHECKSUM_PARTIAL) + return features; - /* MFP mode enabled */ - if (pf->hw.func_caps.npar_enable || pf->hw.func_caps.flex10_enable) { - pf->flags |= I40E_FLAG_MFP_ENABLED; - dev_info(&pf->pdev->dev, "MFP mode Enabled\n"); - if (i40e_get_partition_bw_setting(pf)) { - dev_warn(&pf->pdev->dev, - "Could not get partition bw settings\n"); - } else { - dev_info(&pf->pdev->dev, - "Partition BW Min = %8.8x, Max = %8.8x\n", - pf->min_bw, pf->max_bw); + /* We cannot support GSO if the MSS is going to be less than + * 64 bytes. If it is then we need to drop support for GSO. + */ + if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_size < 64)) + features &= ~NETIF_F_GSO_MASK; - /* nudge the Tx scheduler */ - i40e_set_partition_bw_setting(pf); - } - } + /* MACLEN can support at most 63 words */ + len = skb_network_header(skb) - skb->data; + if (len & ~(63 * 2)) + goto out_err; - if ((pf->hw.func_caps.fd_filters_guaranteed > 0) || - (pf->hw.func_caps.fd_filters_best_effort > 0)) { - pf->flags |= I40E_FLAG_FD_ATR_ENABLED; - pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE; - if (pf->flags & I40E_FLAG_MFP_ENABLED && - pf->hw.num_partitions > 1) - dev_info(&pf->pdev->dev, - "Flow Director Sideband mode Disabled in MFP mode\n"); - else - pf->flags |= I40E_FLAG_FD_SB_ENABLED; - pf->fdir_pf_filter_count = - pf->hw.func_caps.fd_filters_guaranteed; - pf->hw.fdir_shared_filter_count = - pf->hw.func_caps.fd_filters_best_effort; - } + /* IPLEN and EIPLEN can support at most 127 dwords */ + len = skb_transport_header(skb) - skb_network_header(skb); + if (len & ~(127 * 4)) + goto out_err; - if (pf->hw.mac.type == I40E_MAC_X722) { - pf->hw_features |= (I40E_HW_RSS_AQ_CAPABLE | - I40E_HW_128_QP_RSS_CAPABLE | - I40E_HW_ATR_EVICT_CAPABLE | - I40E_HW_WB_ON_ITR_CAPABLE | - I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE | - I40E_HW_NO_PCI_LINK_CHECK | - I40E_HW_USE_SET_LLDP_MIB | - I40E_HW_GENEVE_OFFLOAD_CAPABLE | - I40E_HW_PTP_L4_CAPABLE | - I40E_HW_WOL_MC_MAGIC_PKT_WAKE | - I40E_HW_OUTER_UDP_CSUM_CAPABLE); + if (skb->encapsulation) { + /* L4TUNLEN can support 127 words */ + len = skb_inner_network_header(skb) - skb_transport_header(skb); + if (len & ~(127 * 2)) + goto out_err; -#define I40E_FDEVICT_PCTYPE_DEFAULT 0xc03 - if (rd32(&pf->hw, I40E_GLQF_FDEVICTENA(1)) != - I40E_FDEVICT_PCTYPE_DEFAULT) { - dev_warn(&pf->pdev->dev, - "FD EVICT PCTYPES are not right, disable FD HW EVICT\n"); - pf->hw_features &= ~I40E_HW_ATR_EVICT_CAPABLE; - } - } else if ((pf->hw.aq.api_maj_ver > 1) || - ((pf->hw.aq.api_maj_ver == 1) && - (pf->hw.aq.api_min_ver > 4))) { - /* Supported in FW API version higher than 1.4 */ - pf->hw_features |= I40E_HW_GENEVE_OFFLOAD_CAPABLE; + /* IPLEN can support at most 127 dwords */ + len = skb_inner_transport_header(skb) - + skb_inner_network_header(skb); + if (len & ~(127 * 4)) + goto out_err; } - /* Enable HW ATR eviction if possible */ - if (pf->hw_features & I40E_HW_ATR_EVICT_CAPABLE) - pf->flags |= I40E_FLAG_HW_ATR_EVICT_ENABLED; - - if ((pf->hw.mac.type == I40E_MAC_XL710) && - (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 33)) || - (pf->hw.aq.fw_maj_ver < 4))) { - pf->hw_features |= I40E_HW_RESTART_AUTONEG; - /* No DCB support for FW < v4.33 */ - pf->hw_features |= I40E_HW_NO_DCB_SUPPORT; - } + /* No need to validate L4LEN as TCP is the only protocol with a + * a flexible value and we support all possible values supported + * by TCP, which is at most 15 dwords + */ - /* Disable FW LLDP if FW < v4.3 */ - if ((pf->hw.mac.type == I40E_MAC_XL710) && - (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 3)) || - (pf->hw.aq.fw_maj_ver < 4))) - pf->hw_features |= I40E_HW_STOP_FW_LLDP; + return features; +out_err: + return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); +} - /* Use the FW Set LLDP MIB API if FW > v4.40 */ - if ((pf->hw.mac.type == I40E_MAC_XL710) && - (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver >= 40)) || - (pf->hw.aq.fw_maj_ver >= 5))) - pf->hw_features |= I40E_HW_USE_SET_LLDP_MIB; +#ifdef HAVE_XDP_SUPPORT +/** + * i40e_enter_busy_conf - Enters busy config state + * @vsi: vsi + * + * Returns 0 on success, <0 for failure. + **/ +static int i40e_enter_busy_conf(struct i40e_vsi *vsi) +{ + struct i40e_pf *pf = vsi->back; + int timeout = 50; - if (pf->hw.func_caps.vmdq) { - pf->num_vmdq_vsis = I40E_DEFAULT_NUM_VMDQ_VSI; - pf->flags |= I40E_FLAG_VMDQ_ENABLED; - pf->num_vmdq_qps = i40e_default_queues_per_vmdq(pf); + while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) { + timeout--; + if (!timeout) + return -EBUSY; + usleep_range(1000, 2000); } - if (pf->hw.func_caps.iwarp) { - pf->flags |= I40E_FLAG_IWARP_ENABLED; - /* IWARP needs one extra vector for CQP just like MISC.*/ - pf->num_iwarp_msix = (int)num_online_cpus() + 1; - } + return 0; +} -#ifdef CONFIG_PCI_IOV - if (pf->hw.func_caps.num_vfs && pf->hw.partition_id == 1) { - pf->num_vf_qps = I40E_DEFAULT_QUEUES_PER_VF; - pf->flags |= I40E_FLAG_SRIOV_ENABLED; - pf->num_req_vfs = min_t(int, - pf->hw.func_caps.num_vfs, - I40E_MAX_VF_COUNT); - } -#endif /* CONFIG_PCI_IOV */ - pf->eeprom_version = 0xDEAD; - pf->lan_veb = I40E_NO_VEB; - pf->lan_vsi = I40E_NO_VSI; +/** + * i40e_exit_busy_conf - Exits busy config state + * @vsi: vsi + **/ +static void i40e_exit_busy_conf(struct i40e_vsi *vsi) +{ + struct i40e_pf *pf = vsi->back; - /* By default FW has this off for performance reasons */ - pf->flags &= ~I40E_FLAG_VEB_STATS_ENABLED; + clear_bit(__I40E_CONFIG_BUSY, pf->state); +} - /* set up queue assignment tracking */ - size = sizeof(struct i40e_lump_tracking) - + (sizeof(u16) * pf->hw.func_caps.num_tx_qp); - pf->qp_pile = kzalloc(size, GFP_KERNEL); - if (!pf->qp_pile) { - err = -ENOMEM; - goto sw_init_done; +/** + * i40e_queue_pair_reset_stats - Resets all statistics for a queue pair + * @vsi: vsi + * @queue_pair: queue pair + **/ +static void i40e_queue_pair_reset_stats(struct i40e_vsi *vsi, int queue_pair) +{ + memset(&vsi->rx_rings[queue_pair]->rx_stats, 0, + sizeof(vsi->rx_rings[queue_pair]->rx_stats)); + memset(&vsi->tx_rings[queue_pair]->stats, 0, + sizeof(vsi->tx_rings[queue_pair]->stats)); + if (i40e_enabled_xdp_vsi(vsi)) { + memset(&vsi->xdp_rings[queue_pair]->stats, 0, + sizeof(vsi->xdp_rings[queue_pair]->stats)); } - pf->qp_pile->num_entries = pf->hw.func_caps.num_tx_qp; - pf->qp_pile->search_hint = 0; +} - pf->tx_timeout_recovery_level = 1; +/** + * i40e_queue_pair_clean_rings - Cleans all the rings of a queue pair + * @vsi: vsi + * @queue_pair: queue pair + **/ +static void i40e_queue_pair_clean_rings(struct i40e_vsi *vsi, int queue_pair) +{ + i40e_clean_tx_ring(vsi->tx_rings[queue_pair]); + if (i40e_enabled_xdp_vsi(vsi)) + i40e_clean_tx_ring(vsi->xdp_rings[queue_pair]); + i40e_clean_rx_ring(vsi->rx_rings[queue_pair]); +} - mutex_init(&pf->switch_mutex); +/** + * i40e_queue_pair_toggle_napi - Enables/disables NAPI for a queue pair + * @vsi: vsi + * @queue_pair: queue pair + * @enable: true for enable, false for disable + **/ +static void i40e_queue_pair_toggle_napi(struct i40e_vsi *vsi, int queue_pair, + bool enable) +{ + struct i40e_ring *rxr = vsi->rx_rings[queue_pair]; + struct i40e_q_vector *q_vector = rxr->q_vector; -sw_init_done: - return err; + if (!vsi->netdev) + return; + + /* All rings in a qp belong to the same qvector. */ + if (q_vector->rx.ring || q_vector->tx.ring) { + if (enable) + napi_enable(&q_vector->napi); + else + napi_disable(&q_vector->napi); + } } /** - * i40e_set_ntuple - set the ntuple feature flag and take action - * @pf: board private structure to initialize - * @features: the feature set that the stack is suggesting + * i40e_queue_pair_toggle_rings - Enables/disables all rings for a queue pair + * @vsi: vsi + * @queue_pair: queue pair + * @enable: true for enable, false for disable * - * returns a bool to indicate if reset needs to happen + * Returns 0 on success, <0 on failure. **/ -bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features) +static int i40e_queue_pair_toggle_rings(struct i40e_vsi *vsi, int queue_pair, + bool enable) { - bool need_reset = false; + struct i40e_pf *pf = vsi->back; + int pf_q, ret = 0; - /* Check if Flow Director n-tuple support was enabled or disabled. If - * the state changed, we need to reset. + pf_q = vsi->base_queue + queue_pair; + ret = i40e_control_wait_tx_q(vsi->seid, pf, pf_q, + false /*is xdp*/, enable); + if (ret) { + dev_info(&pf->pdev->dev, + "VSI seid %d Tx ring %d %sable timeout\n", + vsi->seid, pf_q, (enable ? "en" : "dis")); + return ret; + } + + i40e_control_rx_q(pf, pf_q, enable); + ret = i40e_pf_rxq_wait(pf, pf_q, enable); + if (ret) { + dev_info(&pf->pdev->dev, + "VSI seid %d Rx ring %d %sable timeout\n", + vsi->seid, pf_q, (enable ? "en" : "dis")); + return ret; + } + + /* Due to HW errata, on Rx disable only, the register can + * indicate done before it really is. Needs 50ms to be sure */ - if (features & NETIF_F_NTUPLE) { - /* Enable filters and mark for reset */ - if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED)) - need_reset = true; - /* enable FD_SB only if there is MSI-X vector */ - if (pf->num_fdsb_msix > 0) - pf->flags |= I40E_FLAG_FD_SB_ENABLED; - } else { - /* turn off filters, mark for reset and clear SW filter list */ - if (pf->flags & I40E_FLAG_FD_SB_ENABLED) { - need_reset = true; - i40e_fdir_filter_exit(pf); - } - pf->flags &= ~(I40E_FLAG_FD_SB_ENABLED | - I40E_FLAG_FD_SB_AUTO_DISABLED); - /* reset fd counters */ - pf->fd_add_err = 0; - pf->fd_atr_cnt = 0; - /* if ATR was auto disabled it can be re-enabled. */ - if (pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED) { - pf->flags &= ~I40E_FLAG_FD_ATR_AUTO_DISABLED; - if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && - (I40E_DEBUG_FD & pf->hw.debug_mask)) - dev_info(&pf->pdev->dev, "ATR re-enabled.\n"); - } + if (!enable) + mdelay(50); + + if (!i40e_enabled_xdp_vsi(vsi)) + return ret; + + ret = i40e_control_wait_tx_q(vsi->seid, pf, + pf_q + vsi->alloc_queue_pairs, + true /*is xdp*/, enable); + if (ret) { + dev_info(&pf->pdev->dev, + "VSI seid %d XDP Tx ring %d %sable timeout\n", + vsi->seid, pf_q, (enable ? "en" : "dis")); } - return need_reset; + + return ret; } /** - * i40e_clear_rss_lut - clear the rx hash lookup table - * @vsi: the VSI being configured + * i40e_queue_pair_enable_irq - Enables interrupts for a queue pair + * @vsi: vsi + * @queue_pair: queue_pair **/ -static void i40e_clear_rss_lut(struct i40e_vsi *vsi) +static void i40e_queue_pair_enable_irq(struct i40e_vsi *vsi, int queue_pair) { + struct i40e_ring *rxr = vsi->rx_rings[queue_pair]; struct i40e_pf *pf = vsi->back; struct i40e_hw *hw = &pf->hw; - u16 vf_id = vsi->vf_id; - u8 i; - if (vsi->type == I40E_VSI_MAIN) { - for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) - wr32(hw, I40E_PFQF_HLUT(i), 0); - } else if (vsi->type == I40E_VSI_SRIOV) { - for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) - i40e_write_rx_ctl(hw, I40E_VFQF_HLUT1(i, vf_id), 0); - } else { - dev_err(&pf->pdev->dev, "Cannot set RSS LUT - invalid VSI type\n"); - } + /* All rings in a qp belong to the same qvector. */ + if (pf->flags & I40E_FLAG_MSIX_ENABLED) + i40e_irq_dynamic_enable(vsi, rxr->q_vector->v_idx); + else + i40e_irq_dynamic_enable_icr0(pf); + + i40e_flush(hw); } /** - * i40e_set_features - set the netdev feature flags - * @netdev: ptr to the netdev being adjusted - * @features: the feature set that the stack is suggesting - * Note: expects to be called while under rtnl_lock() + * i40e_queue_pair_disable_irq - Disables interrupts for a queue pair + * @vsi: vsi + * @queue_pair: queue_pair **/ -static int i40e_set_features(struct net_device *netdev, - netdev_features_t features) +static void i40e_queue_pair_disable_irq(struct i40e_vsi *vsi, int queue_pair) { - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_vsi *vsi = np->vsi; + struct i40e_ring *rxr = vsi->rx_rings[queue_pair]; struct i40e_pf *pf = vsi->back; - bool need_reset; - - if (features & NETIF_F_RXHASH && !(netdev->features & NETIF_F_RXHASH)) - i40e_pf_config_rss(pf); - else if (!(features & NETIF_F_RXHASH) && - netdev->features & NETIF_F_RXHASH) - i40e_clear_rss_lut(vsi); - - if (features & NETIF_F_HW_VLAN_CTAG_RX) - i40e_vlan_stripping_enable(vsi); - else - i40e_vlan_stripping_disable(vsi); - - need_reset = i40e_set_ntuple(pf, features); + struct i40e_hw *hw = &pf->hw; - if (need_reset) - i40e_do_reset(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED), true); + /* For simplicity, instead of removing the qp interrupt causes + * from the interrupt linked list, we simply disable the interrupt, and + * leave the list intact. + * + * All rings in a qp belong to the same qvector. + */ + if (pf->flags & I40E_FLAG_MSIX_ENABLED) { + u32 intpf = vsi->base_vector + rxr->q_vector->v_idx; - return 0; + wr32(hw, I40E_PFINT_DYN_CTLN(intpf - 1), 0); + i40e_flush(hw); + synchronize_irq(pf->msix_entries[intpf].vector); + } else { + /* Legacy and MSI mode - this stops all interrupt handling */ + wr32(hw, I40E_PFINT_ICR0_ENA, 0); + wr32(hw, I40E_PFINT_DYN_CTL0, 0); + i40e_flush(hw); + synchronize_irq(pf->pdev->irq); + } } /** - * i40e_get_udp_port_idx - Lookup a possibly offloaded for Rx UDP port - * @pf: board private structure - * @port: The UDP port to look up + * i40e_queue_pair_disable - Disables a queue pair + * @vsi: vsi + * @queue_pair: queue pair * - * Returns the index number or I40E_MAX_PF_UDP_OFFLOAD_PORTS if port not found + * Returns 0 on success, <0 on failure. **/ -static u8 i40e_get_udp_port_idx(struct i40e_pf *pf, u16 port) +int i40e_queue_pair_disable(struct i40e_vsi *vsi, int queue_pair) { - u8 i; + int err; - for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { - if (pf->udp_ports[i].port == port) - return i; - } + err = i40e_enter_busy_conf(vsi); + if (err) + return err; - return i; + i40e_queue_pair_disable_irq(vsi, queue_pair); + err = i40e_queue_pair_toggle_rings(vsi, queue_pair, false /* off */); + i40e_queue_pair_toggle_napi(vsi, queue_pair, false /* off */); + i40e_queue_pair_clean_rings(vsi, queue_pair); + i40e_queue_pair_reset_stats(vsi, queue_pair); + + return err; } /** - * i40e_udp_tunnel_add - Get notifications about UDP tunnel ports that come up - * @netdev: This physical port's netdev - * @ti: Tunnel endpoint information + * i40e_queue_pair_enable - Enables a queue pair + * @vsi: vsi + * @queue_pair: queue pair + * + * Returns 0 on success, <0 on failure. **/ -static void i40e_udp_tunnel_add(struct net_device *netdev, - struct udp_tunnel_info *ti) +int i40e_queue_pair_enable(struct i40e_vsi *vsi, int queue_pair) { - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_vsi *vsi = np->vsi; - struct i40e_pf *pf = vsi->back; - u16 port = ntohs(ti->port); - u8 next_idx; - u8 idx; + int err; - idx = i40e_get_udp_port_idx(pf, port); + err = i40e_configure_tx_ring(vsi->tx_rings[queue_pair]); + if (err) + return err; - /* Check if port already exists */ - if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) { - netdev_info(netdev, "port %d already offloaded\n", port); - return; + if (i40e_enabled_xdp_vsi(vsi)) { + err = i40e_configure_tx_ring(vsi->xdp_rings[queue_pair]); + if (err) + return err; } - /* Now check if there is space to add the new port */ - next_idx = i40e_get_udp_port_idx(pf, 0); + err = i40e_configure_rx_ring(vsi->rx_rings[queue_pair]); + if (err) + return err; - if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) { - netdev_info(netdev, "maximum number of offloaded UDP ports reached, not adding port %d\n", - port); - return; - } + err = i40e_queue_pair_toggle_rings(vsi, queue_pair, true /* on */); + i40e_queue_pair_toggle_napi(vsi, queue_pair, true /* on */); + i40e_queue_pair_enable_irq(vsi, queue_pair); - switch (ti->type) { - case UDP_TUNNEL_TYPE_VXLAN: - pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_VXLAN; - break; - case UDP_TUNNEL_TYPE_GENEVE: - if (!(pf->hw_features & I40E_HW_GENEVE_OFFLOAD_CAPABLE)) - return; - pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_NGE; - break; - default: - return; - } + i40e_exit_busy_conf(vsi); - /* New port: add it and mark its index in the bitmap */ - pf->udp_ports[next_idx].port = port; - pf->pending_udp_bitmap |= BIT_ULL(next_idx); - pf->flags |= I40E_FLAG_UDP_FILTER_SYNC; + return err; } /** - * i40e_udp_tunnel_del - Get notifications about UDP tunnel ports that go away - * @netdev: This physical port's netdev - * @ti: Tunnel endpoint information + * i40e_xdp_setup - add/remove an XDP program + * @vsi: VSI to changed + * @prog: XDP program **/ -static void i40e_udp_tunnel_del(struct net_device *netdev, - struct udp_tunnel_info *ti) +static int i40e_xdp_setup(struct i40e_vsi *vsi, + struct bpf_prog *prog) { - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_vsi *vsi = np->vsi; + int frame_size = vsi->netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; struct i40e_pf *pf = vsi->back; - u16 port = ntohs(ti->port); - u8 idx; + struct bpf_prog *old_prog; + bool need_reset; + int i; - idx = i40e_get_udp_port_idx(pf, port); + /* Don't allow frames that span over multiple buffers */ + if (frame_size > vsi->rx_buf_len) + return -EINVAL; - /* Check if port already exists */ - if (idx >= I40E_MAX_PF_UDP_OFFLOAD_PORTS) - goto not_found; + if (!i40e_enabled_xdp_vsi(vsi) && !prog) + return 0; - switch (ti->type) { - case UDP_TUNNEL_TYPE_VXLAN: - if (pf->udp_ports[idx].type != I40E_AQC_TUNNEL_TYPE_VXLAN) - goto not_found; - break; - case UDP_TUNNEL_TYPE_GENEVE: - if (pf->udp_ports[idx].type != I40E_AQC_TUNNEL_TYPE_NGE) - goto not_found; - break; - default: - goto not_found; - } + /* When turning XDP on->off/off->on we reset and rebuild the rings. */ + need_reset = (i40e_enabled_xdp_vsi(vsi) != !!prog); - /* if port exists, set it to 0 (mark for deletion) - * and make it pending - */ - pf->udp_ports[idx].port = 0; - pf->pending_udp_bitmap |= BIT_ULL(idx); - pf->flags |= I40E_FLAG_UDP_FILTER_SYNC; + if (need_reset) + i40e_prep_for_reset(pf, true); - return; -not_found: - netdev_warn(netdev, "UDP port %d was not found, not deleting\n", - port); -} + old_prog = xchg(&vsi->xdp_prog, prog); -static int i40e_get_phys_port_id(struct net_device *netdev, - struct netdev_phys_item_id *ppid) -{ - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_pf *pf = np->vsi->back; - struct i40e_hw *hw = &pf->hw; + if (need_reset) + i40e_reset_and_rebuild(pf, true, true); - if (!(pf->hw_features & I40E_HW_PORT_ID_VALID)) - return -EOPNOTSUPP; + for (i = 0; i < vsi->num_queue_pairs; i++) + WRITE_ONCE(vsi->rx_rings[i]->xdp_prog, vsi->xdp_prog); - ppid->id_len = min_t(int, sizeof(hw->mac.port_addr), sizeof(ppid->id)); - memcpy(ppid->id, hw->mac.port_addr, ppid->id_len); + if (old_prog) + bpf_prog_put(old_prog); return 0; } /** - * i40e_ndo_fdb_add - add an entry to the hardware database - * @ndm: the input from the stack - * @tb: pointer to array of nladdr (unused) - * @dev: the net device pointer - * @addr: the MAC address entry being added - * @flags: instructions from stack about fdb operation - */ -static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, - const unsigned char *addr, u16 vid, - u16 flags) + * i40e_xdp - implements ndo_xdp for i40e + * @dev: netdevice + * @xdp: XDP command + **/ +#ifdef HAVE_NDO_BPF +static int i40e_xdp(struct net_device *dev, + struct netdev_bpf *xdp) +#else +static int i40e_xdp(struct net_device *dev, + struct netdev_xdp *xdp) +#endif { struct i40e_netdev_priv *np = netdev_priv(dev); - struct i40e_pf *pf = np->vsi->back; - int err = 0; + struct i40e_vsi *vsi = np->vsi; - if (!(pf->flags & I40E_FLAG_SRIOV_ENABLED)) - return -EOPNOTSUPP; + if (vsi->type != I40E_VSI_MAIN) + return -EINVAL; - if (vid) { - pr_info("%s: vlans aren't supported yet for dev_uc|mc_add()\n", dev->name); + switch (xdp->command) { + case XDP_SETUP_PROG: + return i40e_xdp_setup(vsi, xdp->prog); + case XDP_QUERY_PROG: +#ifndef NO_NETDEV_BPF_PROG_ATTACHED + xdp->prog_attached = i40e_enabled_xdp_vsi(vsi); +#endif /* !NO_NETDEV_BPF_PROG_ATTACHED */ + xdp->prog_id = vsi->xdp_prog ? vsi->xdp_prog->aux->id : 0; + return 0; + default: return -EINVAL; } +} +#endif /* HAVE_XDP_SUPPORT */ +#endif /* HAVE_NDO_FEATURES_CHECK */ +#ifndef USE_DEFAULT_FDB_DEL_DUMP +#if defined(HAVE_NDO_FDB_ADD_VID) +static int i40e_ndo_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, const unsigned char *addr, + u16 vid) +#elif defined(HAVE_FDB_DEL_NLATTR) +static int i40e_ndo_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, const unsigned char *addr) +#elif defined(USE_CONST_DEV_UC_CHAR) +static int i40e_ndo_fdb_del(struct ndmsg *ndm, struct net_device *dev, + const unsigned char *addr) +#else +static int i40e_ndo_fdb_del(struct ndmsg *ndm, struct net_device *dev, + unsigned char *addr) +#endif /* HAVE_NDO_FDB_ADD_VID */ +{ + struct i40e_netdev_priv *np = netdev_priv(dev); + struct i40e_pf *pf = np->vsi->back; + int err = -EOPNOTSUPP; - /* Hardware does not support aging addresses so if a - * ndm_state is given only allow permanent addresses - */ - if (ndm->ndm_state && !(ndm->ndm_state & NUD_PERMANENT)) { + if (ndm->ndm_state & NUD_PERMANENT) { netdev_info(dev, "FDB only supports static addresses\n"); return -EINVAL; } - if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) - err = dev_uc_add_excl(dev, addr); - else if (is_multicast_ether_addr(addr)) - err = dev_mc_add_excl(dev, addr); - else - err = -EINVAL; - - /* Only return duplicate errors if NLM_F_EXCL is set */ - if (err == -EEXIST && !(flags & NLM_F_EXCL)) - err = 0; + if (pf->flags & I40E_FLAG_SRIOV_ENABLED) { + if (is_unicast_ether_addr(addr)) + err = dev_uc_del(dev, addr); + else if (is_multicast_ether_addr(addr)) + err = dev_mc_del(dev, addr); + else + err = -EINVAL; + } return err; } +static int i40e_ndo_fdb_dump(struct sk_buff *skb, + struct netlink_callback *cb, + struct net_device *dev, + int idx) +{ + struct i40e_netdev_priv *np = netdev_priv(dev); + struct i40e_pf *pf = np->vsi->back; + + if (pf->flags & I40E_FLAG_SRIOV_ENABLED) + idx = ndo_dflt_fdb_dump(skb, cb, dev, idx); + + return idx; +} + +#endif /* USE_DEFAULT_FDB_DEL_DUMP */ +#ifdef HAVE_BRIDGE_ATTRIBS /** * i40e_ndo_bridge_setlink - Set the hardware bridge mode * @dev: the netdev being configured * @nlh: RTNL message + * @flags: bridge flags + * @extack: netdev extended ack structure * * Inserts a new hardware bridge if not already created and * enables the bridging mode requested (VEB or VEPA). If the @@ -9363,9 +13118,19 @@ static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], * * Note: expects to be called while under rtnl_lock() **/ +#if defined(HAVE_NDO_BRIDGE_SETLINK_EXTACK) +static int i40e_ndo_bridge_setlink(struct net_device *dev, + struct nlmsghdr *nlh, + u16 flags, + struct netlink_ext_ack *extack) +#elif defined(HAVE_NDO_BRIDGE_SET_DEL_LINK_FLAGS) static int i40e_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags) +#else +static int i40e_ndo_bridge_setlink(struct net_device *dev, + struct nlmsghdr *nlh) +#endif { struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; @@ -9412,13 +13177,11 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev, } else if (mode != veb->bridge_mode) { /* Existing HW bridge but different mode needs reset */ veb->bridge_mode = mode; - /* TODO: If no VFs or VMDq VSIs, disallow VEB mode */ if (mode == BRIDGE_MODE_VEB) pf->flags |= I40E_FLAG_VEB_MODE_ENABLED; else pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED; - i40e_do_reset(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED), - true); + i40e_do_reset(pf, I40E_PF_RESET_FLAG, true); break; } } @@ -9435,196 +13198,239 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev, * @filter_mask: unused * @nlflags: netlink flags passed in * - * Return the mode in which the hardware bridge is operating in - * i.e VEB or VEPA. - **/ -static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, - struct net_device *dev, - u32 __always_unused filter_mask, - int nlflags) -{ - struct i40e_netdev_priv *np = netdev_priv(dev); - struct i40e_vsi *vsi = np->vsi; - struct i40e_pf *pf = vsi->back; - struct i40e_veb *veb = NULL; - int i; - - /* Only for PF VSI for now */ - if (vsi->seid != pf->vsi[pf->lan_vsi]->seid) - return -EOPNOTSUPP; - - /* Find the HW bridge for the PF VSI */ - for (i = 0; i < I40E_MAX_VEB && !veb; i++) { - if (pf->veb[i] && pf->veb[i]->seid == vsi->uplink_seid) - veb = pf->veb[i]; - } - - if (!veb) - return 0; - - return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode, - 0, 0, nlflags, filter_mask, NULL); -} - -/** - * i40e_features_check - Validate encapsulated packet conforms to limits - * @skb: skb buff - * @dev: This physical port's netdev - * @features: Offload features that the stack believes apply - **/ -static netdev_features_t i40e_features_check(struct sk_buff *skb, - struct net_device *dev, - netdev_features_t features) -{ - size_t len; - - /* No point in doing any of this if neither checksum nor GSO are - * being requested for this frame. We can rule out both by just - * checking for CHECKSUM_PARTIAL - */ - if (skb->ip_summed != CHECKSUM_PARTIAL) - return features; - - /* We cannot support GSO if the MSS is going to be less than - * 64 bytes. If it is then we need to drop support for GSO. - */ - if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_size < 64)) - features &= ~NETIF_F_GSO_MASK; - - /* MACLEN can support at most 63 words */ - len = skb_network_header(skb) - skb->data; - if (len & ~(63 * 2)) - goto out_err; - - /* IPLEN and EIPLEN can support at most 127 dwords */ - len = skb_transport_header(skb) - skb_network_header(skb); - if (len & ~(127 * 4)) - goto out_err; - - if (skb->encapsulation) { - /* L4TUNLEN can support 127 words */ - len = skb_inner_network_header(skb) - skb_transport_header(skb); - if (len & ~(127 * 2)) - goto out_err; - - /* IPLEN can support at most 127 dwords */ - len = skb_inner_transport_header(skb) - - skb_inner_network_header(skb); - if (len & ~(127 * 4)) - goto out_err; - } - - /* No need to validate L4LEN as TCP is the only protocol with a - * a flexible value and we support all possible values supported - * by TCP, which is at most 15 dwords - */ - - return features; -out_err: - return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); -} - -/** - * i40e_xdp_setup - add/remove an XDP program - * @vsi: VSI to changed - * @prog: XDP program - **/ -static int i40e_xdp_setup(struct i40e_vsi *vsi, - struct bpf_prog *prog) -{ - int frame_size = vsi->netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; - struct i40e_pf *pf = vsi->back; - struct bpf_prog *old_prog; - bool need_reset; - int i; - - /* Don't allow frames that span over multiple buffers */ - if (frame_size > vsi->rx_buf_len) - return -EINVAL; - - if (!i40e_enabled_xdp_vsi(vsi) && !prog) - return 0; - - /* When turning XDP on->off/off->on we reset and rebuild the rings. */ - need_reset = (i40e_enabled_xdp_vsi(vsi) != !!prog); - - if (need_reset) - i40e_prep_for_reset(pf, true); - - old_prog = xchg(&vsi->xdp_prog, prog); - - if (need_reset) - i40e_reset_and_rebuild(pf, true, true); - - for (i = 0; i < vsi->num_queue_pairs; i++) - WRITE_ONCE(vsi->rx_rings[i]->xdp_prog, vsi->xdp_prog); - - if (old_prog) - bpf_prog_put(old_prog); - - return 0; -} - -/** - * i40e_xdp - implements ndo_xdp for i40e - * @dev: netdevice - * @xdp: XDP command + * Return the mode in which the hardware bridge is operating in + * i.e VEB or VEPA. **/ -static int i40e_xdp(struct net_device *dev, - struct netdev_xdp *xdp) +#ifdef HAVE_NDO_BRIDGE_GETLINK_NLFLAGS +static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, + struct net_device *dev, + u32 __always_unused filter_mask, + int nlflags) +#elif defined(HAVE_BRIDGE_FILTER) +static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, + struct net_device *dev, + u32 __always_unused filter_mask) +#else +static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, + struct net_device *dev) +#endif /* NDO_BRIDGE_STUFF */ { struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + struct i40e_veb *veb = NULL; + int i; - if (vsi->type != I40E_VSI_MAIN) - return -EINVAL; + /* Only for PF VSI for now */ + if (vsi->seid != pf->vsi[pf->lan_vsi]->seid) + return -EOPNOTSUPP; - switch (xdp->command) { - case XDP_SETUP_PROG: - return i40e_xdp_setup(vsi, xdp->prog); - case XDP_QUERY_PROG: - xdp->prog_attached = i40e_enabled_xdp_vsi(vsi); - xdp->prog_id = vsi->xdp_prog ? vsi->xdp_prog->aux->id : 0; - return 0; - default: - return -EINVAL; + /* Find the HW bridge for the PF VSI */ + for (i = 0; i < I40E_MAX_VEB && !veb; i++) { + if (pf->veb[i] && pf->veb[i]->seid == vsi->uplink_seid) + veb = pf->veb[i]; } + + if (!veb) + return 0; + +#ifdef HAVE_NDO_DFLT_BRIDGE_GETLINK_VLAN_SUPPORT + return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode, + 0, 0, nlflags, filter_mask, NULL); +#elif defined(HAVE_NDO_BRIDGE_GETLINK_NLFLAGS) + return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode, + 0, 0, nlflags); +#elif defined(HAVE_NDO_FDB_ADD_VID) || \ + defined NDO_DFLT_BRIDGE_GETLINK_HAS_BRFLAGS + return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode, + 0, 0); +#else + return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode); +#endif /* HAVE_NDO_BRIDGE_XX */ } +#endif /* HAVE_BRIDGE_ATTRIBS */ +#endif /* HAVE_FDB_OPS */ +#ifdef HAVE_NET_DEVICE_OPS static const struct net_device_ops i40e_netdev_ops = { .ndo_open = i40e_open, .ndo_stop = i40e_close, .ndo_start_xmit = i40e_lan_xmit_frame, +#if defined(HAVE_NDO_GET_STATS64) || defined(HAVE_VOID_NDO_GET_STATS64) .ndo_get_stats64 = i40e_get_netdev_stats_struct, +#else + .ndo_get_stats = i40e_get_netdev_stats_struct, +#endif .ndo_set_rx_mode = i40e_set_rx_mode, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = i40e_set_mac, +#ifdef HAVE_RHEL7_EXTENDED_MIN_MAX_MTU + .extended.ndo_change_mtu = i40e_change_mtu, +#else .ndo_change_mtu = i40e_change_mtu, +#endif /* HAVE_RHEL7_EXTENDED_MIN_MAX_MTU */ +#if defined(HAVE_PTP_1588_CLOCK) || defined(HAVE_I40E_INTELCIM_IOCTL) .ndo_do_ioctl = i40e_ioctl, +#endif .ndo_tx_timeout = i40e_tx_timeout, +#ifdef HAVE_VLAN_RX_REGISTER + .ndo_vlan_rx_register = i40e_vlan_rx_register, +#endif .ndo_vlan_rx_add_vid = i40e_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = i40e_vlan_rx_kill_vid, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = i40e_netpoll, #endif +#ifdef HAVE_SETUP_TC +#ifdef HAVE_RHEL7_NETDEV_OPS_EXT_NDO_SETUP_TC + .extended.ndo_setup_tc_rh = __i40e_setup_tc, +#else +#ifdef NETIF_F_HW_TC .ndo_setup_tc = __i40e_setup_tc, - .ndo_set_features = i40e_set_features, +#else + .ndo_setup_tc = i40e_setup_tc, +#endif /* NETIF_F_HW_TC */ +#endif /* HAVE_RHEL7_NETDEV_OPS_EXT_NDO_SETUP_TC */ +#endif /* HAVE_SETUP_TC */ +#ifdef HAVE_RHEL7_NET_DEVICE_OPS_EXT +/* RHEL7 requires this to be defined to enable extended ops. RHEL7 uses the + * function get_ndo_ext to retrieve offsets for extended fields from with the + * net_device_ops struct and ndo_size is checked to determine whether or not + * the offset is valid. + */ + .ndo_size = sizeof(const struct net_device_ops), +#endif +#ifdef IFLA_VF_MAX .ndo_set_vf_mac = i40e_ndo_set_vf_mac, +#ifdef HAVE_RHEL7_NETDEV_OPS_EXT_NDO_SET_VF_VLAN + .extended.ndo_set_vf_vlan = i40e_ndo_set_vf_port_vlan, +#else .ndo_set_vf_vlan = i40e_ndo_set_vf_port_vlan, +#endif +#ifdef HAVE_VF_STATS + .ndo_get_vf_stats = i40e_get_vf_stats, +#endif +#ifdef HAVE_NDO_SET_VF_MIN_MAX_TX_RATE .ndo_set_vf_rate = i40e_ndo_set_vf_bw, +#else + .ndo_set_vf_tx_rate = i40e_ndo_set_vf_bw, +#endif .ndo_get_vf_config = i40e_ndo_get_vf_config, - .ndo_set_vf_link_state = i40e_ndo_set_vf_link_state, +#ifdef HAVE_VF_SPOOFCHK_CONFIGURE .ndo_set_vf_spoofchk = i40e_ndo_set_vf_spoofchk, +#endif +#ifdef HAVE_NDO_SET_VF_TRUST +#ifdef HAVE_RHEL7_NET_DEVICE_OPS_EXT + .extended.ndo_set_vf_trust = i40e_ndo_set_vf_trust, +#else .ndo_set_vf_trust = i40e_ndo_set_vf_trust, +#endif /* HAVE_RHEL7_NET_DEVICE_OPS_EXT */ +#endif /* HAVE_NDO_SET_VF_TRUST */ +#endif /* IFLA_VF_MAX */ +#ifdef HAVE_UDP_ENC_RX_OFFLOAD +#ifdef HAVE_RHEL7_NETDEV_OPS_EXT_NDO_UDP_TUNNEL + .extended.ndo_udp_tunnel_add = i40e_udp_tunnel_add, + .extended.ndo_udp_tunnel_del = i40e_udp_tunnel_del, +#else .ndo_udp_tunnel_add = i40e_udp_tunnel_add, .ndo_udp_tunnel_del = i40e_udp_tunnel_del, - .ndo_get_phys_port_id = i40e_get_phys_port_id, +#endif +#else /* !HAVE_UDP_ENC_RX_OFFLOAD */ +#ifdef HAVE_VXLAN_RX_OFFLOAD +#if IS_ENABLED(CONFIG_VXLAN) + .ndo_add_vxlan_port = i40e_add_vxlan_port, + .ndo_del_vxlan_port = i40e_del_vxlan_port, +#endif +#endif /* HAVE_VXLAN_RX_OFFLOAD */ +#ifdef HAVE_GENEVE_RX_OFFLOAD +#if IS_ENABLED(CONFIG_GENEVE) + .ndo_add_geneve_port = i40e_add_geneve_port, + .ndo_del_geneve_port = i40e_del_geneve_port, +#endif +#endif /* HAVE_GENEVE_RX_OFFLOAD */ +#endif /* HAVE_UDP_ENC_RX_OFFLOAD */ +#ifdef HAVE_NDO_GET_PHYS_PORT_ID + .ndo_get_phys_port_id = i40e_get_phys_port_id, +#endif /* HAVE_NDO_GET_PHYS_PORT_ID */ +#ifdef HAVE_FDB_OPS .ndo_fdb_add = i40e_ndo_fdb_add, +#ifndef USE_DEFAULT_FDB_DEL_DUMP + .ndo_fdb_del = i40e_ndo_fdb_del, + .ndo_fdb_dump = i40e_ndo_fdb_dump, +#endif +#ifdef HAVE_NDO_FEATURES_CHECK .ndo_features_check = i40e_features_check, +#endif /* HAVE_NDO_FEATURES_CHECK */ +#ifdef HAVE_BRIDGE_ATTRIBS .ndo_bridge_getlink = i40e_ndo_bridge_getlink, .ndo_bridge_setlink = i40e_ndo_bridge_setlink, - .ndo_xdp = i40e_xdp, +#endif /* HAVE_BRIDGE_ATTRIBS */ +#endif /* HAVE_FDB_OPS */ +#ifdef HAVE_XDP_SUPPORT +#ifdef HAVE_NDO_BPF + .ndo_bpf = i40e_xdp, + .ndo_xdp_xmit = i40e_xdp_xmit, +#else + .ndo_xdp = i40e_xdp, + .ndo_xdp_xmit = i40e_xdp_xmit, + .ndo_xdp_flush = i40e_xdp_flush, +#endif /* HAVE_NDO_BPF */ +#endif /* HAVE_XDP_SUPPORT */ +#ifdef HAVE_RHEL6_NET_DEVICE_OPS_EXT +}; + +/* RHEL6 keeps these operations in a separate structure */ +static const struct net_device_ops_ext i40e_netdev_ops_ext = { + .size = sizeof(struct net_device_ops_ext), +#endif /* HAVE_RHEL6_NET_DEVICE_OPS_EXT */ +#ifdef HAVE_NDO_SET_FEATURES + .ndo_set_features = i40e_set_features, +#endif /* HAVE_NDO_SET_FEATURES */ +#ifdef HAVE_NDO_SET_VF_LINK_STATE + .ndo_set_vf_link_state = i40e_ndo_set_vf_link_state, +#endif }; +#else /* HAVE_NET_DEVICE_OPS */ +/** + * i40e_assign_netdev_ops - Initialize netdev operations function pointers + * @dev: ptr to the netdev struct + **/ +#ifdef HAVE_CONFIG_HOTPLUG +static void __devinit i40e_assign_netdev_ops(struct net_device *dev) +#else +static void i40e_assign_netdev_ops(struct net_device *dev) +#endif +{ + dev->open = i40e_open; + dev->stop = i40e_close; + dev->hard_start_xmit = i40e_lan_xmit_frame; + dev->get_stats = i40e_get_netdev_stats_struct; + +#ifdef HAVE_SET_RX_MODE + dev->set_rx_mode = i40e_set_rx_mode; +#endif + dev->set_multicast_list = i40e_set_rx_mode; + dev->set_mac_address = i40e_set_mac; + dev->change_mtu = i40e_change_mtu; +#if defined(HAVE_PTP_1588_CLOCK) || defined(HAVE_I40E_INTELCIM_IOCTL) + dev->do_ioctl = i40e_ioctl; +#endif + dev->tx_timeout = i40e_tx_timeout; +#ifdef NETIF_F_HW_VLAN_TX +#ifdef HAVE_VLAN_RX_REGISTER + dev->vlan_rx_register = i40e_vlan_rx_register; +#endif + dev->vlan_rx_add_vid = i40e_vlan_rx_add_vid; + dev->vlan_rx_kill_vid = i40e_vlan_rx_kill_vid; +#endif +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = i40e_netpoll; +#endif +#ifdef HAVE_NETDEV_SELECT_QUEUE + dev->select_queue = i40e_lan_select_queue; +#endif /* HAVE_NETDEV_SELECT_QUEUE */ +} +#endif /* HAVE_NET_DEVICE_OPS */ /** * i40e_config_netdev - Setup the netdev flags @@ -9655,42 +13461,112 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) hw_enc_features = NETIF_F_SG | NETIF_F_IP_CSUM | +#ifdef NETIF_F_IPV6_CSUM NETIF_F_IPV6_CSUM | +#endif NETIF_F_HIGHDMA | +#ifdef NETIF_F_SOFT_FEATURES NETIF_F_SOFT_FEATURES | +#endif NETIF_F_TSO | +#ifdef HAVE_ENCAP_TSO_OFFLOAD NETIF_F_TSO_ECN | NETIF_F_TSO6 | +#ifdef HAVE_GRE_ENCAP_OFFLOAD NETIF_F_GSO_GRE | +#ifdef NETIF_F_GSO_PARTIAL NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_PARTIAL | +#endif +#ifdef NETIF_F_GSO_IPXIP4 + NETIF_F_GSO_IPXIP4 | +#ifdef NETIF_F_GSO_IPXIP6 + NETIF_F_GSO_IPXIP6 | +#endif +#else +#ifdef NETIF_F_GSO_IPIP + NETIF_F_GSO_IPIP | +#endif +#ifdef NETIF_F_GSO_SIT + NETIF_F_GSO_SIT | +#endif +#endif +#endif NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM | +#endif /* HAVE_ENCAP_TSO_OFFLOAD */ NETIF_F_SCTP_CRC | +#ifdef NETIF_F_RXHASH NETIF_F_RXHASH | +#endif +#ifdef HAVE_NDO_SET_FEATURES NETIF_F_RXCSUM | +#endif 0; if (!(pf->hw_features & I40E_HW_OUTER_UDP_CSUM_CAPABLE)) +#ifndef NETIF_F_GSO_PARTIAL + hw_enc_features ^= NETIF_F_GSO_UDP_TUNNEL_CSUM; +#else netdev->gso_partial_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM; +#endif +#ifdef HAVE_ENCAP_CSUM_OFFLOAD netdev->hw_enc_features |= hw_enc_features; +#endif +#ifdef HAVE_NETDEV_VLAN_FEATURES /* record features VLANs can make use of */ +#ifdef NETIF_F_GSO_PARTIAL netdev->vlan_features |= hw_enc_features | NETIF_F_TSO_MANGLEID; +#else + netdev->vlan_features |= hw_enc_features; +#endif +#endif - if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) - netdev->hw_features |= NETIF_F_NTUPLE; hw_features = hw_enc_features | +#ifdef NETIF_F_HW_VLAN_CTAG_RX NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; +#else /* NETIF_F_HW_VLAN_CTAG_RX */ + NETIF_F_HW_VLAN_TX | + NETIF_F_HW_VLAN_RX; +#endif /* !NETIF_F_HW_VLAN_CTAG_RX */ + +#if defined(HAVE_NDO_SET_FEATURES) || defined(ETHTOOL_GRXRINGS) + if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) +#ifdef NETIF_F_HW_TC + hw_features |= NETIF_F_NTUPLE | NETIF_F_HW_TC; +#else + hw_features |= NETIF_F_NTUPLE; +#endif +#endif +#ifdef HAVE_NDO_SET_FEATURES +#ifdef HAVE_RHEL6_NET_DEVICE_OPS_EXT + hw_features |= get_netdev_hw_features(netdev); + set_netdev_hw_features(netdev, hw_features); +#else netdev->hw_features |= hw_features; +#endif +#endif /* HAVE_NDO_SET_FEATURES */ +#ifdef NETIF_F_HW_VLAN_CTAG_RX netdev->features |= hw_features | NETIF_F_HW_VLAN_CTAG_FILTER; +#else + netdev->features |= hw_features | NETIF_F_HW_VLAN_FILTER; +#endif +#ifdef NETIF_F_GSO_PARTIAL netdev->hw_enc_features |= NETIF_F_TSO_MANGLEID; +#endif + +#ifndef HAVE_NDO_SET_FEATURES +#ifdef NETIF_F_GRO + netdev->features |= NETIF_F_GRO; +#endif +#endif if (vsi->type == I40E_VSI_MAIN) { SET_NETDEV_DEV(netdev, &pf->pdev->dev); @@ -9718,7 +13594,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) snprintf(netdev->name, IFNAMSIZ, "%.*sv%%d", IFNAMSIZ - 4, pf->vsi[pf->lan_vsi]->netdev->name); - random_ether_addr(mac_addr); + eth_random_addr(mac_addr); spin_lock_bh(&vsi->mac_filter_hash_lock); i40e_add_mac_filter(vsi, mac_addr); @@ -9744,20 +13620,47 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) spin_unlock_bh(&vsi->mac_filter_hash_lock); ether_addr_copy(netdev->dev_addr, mac_addr); +#ifdef ETHTOOL_GPERMADDR ether_addr_copy(netdev->perm_addr, mac_addr); +#endif + +#ifdef HAVE_MPLS_FEATURES + if (pf->hw_features & I40E_HW_MPLS_HDR_OFFLOAD_CAPABLE) + netdev->mpls_features = NETIF_F_HW_CSUM; +#endif +#ifdef IFF_UNICAST_FLT netdev->priv_flags |= IFF_UNICAST_FLT; +#endif +#ifdef IFF_SUPP_NOFCS netdev->priv_flags |= IFF_SUPP_NOFCS; +#endif /* Setup netdev TC information */ i40e_vsi_config_netdev_tc(vsi, vsi->tc_config.enabled_tc); +#ifdef HAVE_NET_DEVICE_OPS netdev->netdev_ops = &i40e_netdev_ops; +#ifdef HAVE_RHEL6_NET_DEVICE_OPS_EXT + set_netdev_ops_ext(netdev, &i40e_netdev_ops_ext); +#endif /* HAVE_RHEL6_NET_DEVICE_OPS_EXT */ +#else /* HAVE_NET_DEVICE_OPS */ + i40e_assign_netdev_ops(netdev); +#endif /* HAVE_NET_DEVICE_OPS */ netdev->watchdog_timeo = 5 * HZ; +#ifdef SIOCETHTOOL i40e_set_ethtool_ops(netdev); +#endif +#ifdef HAVE_NETDEVICE_MIN_MAX_MTU /* MTU range: 68 - 9706 */ +#ifdef HAVE_RHEL7_EXTENDED_MIN_MAX_MTU + netdev->extended->min_mtu = ETH_MIN_MTU; + netdev->extended->max_mtu = I40E_MAX_RXBUFFER - I40E_PACKET_HDR_PAD; +#else netdev->min_mtu = ETH_MIN_MTU; netdev->max_mtu = I40E_MAX_RXBUFFER - I40E_PACKET_HDR_PAD; +#endif /* HAVE_RHEL7_EXTENDED_MIN_MAX_MTU */ +#endif /* HAVE_NETDEVICE_MIN_MAX_NTU */ return 0; } @@ -9789,7 +13692,7 @@ int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi) struct i40e_pf *pf = vsi->back; /* Uplink is not a bridge so default to VEB */ - if (vsi->veb_idx == I40E_NO_VEB) + if (vsi->veb_idx >= I40E_MAX_VEB) return 1; veb = pf->veb[vsi->veb_idx]; @@ -9799,6 +13702,7 @@ int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi) return -ENOENT; } +#ifdef HAVE_BRIDGE_ATTRIBS /* Uplink is a bridge in VEPA mode */ if (veb->bridge_mode & BRIDGE_MODE_VEPA) { return 0; @@ -9806,6 +13710,10 @@ int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi) /* Uplink is a bridge in VEB mode */ return 1; } +#else + if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED) + return 1; +#endif /* VEPA is now default bridge, so return 0 */ return 0; @@ -9830,6 +13738,7 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) u8 enabled_tc = 0x1; /* TC0 enabled */ int f_count = 0; + u32 val; memset(&ctxt, 0, sizeof(ctxt)); switch (vsi->type) { @@ -9860,6 +13769,31 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) enabled_tc = i40e_pf_get_tc_map(pf); + /* Source pruning is enabled by default, so the flag is + * negative logic - if it's set, we need to fiddle with + * the VSI to disable source pruning. + */ + if (pf->flags & I40E_FLAG_SOURCE_PRUNING_DISABLED) { + memset(&ctxt, 0, sizeof(ctxt)); + ctxt.seid = pf->main_vsi_seid; + ctxt.pf_num = pf->hw.pf_id; + ctxt.vf_num = 0; + ctxt.info.valid_sections |= + cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID); + ctxt.info.switch_id = + cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_LOCAL_LB); + ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL); + if (ret) { + dev_info(&pf->pdev->dev, + "update vsi failed, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); + ret = -ENOENT; + goto err; + } + } + /* MFP mode setup queue map and update VSI */ if ((pf->flags & I40E_FLAG_MFP_ENABLED) && !(pf->hw.func_caps.iscsi)) { /* NIC type PF */ @@ -9967,12 +13901,11 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) ctxt.info.valid_sections |= cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID); ctxt.info.port_vlan_flags |= I40E_AQ_VSI_PVLAN_MODE_ALL; - if (pf->vf[vsi->vf_id].spoofchk) { + if (pf->vf[vsi->vf_id].mac_anti_spoof) { ctxt.info.valid_sections |= cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID); ctxt.info.sec_flags |= - (I40E_AQ_VSI_SEC_FLAG_ENABLE_VLAN_CHK | - I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK); + I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK; } /* Setup the VSI tx/rx queue map for TC0 only for now */ i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, true); @@ -10001,6 +13934,10 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) vsi->info.valid_sections = 0; vsi->seid = ctxt.seid; vsi->id = ctxt.vsi_number; + val = rd32(&pf->hw, 0x208800 + (4*(vsi->id))); + if (!(val & 0x1)) /* MACVSIPRUNEENABLE = 1*/ + dev_warn(&vsi->back->pdev->dev, + "Note: VSI source pruning is not being set correctly by FW\n"); } vsi->active_filters = 0; @@ -10015,7 +13952,7 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) if (f_count) { vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; - pf->flags |= I40E_FLAG_FILTER_SYNC; + set_bit(__I40E_MACVLAN_SYNC_PENDING, pf->state); } /* Update VSI BW information */ @@ -10062,6 +13999,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi) return -ENODEV; } + set_bit(__I40E_VSI_RELEASING, vsi->state); uplink_seid = vsi->uplink_seid; if (vsi->type != I40E_VSI_SRIOV) { if (vsi->netdev_registered) { @@ -10166,6 +14104,7 @@ static int i40e_vsi_setup_vectors(struct i40e_vsi *vsi) goto vector_setup_out; } +#if !defined(I40E_LEGACY_INTERRUPT) && !defined(I40E_MSI_INTERRUPT) /* In Legacy mode, we do not have to get any other vector since we * piggyback on the misc/ICR0 for queue interrupts. */ @@ -10183,6 +14122,7 @@ static int i40e_vsi_setup_vectors(struct i40e_vsi *vsi) goto vector_setup_out; } +#endif vector_setup_out: return ret; } @@ -10280,7 +14220,6 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, { struct i40e_vsi *vsi = NULL; struct i40e_veb *veb = NULL; - u16 alloc_queue_pairs; int ret, i; int v_idx; @@ -10330,6 +14269,7 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, "New VSI creation error, uplink seid of LAN VSI expected.\n"); return NULL; } +#ifdef HAVE_BRIDGE_ATTRIBS /* We come up by default in VEPA mode if SRIOV is not * already enabled, in which case we can't force VEPA * mode. @@ -10338,6 +14278,7 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, veb->bridge_mode = BRIDGE_MODE_VEPA; pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED; } +#endif i40e_config_bridge_mode(veb); } for (i = 0; i < I40E_MAX_VEB && !veb; i++) { @@ -10368,14 +14309,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, else if (type == I40E_VSI_SRIOV) vsi->vf_id = param1; /* assign it some queues */ - alloc_queue_pairs = vsi->alloc_queue_pairs * - (i40e_enabled_xdp_vsi(vsi) ? 2 : 1); - - ret = i40e_get_lump(pf, pf->qp_pile, alloc_queue_pairs, vsi->idx); + ret = i40e_get_lump(pf, pf->qp_pile, vsi->alloc_queue_pairs, + vsi->idx); if (ret < 0) { dev_info(&pf->pdev->dev, "failed to get tracking for %d queues for VSI %d err=%d\n", - alloc_queue_pairs, vsi->seid, ret); + vsi->alloc_queue_pairs, vsi->seid, ret); goto err_vsi; } vsi->base_queue = ret; @@ -10398,10 +14337,14 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, goto err_netdev; vsi->netdev_registered = true; netif_carrier_off(vsi->netdev); -#ifdef CONFIG_I40E_DCB + /* make sure transmit queues start off as stopped */ + netif_tx_stop_all_queues(vsi->netdev); +#ifdef CONFIG_DCB +#ifdef HAVE_DCBNL_IEEE /* Setup DCB netlink interface */ i40e_dcbnl_setup(vsi); -#endif /* CONFIG_I40E_DCB */ +#endif /* HAVE_DCBNL_IEEE */ +#endif /* CONFIG_DCB */ /* fall through */ case I40E_VSI_FDIR: @@ -10484,16 +14427,16 @@ static int i40e_veb_get_bw_info(struct i40e_veb *veb) goto out; } - veb->bw_limit = le16_to_cpu(ets_data.port_bw_limit); + veb->bw_limit = LE16_TO_CPU(ets_data.port_bw_limit); veb->bw_max_quanta = ets_data.tc_bw_max; veb->is_abs_credits = bw_data.absolute_credits_enable; veb->enabled_tc = ets_data.tc_valid_bits; - tc_bw_max = le16_to_cpu(bw_data.tc_bw_max[0]) | - (le16_to_cpu(bw_data.tc_bw_max[1]) << 16); + tc_bw_max = LE16_TO_CPU(bw_data.tc_bw_max[0]) | + (LE16_TO_CPU(bw_data.tc_bw_max[1]) << 16); for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { veb->bw_tc_share_credits[i] = bw_data.tc_bw_share_credits[i]; veb->bw_tc_limit_credits[i] = - le16_to_cpu(bw_data.tc_bw_limits[i]); + LE16_TO_CPU(bw_data.tc_bw_limits[i]); veb->bw_tc_max_quanta[i] = ((tc_bw_max >> (i*4)) & 0x7); } @@ -10745,7 +14688,7 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags, for (vsi_idx = 0; vsi_idx < pf->num_alloc_vsi; vsi_idx++) if (pf->vsi[vsi_idx] && pf->vsi[vsi_idx]->seid == vsi_seid) break; - if (vsi_idx >= pf->num_alloc_vsi && vsi_seid != 0) { + if (vsi_idx == pf->num_alloc_vsi && vsi_seid != 0) { dev_info(&pf->pdev->dev, "vsi seid %d not found\n", vsi_seid); return NULL; @@ -10822,7 +14765,7 @@ static void i40e_setup_pf_switch_element(struct i40e_pf *pf, /* Main VEB? */ if (uplink_seid != pf->mac_seid) break; - if (pf->lan_veb == I40E_NO_VEB) { + if (pf->lan_veb >= I40E_MAX_VEB) { int v; /* find existing or else empty VEB */ @@ -10832,13 +14775,15 @@ static void i40e_setup_pf_switch_element(struct i40e_pf *pf, break; } } - if (pf->lan_veb == I40E_NO_VEB) { + if (pf->lan_veb >= I40E_MAX_VEB) { v = i40e_veb_mem_alloc(pf); if (v < 0) break; pf->lan_veb = v; } } + if (pf->lan_veb >= I40E_MAX_VEB) + break; pf->veb[pf->lan_veb]->seid = seid; pf->veb[pf->lan_veb]->uplink_seid = pf->mac_seid; @@ -10962,14 +14907,16 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) */ if ((pf->hw.pf_id == 0) && - !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) + !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) { flags = I40E_AQ_SET_SWITCH_CFG_PROMISC; + pf->last_sw_conf_flags = flags; + } if (pf->hw.pf_id == 0) { u16 valid_flags; valid_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC; - ret = i40e_aq_set_switch_config(&pf->hw, flags, valid_flags, + ret = i40e_aq_set_switch_config(&pf->hw, flags, valid_flags, 0, NULL); if (ret && pf->hw.aq.asq_last_status != I40E_AQ_RC_ESRCH) { dev_info(&pf->pdev->dev, @@ -10979,6 +14926,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) pf->hw.aq.asq_last_status)); /* not a fatal problem, just keep going */ } + pf->last_sw_conf_valid_flags = valid_flags; } /* first time setup */ @@ -10989,7 +14937,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) /* Set up the PF VSI associated with the PF's main VSI * that is already in the HW switch */ - if (pf->lan_veb != I40E_NO_VEB && pf->veb[pf->lan_veb]) + if (pf->lan_veb < I40E_MAX_VEB && pf->veb[pf->lan_veb]) uplink_seid = pf->veb[pf->lan_veb]->seid; else uplink_seid = pf->mac_seid; @@ -10999,6 +14947,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) vsi = i40e_vsi_reinit_setup(pf->vsi[pf->lan_vsi]); if (!vsi) { dev_info(&pf->pdev->dev, "setup of MAIN VSI failed\n"); + i40e_cloud_filter_exit(pf); i40e_fdir_teardown(pf); return -EAGAIN; } @@ -11035,11 +14984,17 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) pf->fc_autoneg_status = ((pf->hw.phy.link_info.an_info & I40E_AQ_AN_COMPLETED) ? true : false); +#ifdef HAVE_PTP_1588_CLOCK i40e_ptp_init(pf); +#endif /* HAVE_PTP_1588_CLOCK */ +#if defined(HAVE_VXLAN_RX_OFFLOAD) || defined(HAVE_UDP_ENC_RX_OFFLOAD) +#if defined(HAVE_UDP_ENC_TUNNEL) || defined(HAVE_UDP_ENC_RX_OFFLOAD) /* repopulate tunnel port filters */ i40e_sync_udp_filters(pf); +#endif /* HAVE_UDP_ENC_TUNNEL || HAVE_UDP_ENC_RX_OFFLOAD */ +#endif /* HAVE_VXLAN_RX_OFFLOAD || HAVE_UDP_ENC_RX_OFFLOAD */ return ret; } @@ -11074,6 +15029,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) I40E_FLAG_DCB_ENABLED | I40E_FLAG_SRIOV_ENABLED | I40E_FLAG_VMDQ_ENABLED); + pf->flags |= I40E_FLAG_FD_SB_INACTIVE; } else if (!(pf->flags & (I40E_FLAG_RSS_ENABLED | I40E_FLAG_FD_SB_ENABLED | I40E_FLAG_FD_ATR_ENABLED | @@ -11088,8 +15044,11 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) I40E_FLAG_FD_ATR_ENABLED | I40E_FLAG_DCB_ENABLED | I40E_FLAG_VMDQ_ENABLED); + pf->flags |= I40E_FLAG_FD_SB_INACTIVE; } else { /* Not enough queues for all TCs */ + bool is_max_n_of_queues_required; + if ((pf->flags & I40E_FLAG_DCB_CAPABLE) && (queues_left < I40E_MAX_TRAFFIC_CLASS)) { pf->flags &= ~(I40E_FLAG_DCB_CAPABLE | @@ -11098,8 +15057,21 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) } pf->num_lan_qps = max_t(int, pf->rss_size_max, num_online_cpus()); - pf->num_lan_qps = min_t(int, pf->num_lan_qps, - pf->hw.func_caps.num_tx_qp); + is_max_n_of_queues_required = pf->hw.func_caps.num_tx_qp < + pf->num_req_vfs * pf->num_vf_qps + pf->num_vf_qps; + if (is_max_n_of_queues_required) + dev_warn(&pf->pdev->dev, "not enough %u queues for PF and %u VFs. Using maximum available queues for PF.\n", + pf->hw.func_caps.num_tx_qp, pf->num_req_vfs); + if (pf->hw.func_caps.npar_enable || + is_max_n_of_queues_required) + pf->num_lan_qps = min_t + (int, pf->num_lan_qps, + pf->hw.func_caps.num_tx_qp); + else + pf->num_lan_qps = min_t + (int, pf->num_lan_qps, + pf->hw.func_caps.num_tx_qp - + pf->num_req_vfs * pf->num_vf_qps); queues_left -= pf->num_lan_qps; } @@ -11109,6 +15081,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) queues_left -= 1; /* save 1 queue for FD */ } else { pf->flags &= ~I40E_FLAG_FD_SB_ENABLED; + pf->flags |= I40E_FLAG_FD_SB_INACTIVE; dev_info(&pf->pdev->dev, "not enough queues for Flow Director. Flow Director feature is disabled\n"); } } @@ -11195,36 +15168,271 @@ static void i40e_print_features(struct i40e_pf *pf) i += snprintf(&buf[i], REMAIN(i), " FD_SB"); i += snprintf(&buf[i], REMAIN(i), " NTUPLE"); } + i += snprintf(&buf[i], REMAIN(i), " CloudF"); if (pf->flags & I40E_FLAG_DCB_CAPABLE) i += snprintf(&buf[i], REMAIN(i), " DCB"); +#if IS_ENABLED(CONFIG_VXLAN) i += snprintf(&buf[i], REMAIN(i), " VxLAN"); +#endif +#if IS_ENABLED(CONFIG_GENEVE) i += snprintf(&buf[i], REMAIN(i), " Geneve"); +#endif +#ifdef HAVE_GRE_ENCAP_OFFLOAD + i += snprintf(&buf[i], REMAIN(i), " NVGRE"); +#endif +#ifdef HAVE_PTP_1588_CLOCK if (pf->flags & I40E_FLAG_PTP) i += snprintf(&buf[i], REMAIN(i), " PTP"); +#endif if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED) i += snprintf(&buf[i], REMAIN(i), " VEB"); else i += snprintf(&buf[i], REMAIN(i), " VEPA"); - dev_info(&pf->pdev->dev, "%s\n", buf); - kfree(buf); - WARN_ON(i > INFO_STRING_LEN); + dev_info(&pf->pdev->dev, "%s\n", buf); + kfree(buf); + WARN_ON(i > INFO_STRING_LEN); +} + +/** + * i40e_get_platform_mac_addr - get platform-specific MAC address + * @pdev: PCI device information struct + * @pf: board private structure + * + * Look up the MAC address for the device. First we'll try + * eth_platform_get_mac_address, which will check Open Firmware, or arch + * specific fallback. Otherwise, we'll default to the stored value in + * firmware. + **/ +static void i40e_get_platform_mac_addr(struct pci_dev *pdev, struct i40e_pf *pf) +{ + if (eth_platform_get_mac_address(&pdev->dev, pf->hw.mac.addr)) + i40e_get_mac_addr(&pf->hw, pf->hw.mac.addr); +} + +/** + * i40e_check_recovery_mode - check if we are running transition firmware + * @pf: board private structure + * + * Check registers indicating the firmware runs in recovery mode. Sets the + * appropriate driver state. + * + * Returns true if the recovery mode was detected, false otherwise + **/ +static bool i40e_check_recovery_mode(struct i40e_pf *pf) +{ + u32 val = rd32(&pf->hw, I40E_GL_FWSTS); + + if (val & I40E_GL_FWSTS_FWS1B_MASK) { + dev_crit(&pf->pdev->dev, "Firmware recovery mode detected. Limiting functionality.\n"); + dev_crit(&pf->pdev->dev, "Refer to the Intel(R) Ethernet Adapters and Devices User Guide for details on firmware recovery mode.\n"); + set_bit(__I40E_RECOVERY_MODE, pf->state); + + return true; + } + if (test_bit(__I40E_RECOVERY_MODE, pf->state)) + dev_info(&pf->pdev->dev, "Please do Power-On Reset to initialize adapter in normal mode with full functionality.\n"); + + return false; +} + +/** + * i40e_pf_loop_reset - perform reset in a loop. + * @pf: board private structure + * + * This function is useful when a NIC is about to enter recovery mode. + * When a NIC's internal data structures are corrupted the NIC's + * firmware is going to enter recovery mode. + * Right after a POR it takes about 7 minutes for firmware to enter + * recovery mode. Until that time a NIC is in some kind of intermediate + * state. After that time period the NIC almost surely enters + * recovery mode. The only way for a driver to detect intermediate + * state is to issue a series of pf-resets and check a return value. + * If a PF reset returns success then the firmware could be in recovery + * mode so the caller of this code needs to check for recovery mode + * if this function returns success. There is a little chance that + * firmware will hang in intermediate state forever. + * Since waiting 7 minutes is quite a lot of time this function waits + * 10 seconds and then gives up by returning an error. + * + * Return 0 on success, negative on failure. + **/ +static i40e_status i40e_pf_loop_reset(struct i40e_pf * const pf) +{ + /* wait max 10 seconds for PF reset to succeed */ + const unsigned long time_end = jiffies + 10 * HZ; + + struct i40e_hw *hw = &pf->hw; + i40e_status ret; + + ret = i40e_pf_reset(hw); + while (ret != I40E_SUCCESS && time_before(jiffies, time_end)) { + usleep_range(10000, 20000); + ret = i40e_pf_reset(hw); + } + + if (ret == I40E_SUCCESS) + pf->pfr_count++; + else + dev_info(&pf->pdev->dev, "PF reset failed: %d\n", ret); + + return ret; +} + +/** + * i40e_check_fw_empr - check if FW issued unexpected EMP Reset + * @pf: board private structure + * + * Check FW registers to determine if FW issued unexpected EMP Reset. + * Every time when unexpected EMP Reset occurs the FW increments + * a counter of unexpected EMP Resets. When the counter reaches 10 + * the FW should enter the Recovery mode + * + * Returns true if FW issued unexpected EMP Reset + **/ +static inline bool i40e_check_fw_empr(struct i40e_pf * const pf) +{ + const u32 fw_sts = rd32(&pf->hw, I40E_GL_FWSTS) & + I40E_GL_FWSTS_FWS1B_MASK; + const bool is_empr = (fw_sts > I40E_GL_FWSTS_FWS1B_EMPR_0) && + (fw_sts <= I40E_GL_FWSTS_FWS1B_EMPR_10); + + return is_empr; +} + +/** + * i40e_handle_resets - handle EMP resets and PF resets + * @pf: board private structure + * + * Handle both EMP resets and PF resets and conclude whether there are + * any issues regarding these resets. If there are any issues then + * generate log entry. + * + * Return 0 if NIC is healthy or negative value when there are issues + * with resets + **/ +static inline i40e_status i40e_handle_resets(struct i40e_pf * const pf) +{ + const i40e_status pfr = i40e_pf_loop_reset(pf); + const bool is_empr = i40e_check_fw_empr(pf); + + if (is_empr || pfr != I40E_SUCCESS) + dev_crit(&pf->pdev->dev, "Entering recovery mode due to repeated FW resets. This may take several minutes. Refer to the Intel(R) Ethernet Adapters and Devices User Guide.\n"); + + return is_empr ? I40E_ERR_RESET_FAILED : pfr; +} + +/** + * i40e_init_recovery_mode - initialize subsystems needed in recovery mode + * @pf: board private structure + * @hw: ptr to the hardware info + * + * This function does a minimal setup of all subsystems needed for running + * recovery mode. + * + * Returns 0 on success, negative on failure + **/ +static int i40e_init_recovery_mode(struct i40e_pf *pf, struct i40e_hw *hw) +{ + struct i40e_vsi *vsi; + int err; + int v_idx; + +#ifdef HAVE_PCI_ERS + pci_save_state(pf->pdev); +#endif + + /* set up periodic task facility */ + timer_setup(&pf->service_timer, i40e_service_timer, 0); + pf->service_timer_period = HZ; + + INIT_WORK(&pf->service_task, i40e_service_task); + clear_bit(__I40E_SERVICE_SCHED, pf->state); + + err = i40e_init_interrupt_scheme(pf); + if (err) + goto err_switch_setup; + + /* The number of VSIs reported by the FW is the minimum guaranteed + * to us; HW supports far more and we share the remaining pool with + * the other PFs. We allocate space for more than the guarantee with + * the understanding that we might not get them all later. + */ + if (pf->hw.func_caps.num_vsis < I40E_MIN_VSI_ALLOC) + pf->num_alloc_vsi = I40E_MIN_VSI_ALLOC; + else + pf->num_alloc_vsi = pf->hw.func_caps.num_vsis; + + /* Set up the vsi struct and our local tracking of the MAIN PF vsi. */ + pf->vsi = kcalloc(pf->num_alloc_vsi, sizeof(struct i40e_vsi *), + GFP_KERNEL); + if (!pf->vsi) { + err = -ENOMEM; + goto err_switch_setup; + } + + /* We allocate one VSI which is needed as absolute minimum + * in order to register the netdev + */ + v_idx = i40e_vsi_mem_alloc(pf, I40E_VSI_MAIN); + if (v_idx < 0) + goto err_switch_setup; + pf->lan_vsi = v_idx; + vsi = pf->vsi[v_idx]; + if (!vsi) + goto err_switch_setup; + vsi->alloc_queue_pairs = 1; + err = i40e_config_netdev(vsi); + if (err) + goto err_switch_setup; + err = register_netdev(vsi->netdev); + if (err) + goto err_switch_setup; + vsi->netdev_registered = true; + i40e_dbg_pf_init(pf); + + err = i40e_setup_misc_vector_for_recovery_mode(pf); + if (err) + goto err_switch_setup; + + /* tell the firmware that we're starting */ + i40e_send_version(pf); + + /* since everything's happy, start the service_task timer */ + mod_timer(&pf->service_timer, + round_jiffies(jiffies + pf->service_timer_period)); + + return 0; + +err_switch_setup: + i40e_reset_interrupt_capability(pf); + del_timer_sync(&pf->service_timer); + dev_warn(&pf->pdev->dev, "previous errors forcing module to load in debug mode\n"); + i40e_dbg_pf_init(pf); + set_bit(__I40E_DEBUG_MODE, pf->state); + return 0; } /** - * i40e_get_platform_mac_addr - get platform-specific MAC address - * @pdev: PCI device information struct - * @pf: board private structure - * - * Look up the MAC address for the device. First we'll try - * eth_platform_get_mac_address, which will check Open Firmware, or arch - * specific fallback. Otherwise, we'll default to the stored value in - * firmware. + * i40e_set_fec_in_flags - helper function for setting FEC options in flags + * @fec_cfg: FEC option to set in flags + * @flags: ptr to flags in which we set FEC option **/ -static void i40e_get_platform_mac_addr(struct pci_dev *pdev, struct i40e_pf *pf) +void i40e_set_fec_in_flags(u8 fec_cfg, u64 *flags) { - if (eth_platform_get_mac_address(&pdev->dev, pf->hw.mac.addr)) - i40e_get_mac_addr(&pf->hw, pf->hw.mac.addr); + if (fec_cfg & I40E_AQ_SET_FEC_AUTO) + *flags |= I40E_FLAG_RS_FEC | I40E_FLAG_BASE_R_FEC; + else if ((fec_cfg & I40E_AQ_SET_FEC_REQUEST_RS) || + (fec_cfg & I40E_AQ_SET_FEC_ABILITY_RS)) { + *flags |= I40E_FLAG_RS_FEC; + *flags &= ~I40E_FLAG_BASE_R_FEC; + } else if ((fec_cfg & I40E_AQ_SET_FEC_REQUEST_KR) || + (fec_cfg & I40E_AQ_SET_FEC_ABILITY_KR)) { + *flags |= I40E_FLAG_BASE_R_FEC; + *flags &= ~I40E_FLAG_RS_FEC; + } + if (fec_cfg == 0) + *flags &= ~(I40E_FLAG_RS_FEC | I40E_FLAG_BASE_R_FEC); } /** @@ -11238,15 +15446,24 @@ static void i40e_get_platform_mac_addr(struct pci_dev *pdev, struct i40e_pf *pf) * * Returns 0 on success, negative on failure **/ +#ifdef HAVE_CONFIG_HOTPLUG +static int __devinit i40e_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +#else static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +#endif { struct i40e_aq_get_phy_abilities_resp abilities; +#ifdef CONFIG_DCB + enum i40e_get_fw_lldp_status_resp lldp_status; + i40e_status status; +#endif /* CONFIG_DCB */ struct i40e_pf *pf; struct i40e_hw *hw; static u16 pfs_found; u16 wol_nvm_bits; u16 link_status; - int err; + int err = 0; u32 val; u32 i; u8 set_fc_aq_fail; @@ -11270,7 +15487,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = pci_request_mem_regions(pdev, i40e_driver_name); if (err) { dev_info(&pdev->dev, - "pci_request_selected_regions failed %d\n", err); + "pci_request_mem_regions failed %d\n", err); goto err_pci_reg; } @@ -11289,6 +15506,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } pf->next_vsi = 0; pf->pdev = pdev; + pci_set_drvdata(pdev, pf); set_bit(__I40E_DOWN, pf->state); hw = &pf->hw; @@ -11296,7 +15514,17 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pf->ioremap_len = min_t(int, pci_resource_len(pdev, 0), I40E_MAX_CSR_SPACE); - + /* We believe that the highest register to read is + * I40E_GLGEN_STAT_CLEAR, so we check if the BAR size + * is not less than that before mapping to prevent a + * kernel panic. + */ + if (pf->ioremap_len < I40E_GLGEN_STAT_CLEAR) { + dev_err(&pdev->dev, "Cannot map registers, bar size 0x%X too small, aborting\n", + pf->ioremap_len); + err = -ENOMEM; + goto err_ioremap; + } hw->hw_addr = ioremap(pci_resource_start(pdev, 0), pf->ioremap_len); if (!hw->hw_addr) { err = -EIO; @@ -11315,41 +15543,42 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hw->bus.bus_id = pdev->bus->number; pf->instance = pfs_found; + /* Select something other than the 802.1ad ethertype for the + * switch to use internally and drop on ingress. + */ + hw->switch_tag = 0xffff; + hw->first_tag = ETH_P_8021AD; + hw->second_tag = ETH_P_8021Q; + INIT_LIST_HEAD(&pf->l3_flex_pit_list); INIT_LIST_HEAD(&pf->l4_flex_pit_list); + INIT_LIST_HEAD(&pf->ddp_old_prof); - /* set up the locks for the AQ, do this only once in probe + /* set up the spinlocks for the AQ, do this only once in probe * and destroy them only once in remove */ - mutex_init(&hw->aq.asq_mutex); - mutex_init(&hw->aq.arq_mutex); - - pf->msg_enable = netif_msg_init(debug, - NETIF_MSG_DRV | - NETIF_MSG_PROBE | - NETIF_MSG_LINK); - if (debug < -1) - pf->hw.debug_mask = debug; - - /* do a special CORER for clearing PXE mode once at init */ - if (hw->revision_id == 0 && - (rd32(hw, I40E_GLLAN_RCTL_0) & I40E_GLLAN_RCTL_0_PXE_MODE_MASK)) { - wr32(hw, I40E_GLGEN_RTRIG, I40E_GLGEN_RTRIG_CORER_MASK); - i40e_flush(hw); - msleep(200); - pf->corer_count++; + i40e_init_spinlock_d(&hw->aq.asq_spinlock); + i40e_init_spinlock_d(&hw->aq.arq_spinlock); - i40e_clear_pxe_mode(hw); - } + if (debug != -1) + pf->msg_enable = pf->hw.debug_mask = debug; /* Reset here to make sure all is clean and to define PF 'n' */ + /* have to do the PF reset first to "graceful abort" all queues */ i40e_clear_hw(hw); - err = i40e_pf_reset(hw); + + err = i40e_set_mac_type(hw); if (err) { - dev_info(&pdev->dev, "Initial pf_reset failed: %d\n", err); + dev_warn(&pdev->dev, "unidentified MAC or BLANK NVM: %d\n", + err); goto err_pf_reset; } - pf->pfr_count++; + + err = i40e_handle_resets(pf); + if (err) + goto err_pf_reset; + + i40e_check_recovery_mode(pf); hw->aq.num_arq_entries = I40E_AQ_LEN; hw->aq.num_asq_entries = I40E_AQ_LEN; @@ -11375,7 +15604,11 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) { if (err == I40E_ERR_FIRMWARE_API_VERSION) dev_info(&pdev->dev, - "The driver for the device stopped because the NVM image is newer than expected. You must install the most recent version of the network driver.\n"); + "The driver for the device stopped because the NVM image v%u.%u is newer than expected v%u.%u. You must install the most recent version of the network driver.\n", + hw->aq.api_maj_ver, + hw->aq.api_min_ver, + I40E_FW_API_VERSION_MAJOR, + I40E_FW_MINOR_VERSION(hw)); else dev_info(&pdev->dev, "The driver for the device stopped because the device firmware failed to init. Try updating your NVM image.\n"); @@ -11391,13 +15624,20 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) i40e_nvm_version_str(hw)); if (hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR && - hw->aq.api_min_ver > I40E_FW_API_VERSION_MINOR) + hw->aq.api_min_ver > I40E_FW_MINOR_VERSION(hw)) dev_info(&pdev->dev, - "The driver for the device detected a newer version of the NVM image than expected. Please install the most recent version of the network driver.\n"); - else if (hw->aq.api_maj_ver < I40E_FW_API_VERSION_MAJOR || - hw->aq.api_min_ver < (I40E_FW_API_VERSION_MINOR - 1)) + "The driver for the device detected a newer version of the NVM image v%u.%u than expected v%u.%u. Please install the most recent version of the network driver.\n", + hw->aq.api_maj_ver, + hw->aq.api_min_ver, + I40E_FW_API_VERSION_MAJOR, + I40E_FW_MINOR_VERSION(hw)); + else if (hw->aq.api_maj_ver == 1 && hw->aq.api_min_ver < 4) dev_info(&pdev->dev, - "The driver for the device detected an older version of the NVM image than expected. Please update the NVM image.\n"); + "The driver for the device detected an older version of the NVM image v%u.%u than expected v%u.%u. Please update the NVM image.\n", + hw->aq.api_maj_ver, + hw->aq.api_min_ver, + I40E_FW_API_VERSION_MAJOR, + I40E_FW_MINOR_VERSION(hw)); i40e_verify_eeprom(pf); @@ -11406,7 +15646,8 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev_warn(&pdev->dev, "This device is a pre-production adapter/LOM. Please be aware there may be issues with your hardware. If you are experiencing problems please contact your Intel or hardware representative who provided you with this hardware.\n"); i40e_clear_pxe_mode(hw); - err = i40e_get_capabilities(pf); + + err = i40e_get_capabilities(pf, i40e_aqc_opc_list_func_capabilities); if (err) goto err_adminq_setup; @@ -11416,6 +15657,9 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_sw_init; } + if (test_bit(__I40E_RECOVERY_MODE, pf->state)) + return i40e_init_recovery_mode(pf, hw); + err = i40e_init_lan_hmc(hw, hw->func_caps.num_tx_qp, hw->func_caps.num_rx_qp, 0, 0); if (err) { @@ -11436,7 +15680,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) */ if (pf->hw_features & I40E_HW_STOP_FW_LLDP) { dev_info(&pdev->dev, "Stopping firmware LLDP agent.\n"); - i40e_aq_stop_lldp(hw, true, NULL); + i40e_aq_stop_lldp(hw, true, false, NULL); } /* allow a platform config to override the HW addr */ @@ -11452,20 +15696,37 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) i40e_get_port_mac_addr(hw, hw->mac.port_addr); if (is_valid_ether_addr(hw->mac.port_addr)) pf->hw_features |= I40E_HW_PORT_ID_VALID; +#ifdef HAVE_PTP_1588_CLOCK + i40e_ptp_alloc_pins(pf); +#endif /* HAVE_PTP_1588_CLOCK */ - pci_set_drvdata(pdev, pf); +#ifdef HAVE_PCI_ERS pci_save_state(pdev); -#ifdef CONFIG_I40E_DCB +#endif +#ifdef CONFIG_DCB + status = i40e_get_fw_lldp_status(&pf->hw, &lldp_status); + (status == I40E_SUCCESS && + lldp_status == I40E_GET_FW_LLDP_STATUS_ENABLED) ? + (pf->flags &= ~I40E_FLAG_DISABLE_FW_LLDP) : + (pf->flags |= I40E_FLAG_DISABLE_FW_LLDP); + dev_info(&pdev->dev, + (pf->flags & I40E_FLAG_DISABLE_FW_LLDP) ? + "FW LLDP is disabled\n" : + "FW LLDP is enabled\n"); + + /* Enable FW to write default DCB config on link-up */ + i40e_aq_set_dcb_parameters(hw, true, NULL); + err = i40e_init_pf_dcb(pf); if (err) { dev_info(&pdev->dev, "DCB init failed %d, disabled\n", err); pf->flags &= ~(I40E_FLAG_DCB_CAPABLE | I40E_FLAG_DCB_ENABLED); /* Continue without DCB enabled */ } -#endif /* CONFIG_I40E_DCB */ +#endif /* CONFIG_DCB */ /* set up periodic task facility */ - setup_timer(&pf->service_timer, i40e_service_timer, (unsigned long)pf); + timer_setup(&pf->service_timer, i40e_service_timer, 0); pf->service_timer_period = HZ; INIT_WORK(&pf->service_task, i40e_service_task); @@ -11473,7 +15734,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* NVM bit on means WoL disabled for the port */ i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits); - if (BIT (hw->port) & wol_nvm_bits || hw->partition_id != 1) + if (BIT(hw->port) & wol_nvm_bits || hw->partition_id != 1) pf->wol_en = false; else pf->wol_en = true; @@ -11510,6 +15771,10 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) !test_bit(__I40E_BAD_EEPROM, pf->state)) { if (pci_num_vf(pdev)) pf->flags |= I40E_FLAG_VEB_MODE_ENABLED; +#if !defined(HAVE_SRIOV_CONFIGURE) && !defined(HAVE_RHEL6_SRIOV_CONFIGURE) + else if (pf->num_req_vfs) + pf->flags |= I40E_FLAG_VEB_MODE_ENABLED; +#endif } #endif err = i40e_setup_pf_switch(pf, false); @@ -11517,6 +15782,20 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev_info(&pdev->dev, "setup_pf_switch failed: %d\n", err); goto err_vsis; } + if (i40e_is_l4mode_enabled() && hw->pf_id == 0) { + u8 l4type = I40E_AQ_SET_SWITCH_L4_TYPE_BOTH; + + switch (l4mode) { + case L4_MODE_UDP: + l4type = I40E_AQ_SET_SWITCH_L4_TYPE_UDP; + break; + case L4_MODE_TCP: + l4type = I40E_AQ_SET_SWITCH_L4_TYPE_TCP; + break; + } + i40e_set_switch_mode(pf, l4type); + } + INIT_LIST_HEAD(&pf->vsi[pf->lan_vsi]->ch_list); /* Make sure flow control is set according to current settings */ err = i40e_set_fc(hw, &set_fc_aq_fail, true); @@ -11615,6 +15894,15 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev_info(&pdev->dev, "Error %d allocating resources for existing VFs\n", err); +#if !defined(HAVE_SRIOV_CONFIGURE) && !defined(HAVE_RHEL6_SRIOV_CONFIGURE) + } else if (pf->num_req_vfs) { + err = i40e_alloc_vfs(pf, pf->num_req_vfs); + if (err) { + pf->flags &= ~I40E_FLAG_SRIOV_ENABLED; + dev_info(&pdev->dev, + "failed to alloc vfs: %d\n", err); + } +#endif /* HAVE_SRIOV_CONFIGURE */ } } #endif /* CONFIG_PCI_IOV */ @@ -11631,6 +15919,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } + pfs_found++; i40e_dbg_pf_init(pf); /* tell the firmware that we're starting */ @@ -11647,7 +15936,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev_info(&pdev->dev, "Failed to add PF to client API service list: %d\n", err); } - #define PCI_SPEED_SIZE 8 #define PCI_WIDTH_SIZE 8 /* Devices on the IOSF bus do not have this information @@ -11668,23 +15956,23 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) switch (hw->bus.speed) { case i40e_bus_speed_8000: - strncpy(speed, "8.0", PCI_SPEED_SIZE); break; + strlcpy(speed, "8.0", PCI_SPEED_SIZE); break; case i40e_bus_speed_5000: - strncpy(speed, "5.0", PCI_SPEED_SIZE); break; + strlcpy(speed, "5.0", PCI_SPEED_SIZE); break; case i40e_bus_speed_2500: - strncpy(speed, "2.5", PCI_SPEED_SIZE); break; + strlcpy(speed, "2.5", PCI_SPEED_SIZE); break; default: break; } switch (hw->bus.width) { case i40e_bus_width_pcie_x8: - strncpy(width, "8", PCI_WIDTH_SIZE); break; + strlcpy(width, "8", PCI_WIDTH_SIZE); break; case i40e_bus_width_pcie_x4: - strncpy(width, "4", PCI_WIDTH_SIZE); break; + strlcpy(width, "4", PCI_WIDTH_SIZE); break; case i40e_bus_width_pcie_x2: - strncpy(width, "2", PCI_WIDTH_SIZE); break; + strlcpy(width, "2", PCI_WIDTH_SIZE); break; case i40e_bus_width_pcie_x1: - strncpy(width, "1", PCI_WIDTH_SIZE); break; + strlcpy(width, "1", PCI_WIDTH_SIZE); break; default: break; } @@ -11707,6 +15995,9 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); pf->hw.phy.link_info.requested_speeds = abilities.link_speed; + /* set the FEC config due to the board capabilities */ + i40e_set_fec_in_flags(abilities.fec_cfg_curr_mod_ext_info, &pf->flags); + /* get the supported phy types from the fw */ err = i40e_aq_get_phy_capabilities(hw, false, true, &abilities, NULL); if (err) @@ -11724,7 +16015,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pf->main_vsi_seid); if ((pf->hw.device_id == I40E_DEV_ID_10G_BASE_T) || - (pf->hw.device_id == I40E_DEV_ID_10G_BASE_T4)) + (pf->hw.device_id == I40E_DEV_ID_10G_BASE_T4)) pf->hw_features |= I40E_HW_PHY_CONTROLS_LEDS; if (pf->hw.device_id == I40E_DEV_ID_SFP_I_X722) pf->hw_features |= I40E_HW_HAVE_CRT_RETIMER; @@ -11770,7 +16061,11 @@ err_dma: * Hot-Plug event, or because the driver is going to be removed from * memory. **/ +#ifdef HAVE_CONFIG_HOTPLUG +static void __devexit i40e_remove(struct pci_dev *pdev) +#else static void i40e_remove(struct pci_dev *pdev) +#endif { struct i40e_pf *pf = pci_get_drvdata(pdev); struct i40e_hw *hw = &pf->hw; @@ -11778,25 +16073,40 @@ static void i40e_remove(struct pci_dev *pdev) int i; i40e_dbg_pf_exit(pf); - +#ifdef HAVE_PTP_1588_CLOCK i40e_ptp_stop(pf); /* Disable RSS in hw */ i40e_write_rx_ctl(hw, I40E_PFQF_HENA(0), 0); i40e_write_rx_ctl(hw, I40E_PFQF_HENA(1), 0); +#endif /* HAVE_PTP_1588_CLOCK */ /* no more scheduling of any task */ set_bit(__I40E_SUSPENDED, pf->state); set_bit(__I40E_DOWN, pf->state); - if (pf->service_timer.data) + if (pf->service_timer.function) del_timer_sync(&pf->service_timer); if (pf->service_task.func) cancel_work_sync(&pf->service_task); - /* Client close must be called explicitly here because the timer * has been stopped. */ i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], false); + if (test_bit(__I40E_DEBUG_MODE, pf->state)) + goto debug_mode_clear; + if (test_bit(__I40E_RECOVERY_MODE, pf->state)) { + struct i40e_vsi *vsi = pf->vsi[0]; + + /* We know that we have allocated only one vsi for this PF, + * it was just for registering netdevice, so the interface + * could be visible in the 'ifconfig' output + */ + unregister_netdev(vsi->netdev); + free_netdev(vsi->netdev); + vsi->netdev = NULL; + + goto unmap; + } if (pf->flags & I40E_FLAG_SRIOV_ENABLED) { i40e_free_vfs(pf); @@ -11823,6 +16133,8 @@ static void i40e_remove(struct pci_dev *pdev) if (pf->vsi[pf->lan_vsi]) i40e_vsi_release(pf->vsi[pf->lan_vsi]); + i40e_cloud_filter_exit(pf); + /* remove attached clients */ if (pf->flags & I40E_FLAG_IWARP_ENABLED) { ret_code = i40e_lan_del_device(pf); @@ -11840,22 +16152,37 @@ static void i40e_remove(struct pci_dev *pdev) ret_code); } - /* shutdown the adminq */ - i40e_shutdown_adminq(hw); - - /* destroy the locks only once, here */ - mutex_destroy(&hw->aq.arq_mutex); - mutex_destroy(&hw->aq.asq_mutex); +unmap: + /* Free MSI/legacy interrupt 0 when in recovery mode. + * This is normally done in i40e_vsi_free_irq on + * VSI close but since recovery mode doesn't allow to up + * an interface and we do not allocate all Rx/Tx resources + * for it we'll just do it here + */ + if (test_bit(__I40E_RECOVERY_MODE, pf->state) && + !(pf->flags & I40E_FLAG_MSIX_ENABLED)) + free_irq(pf->pdev->irq, pf); /* Clear all dynamic memory lists of rings, q_vectors, and VSIs */ + rtnl_lock(); i40e_clear_interrupt_scheme(pf); for (i = 0; i < pf->num_alloc_vsi; i++) { if (pf->vsi[i]) { - i40e_vsi_clear_rings(pf->vsi[i]); + if (!test_bit(__I40E_RECOVERY_MODE, pf->state)) + i40e_vsi_clear_rings(pf->vsi[i]); i40e_vsi_clear(pf->vsi[i]); pf->vsi[i] = NULL; } } + rtnl_unlock(); + +debug_mode_clear: + /* shutdown the adminq */ + i40e_shutdown_adminq(hw); + + /* destroy the locks only once, here */ + i40e_destroy_spinlock_d(&hw->aq.arq_spinlock); + i40e_destroy_spinlock_d(&hw->aq.asq_spinlock); for (i = 0; i < I40E_MAX_VEB; i++) { kfree(pf->veb[i]); @@ -11873,9 +16200,11 @@ static void i40e_remove(struct pci_dev *pdev) pci_disable_device(pdev); } +#ifdef HAVE_PCI_ERS /** * i40e_pci_error_detected - warning that something funky happened in PCI land * @pdev: PCI device information struct + * @error: the type of PCI error * * Called to warn that something happened and the error handling steps * are in progress. Allows the driver to quiesce things, be ready for @@ -11890,7 +16219,7 @@ static pci_ers_result_t i40e_pci_error_detected(struct pci_dev *pdev, if (!pf) { dev_info(&pdev->dev, - "Cannot recover - error happened during device probe\n"); + "Cannot recover -error happened during device probe\n"); return PCI_ERS_RESULT_DISCONNECT; } @@ -11947,6 +16276,49 @@ static pci_ers_result_t i40e_pci_error_slot_reset(struct pci_dev *pdev) return result; } +#if defined(HAVE_PCI_ERROR_HANDLER_RESET_PREPARE) || defined(HAVE_PCI_ERROR_HANDLER_RESET_NOTIFY) || defined(HAVE_RHEL7_PCI_RESET_NOTIFY) +/** + * i40e_pci_error_reset_prepare - prepare device driver for pci reset + * @pdev: PCI device information struct + */ +static void i40e_pci_error_reset_prepare(struct pci_dev *pdev) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + + i40e_prep_for_reset(pf, false); +} + +/** + * i40e_pci_error_reset_done - pci reset done, device driver reset can begin + * @pdev: PCI device information struct + */ +static void i40e_pci_error_reset_done(struct pci_dev *pdev) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + + i40e_reset_and_rebuild(pf, false, false); +} + +#endif +#if defined(HAVE_PCI_ERROR_HANDLER_RESET_NOTIFY) || defined(HAVE_RHEL7_PCI_RESET_NOTIFY) +/** + * i40e_pci_error_reset_notify - notify device driver of pci reset + * @pdev: PCI device information struct + * @prepare: true if device is about to be reset; false if reset attempt + * completed + * + * Called to perform pf reset when a pci function level reset is triggered + **/ +static void i40e_pci_error_reset_notify(struct pci_dev *pdev, bool prepare) +{ + dev_dbg(&pdev->dev, "%s\n", __func__); + if (prepare) + i40e_pci_error_reset_prepare(pdev); + else + i40e_pci_error_reset_done(pdev); +} + +#endif /** * i40e_pci_error_resume - restart operations after PCI error recovery * @pdev: PCI device information struct @@ -12012,6 +16384,11 @@ static void i40e_enable_mc_magic_wake(struct i40e_pf *pf) "Failed to enable Multicast Magic Packet wake up\n"); } +/* FW state indicating on X722 that we need to disable WoL to + * allow adapter to shutdown completely + */ +#define I40E_GL_FWSTS_FWS0B_STAGE_FW_UPDATE_POR_REQUIRED 0x0F + /** * i40e_shutdown - PCI callback for shutting down * @pdev: PCI device information struct @@ -12020,18 +16397,17 @@ static void i40e_shutdown(struct pci_dev *pdev) { struct i40e_pf *pf = pci_get_drvdata(pdev); struct i40e_hw *hw = &pf->hw; + u32 val = 0; set_bit(__I40E_SUSPENDED, pf->state); set_bit(__I40E_DOWN, pf->state); - rtnl_lock(); - i40e_prep_for_reset(pf, true); - rtnl_unlock(); - wr32(hw, I40E_PFPM_APM, (pf->wol_en ? I40E_PFPM_APM_APME_MASK : 0)); - wr32(hw, I40E_PFPM_WUFC, (pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0)); + if (test_bit(__I40E_DEBUG_MODE, pf->state)) + goto debug_mode; del_timer_sync(&pf->service_timer); cancel_work_sync(&pf->service_task); + i40e_cloud_filter_exit(pf); i40e_fdir_teardown(pf); /* Client close must be called explicitly here because the timer @@ -12039,55 +16415,186 @@ static void i40e_shutdown(struct pci_dev *pdev) */ i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], false); - if (pf->wol_en && (pf->hw_features & I40E_HW_WOL_MC_MAGIC_PKT_WAKE)) - i40e_enable_mc_magic_wake(pf); + val = rd32(hw, I40E_GL_FWSTS) & I40E_GL_FWSTS_FWS0B_MASK; - i40e_prep_for_reset(pf, false); + if (pf->hw.mac.type == I40E_MAC_X722) { + /* We check here if we need to disable the WoL to allow adapter + * to shutdown completely after a FW update + */ + if (val != I40E_GL_FWSTS_FWS0B_STAGE_FW_UPDATE_POR_REQUIRED && + pf->wol_en) { + if (pf->hw_features & I40E_HW_WOL_MC_MAGIC_PKT_WAKE) + i40e_enable_mc_magic_wake(pf); + + i40e_prep_for_reset(pf, false); + + wr32(hw, I40E_PFPM_APM, I40E_PFPM_APM_APME_MASK); + wr32(hw, I40E_PFPM_WUFC, I40E_PFPM_WUFC_MAG_MASK); + } else { + i40e_prep_for_reset(pf, false); + + wr32(hw, I40E_PFPM_APM, 0); + wr32(hw, I40E_PFPM_WUFC, 0); + } + } else { + i40e_prep_for_reset(pf, false); + + wr32(hw, I40E_PFPM_APM, + (pf->wol_en ? I40E_PFPM_APM_APME_MASK : 0)); + wr32(hw, I40E_PFPM_WUFC, + (pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0)); + } - wr32(hw, I40E_PFPM_APM, - (pf->wol_en ? I40E_PFPM_APM_APME_MASK : 0)); - wr32(hw, I40E_PFPM_WUFC, - (pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0)); + /* Free MSI/legacy interrupt 0 when in recovery mode. + * This is normally done in i40e_vsi_free_irq on + * VSI close but since recovery mode doesn't allow to up + * an interface and we do not allocate all Rx/Tx resources + * for it we'll just do it here + */ + if (test_bit(__I40E_RECOVERY_MODE, pf->state) && + !(pf->flags & I40E_FLAG_MSIX_ENABLED)) + free_irq(pf->pdev->irq, pf); + /* Since we're going to destroy queues during the + * i40e_clear_interrupt_scheme() we should hold the RTNL lock for this + * whole section + */ + rtnl_lock(); i40e_clear_interrupt_scheme(pf); + rtnl_unlock(); - if (system_state == SYSTEM_POWER_OFF) { +debug_mode: + + if (pf->hw.mac.type == I40E_MAC_X722 && + val == I40E_GL_FWSTS_FWS0B_STAGE_FW_UPDATE_POR_REQUIRED) { + pci_wake_from_d3(pdev, false); + device_set_wakeup_enable(&pdev->dev, false); + } else if (system_state == SYSTEM_POWER_OFF) { pci_wake_from_d3(pdev, pf->wol_en); - pci_set_power_state(pdev, PCI_D3hot); } + pci_set_power_state(pdev, PCI_D3hot); } #ifdef CONFIG_PM /** - * i40e_suspend - PCI callback for moving to D3 - * @pdev: PCI device information struct + * i40e_suspend - PM callback for moving to D3 + * @dev: generic device information structure **/ -static int i40e_suspend(struct pci_dev *pdev, pm_message_t state) +static int i40e_suspend(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct i40e_pf *pf = pci_get_drvdata(pdev); struct i40e_hw *hw = &pf->hw; - int retval = 0; - set_bit(__I40E_SUSPENDED, pf->state); + /* If we're already suspended, then there is nothing to do */ + if (test_and_set_bit(__I40E_SUSPENDED, pf->state)) + return 0; + set_bit(__I40E_DOWN, pf->state); + /* Ensure service task will not be running */ + del_timer_sync(&pf->service_timer); + cancel_work_sync(&pf->service_task); + + /* Client close must be called explicitly here because the timer + * has been stopped. + */ + i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], false); + + if (test_bit(__I40E_DEBUG_MODE, pf->state)) + return 0; + if (pf->wol_en && (pf->hw_features & I40E_HW_WOL_MC_MAGIC_PKT_WAKE)) i40e_enable_mc_magic_wake(pf); - i40e_prep_for_reset(pf, false); + /* Since we're going to destroy queues during the + * i40e_clear_interrupt_scheme() we should hold the RTNL lock for this + * whole section + */ + rtnl_lock(); + + i40e_prep_for_reset(pf, true); wr32(hw, I40E_PFPM_APM, (pf->wol_en ? I40E_PFPM_APM_APME_MASK : 0)); wr32(hw, I40E_PFPM_WUFC, (pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0)); - i40e_stop_misc_vector(pf); - if (pf->msix_entries) { - synchronize_irq(pf->msix_entries[0].vector); - free_irq(pf->msix_entries[0].vector, pf); + /* Clear the interrupt scheme and release our IRQs so that the system + * can safely hibernate even when there are a large number of CPUs. + * Otherwise hibernation might fail when mapping all the vectors back + * to CPU0. + */ + i40e_clear_interrupt_scheme(pf); + + rtnl_unlock(); + + return 0; +} + +/** + * i40e_resume - PM callback for waking up from D3 + * @dev: generic device information structure + **/ +static int i40e_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct i40e_pf *pf = pci_get_drvdata(pdev); + int err; + + /* If we're not suspended, then there is nothing to do */ + if (!test_bit(__I40E_SUSPENDED, pf->state)) + return 0; + + /* We need to hold the RTNL lock prior to restoring interrupt schemes, + * since we're going to be restoring queues + */ + rtnl_lock(); + + /* We cleared the interrupt scheme when we suspended, so we need to + * restore it now to resume device functionality. + */ + err = i40e_restore_interrupt_scheme(pf); + if (err) { + dev_err(&pdev->dev, "Cannot restore interrupt scheme: %d\n", + err); } + + clear_bit(__I40E_DOWN, pf->state); + i40e_reset_and_rebuild(pf, false, true); + + rtnl_unlock(); + + /* Clear suspended state last after everything is recovered */ + clear_bit(__I40E_SUSPENDED, pf->state); + + /* Restart the service task */ + mod_timer(&pf->service_timer, + round_jiffies(jiffies + pf->service_timer_period)); + + return 0; +} + +#ifdef USE_LEGACY_PM_SUPPORT +/** + * i40e_legacy_suspend - PCI callback for moving to D3 + * @pdev: PCI device information struct + * @state: PCI power state + * + * Legacy suspend handler for older kernels which do not support the newer + * generic callbacks + **/ +static int i40e_legacy_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + int retval = i40e_suspend(&pdev->dev); + + /* Some older kernels may not handle state correctly for legacy power + * management, so we'll handle it here ourselves + */ retval = pci_save_state(pdev); if (retval) return retval; + pci_disable_device(pdev); pci_wake_from_d3(pdev, pf->wol_en); pci_set_power_state(pdev, PCI_D3hot); @@ -12095,12 +16602,14 @@ static int i40e_suspend(struct pci_dev *pdev, pm_message_t state) } /** - * i40e_resume - PCI callback for waking up from D3 + * i40e_legacy_resume - PCI callback for waking up from D3 * @pdev: PCI device information struct + * + * Legacy resume handler for kernels which do not support the newer generic + * callbacks. **/ -static int i40e_resume(struct pci_dev *pdev) +static int i40e_legacy_resume(struct pci_dev *pdev) { - struct i40e_pf *pf = pci_get_drvdata(pdev); u32 err; pci_set_power_state(pdev, PCI_D0); @@ -12112,7 +16621,7 @@ static int i40e_resume(struct pci_dev *pdev) err = pci_enable_device_mem(pdev); if (err) { - dev_err(&pdev->dev, "Cannot enable PCI device from suspend\n"); + dev_err(pci_dev_to_dev(pdev), "Cannot enable PCI device from suspend\n"); return err; } pci_set_master(pdev); @@ -12120,43 +16629,75 @@ static int i40e_resume(struct pci_dev *pdev) /* no wakeup events while running */ pci_wake_from_d3(pdev, false); - /* handling the reset will rebuild the device state */ - if (test_and_clear_bit(__I40E_SUSPENDED, pf->state)) { - clear_bit(__I40E_DOWN, pf->state); - if (pf->msix_entries) { - err = request_irq(pf->msix_entries[0].vector, - i40e_intr, 0, pf->int_name, pf); - if (err) { - dev_err(&pf->pdev->dev, - "request_irq for %s failed: %d\n", - pf->int_name, err); - } - } - i40e_reset_and_rebuild(pf, false, false); - } - - return 0; + return i40e_resume(&pdev->dev); } +#endif /* USE_LEGACY_PM_SUPPORT */ +#endif /* CONFIG_PM */ -#endif +#ifdef HAVE_CONST_STRUCT_PCI_ERROR_HANDLERS static const struct pci_error_handlers i40e_err_handler = { +#else +static struct pci_error_handlers i40e_err_handler = { +#endif .error_detected = i40e_pci_error_detected, .slot_reset = i40e_pci_error_slot_reset, +#ifdef HAVE_PCI_ERROR_HANDLER_RESET_NOTIFY + .reset_notify = i40e_pci_error_reset_notify, +#endif +#ifdef HAVE_PCI_ERROR_HANDLER_RESET_PREPARE + .reset_prepare = i40e_pci_error_reset_prepare, + .reset_done = i40e_pci_error_reset_done, +#endif .resume = i40e_pci_error_resume, }; +#if defined(HAVE_RHEL6_SRIOV_CONFIGURE) || defined(HAVE_RHEL7_PCI_DRIVER_RH) +static struct pci_driver_rh i40e_driver_rh = { +#ifdef HAVE_RHEL6_SRIOV_CONFIGURE + .sriov_configure = i40e_pci_sriov_configure, +#elif defined(HAVE_RHEL7_PCI_RESET_NOTIFY) + .reset_notify = i40e_pci_error_reset_notify, +#endif +}; + +#endif +#endif /* HAVE_PCI_ERS */ +#if defined(CONFIG_PM) && !defined(USE_LEGACY_PM_SUPPORT) +static SIMPLE_DEV_PM_OPS(i40e_pm_ops, i40e_suspend, i40e_resume); +#endif /* CONFIG_PM && !USE_LEGACY_PM_SUPPORT */ + static struct pci_driver i40e_driver = { .name = i40e_driver_name, .id_table = i40e_pci_tbl, .probe = i40e_probe, +#ifdef HAVE_CONFIG_HOTPLUG + .remove = __devexit_p(i40e_remove), +#else .remove = i40e_remove, -#ifdef CONFIG_PM - .suspend = i40e_suspend, - .resume = i40e_resume, #endif +#ifdef CONFIG_PM +#ifdef USE_LEGACY_PM_SUPPORT + .suspend = i40e_legacy_suspend, + .resume = i40e_legacy_resume, +#else /* USE_LEGACY_PM_SUPPORT */ + .driver = { + .pm = &i40e_pm_ops, + }, +#endif /* !USE_LEGACY_PM_SUPPORT */ +#endif /* CONFIG_PM */ .shutdown = i40e_shutdown, +#ifdef HAVE_PCI_ERS .err_handler = &i40e_err_handler, +#endif +#ifdef HAVE_SRIOV_CONFIGURE .sriov_configure = i40e_pci_sriov_configure, +#endif +#ifdef HAVE_RHEL6_SRIOV_CONFIGURE + .rh_reserved = &i40e_driver_rh, +#endif +#ifdef HAVE_RHEL7_PCI_DRIVER_RH + .pci_driver_rh = &i40e_driver_rh, +#endif }; /** @@ -12170,11 +16711,6 @@ static int __init i40e_init_module(void) pr_info("%s: %s - version %s\n", i40e_driver_name, i40e_driver_string, i40e_driver_version_str); pr_info("%s: %s\n", i40e_driver_name, i40e_copyright); -#if defined (CONFIG_ARM64) - mark_tech_preview(i40e_driver.name, THIS_MODULE); -#elif !defined (CONFIG_ARM64) && !defined (CONFIG_PPC) - mark_driver_unsupported(i40e_driver.name); -#endif /* There is no need to throttle the number of active tasks because * each device limits its own task using a state bit for scheduling @@ -12188,7 +16724,13 @@ static int __init i40e_init_module(void) pr_err("%s: Failed to create workqueue\n", i40e_driver_name); return -ENOMEM; } +#ifdef HAVE_RHEL7_PCI_DRIVER_RH + /* The size member must be initialized in the driver via a call to + * set_pci_driver_rh_size before pci_register_driver is called + */ + set_pci_driver_rh_size(i40e_driver_rh); +#endif i40e_dbg_init(); return pci_register_driver(&i40e_driver); } @@ -12205,5 +16747,8 @@ static void __exit i40e_exit_module(void) pci_unregister_driver(&i40e_driver); destroy_workqueue(i40e_wq); i40e_dbg_exit(); +#ifdef HAVE_KFREE_RCU_BARRIER + rcu_barrier(); +#endif } module_exit(i40e_exit_module); diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index d591b3e6b..04fe349dd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #include "i40e_prototype.h" @@ -34,12 +11,12 @@ * once per NVM initialization, e.g. inside the i40e_init_shared_code(). * Please notice that the NVM term is used here (& in all methods covered * in this file) as an equivalent of the FLASH part mapped into the SR. - * We are accessing FLASH always thru the Shadow RAM. + * We are accessing FLASH always through the Shadow RAM. **/ i40e_status i40e_init_nvm(struct i40e_hw *hw) { struct i40e_nvm_info *nvm = &hw->nvm; - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; u32 fla, gens; u8 sr_size; @@ -78,7 +55,7 @@ i40e_status i40e_init_nvm(struct i40e_hw *hw) i40e_status i40e_acquire_nvm(struct i40e_hw *hw, enum i40e_aq_resource_access_type access) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; u64 gtime, timeout; u64 time_left = 0; @@ -108,13 +85,13 @@ i40e_status i40e_acquire_nvm(struct i40e_hw *hw, I40E_NVM_RESOURCE_ID, access, 0, &time_left, NULL); - if (!ret_code) { + if (ret_code == I40E_SUCCESS) { hw->nvm.hw_semaphore_timeout = I40E_MS_TO_GTIME(time_left) + gtime; break; } } - if (ret_code) { + if (ret_code != I40E_SUCCESS) { hw->nvm.hw_semaphore_timeout = 0; i40e_debug(hw, I40E_DEBUG_NVM, "NVM acquire timed out, wait %llu ms before trying again. status=%d aq_err=%d\n", @@ -147,11 +124,10 @@ void i40e_release_nvm(struct i40e_hw *hw) */ while ((ret_code == I40E_ERR_ADMIN_QUEUE_TIMEOUT) && (total_delay < hw->aq.asq_cmd_timeout)) { - usleep_range(1000, 2000); - ret_code = i40e_aq_release_resource(hw, - I40E_NVM_RESOURCE_ID, - 0, NULL); - total_delay++; + usleep_range(1000, 2000); + ret_code = i40e_aq_release_resource(hw, + I40E_NVM_RESOURCE_ID, 0, NULL); + total_delay++; } } @@ -170,7 +146,7 @@ static i40e_status i40e_poll_sr_srctl_done_bit(struct i40e_hw *hw) for (wait_cnt = 0; wait_cnt < I40E_SRRD_SRCTL_ATTEMPTS; wait_cnt++) { srctl = rd32(hw, I40E_GLNVM_SRCTL); if (srctl & I40E_GLNVM_SRCTL_DONE_MASK) { - ret_code = 0; + ret_code = I40E_SUCCESS; break; } udelay(5); @@ -188,15 +164,15 @@ static i40e_status i40e_poll_sr_srctl_done_bit(struct i40e_hw *hw) * * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. **/ -static i40e_status i40e_read_nvm_word_srctl(struct i40e_hw *hw, u16 offset, - u16 *data) +static i40e_status i40e_read_nvm_word_srctl(struct i40e_hw *hw, + u16 offset, u16 *data) { i40e_status ret_code = I40E_ERR_TIMEOUT; u32 sr_reg; if (offset >= hw->nvm.sr_size) { i40e_debug(hw, I40E_DEBUG_NVM, - "NVM read error: offset %d beyond Shadow RAM limit %d\n", + "NVM read error: Offset %d beyond Shadow RAM limit %d\n", offset, hw->nvm.sr_size); ret_code = I40E_ERR_PARAM; goto read_nvm_exit; @@ -204,7 +180,7 @@ static i40e_status i40e_read_nvm_word_srctl(struct i40e_hw *hw, u16 offset, /* Poll the done bit first */ ret_code = i40e_poll_sr_srctl_done_bit(hw); - if (!ret_code) { + if (ret_code == I40E_SUCCESS) { /* Write the address and start reading */ sr_reg = ((u32)offset << I40E_GLNVM_SRCTL_ADDR_SHIFT) | BIT(I40E_GLNVM_SRCTL_START_SHIFT); @@ -212,14 +188,14 @@ static i40e_status i40e_read_nvm_word_srctl(struct i40e_hw *hw, u16 offset, /* Poll I40E_GLNVM_SRCTL until the done bit is set */ ret_code = i40e_poll_sr_srctl_done_bit(hw); - if (!ret_code) { + if (ret_code == I40E_SUCCESS) { sr_reg = rd32(hw, I40E_GLNVM_SRDATA); *data = (u16)((sr_reg & I40E_GLNVM_SRDATA_RDDATA_MASK) >> I40E_GLNVM_SRDATA_RDDATA_SHIFT); } } - if (ret_code) + if (ret_code != I40E_SUCCESS) i40e_debug(hw, I40E_DEBUG_NVM, "NVM read error: Couldn't access Shadow RAM address: 0x%x\n", offset); @@ -239,9 +215,10 @@ read_nvm_exit: * * Writes a 16 bit words buffer to the Shadow RAM using the admin command. **/ -static i40e_status i40e_read_nvm_aq(struct i40e_hw *hw, u8 module_pointer, - u32 offset, u16 words, void *data, - bool last_command) +static i40e_status i40e_read_nvm_aq(struct i40e_hw *hw, + u8 module_pointer, u32 offset, + u16 words, void *data, + bool last_command) { i40e_status ret_code = I40E_ERR_NVM; struct i40e_asq_cmd_details cmd_details; @@ -287,18 +264,18 @@ static i40e_status i40e_read_nvm_aq(struct i40e_hw *hw, u8 module_pointer, * Reads one 16 bit word from the Shadow RAM using the AdminQ **/ static i40e_status i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset, - u16 *data) + u16 *data) { i40e_status ret_code = I40E_ERR_TIMEOUT; ret_code = i40e_read_nvm_aq(hw, 0x0, offset, 1, data, true); - *data = le16_to_cpu(*(__le16 *)data); + *data = LE16_TO_CPU(*(__le16 *)data); return ret_code; } /** - * __i40e_read_nvm_word - Reads nvm word, assumes caller does the locking + * __i40e_read_nvm_word - Reads NVM word, assumes caller does the locking * @hw: pointer to the HW structure * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) * @data: word read from the Shadow RAM @@ -309,19 +286,17 @@ static i40e_status i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset, * taken via i40e_acquire_nvm(). **/ static i40e_status __i40e_read_nvm_word(struct i40e_hw *hw, - u16 offset, u16 *data) + u16 offset, u16 *data) { - i40e_status ret_code = 0; if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) - ret_code = i40e_read_nvm_word_aq(hw, offset, data); - else - ret_code = i40e_read_nvm_word_srctl(hw, offset, data); - return ret_code; + return i40e_read_nvm_word_aq(hw, offset, data); + + return i40e_read_nvm_word_srctl(hw, offset, data); } /** - * i40e_read_nvm_word - Reads nvm word and acquire lock if necessary + * i40e_read_nvm_word - Reads NVM word, acquires lock if necessary * @hw: pointer to the HW structure * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) * @data: word read from the Shadow RAM @@ -329,21 +304,93 @@ static i40e_status __i40e_read_nvm_word(struct i40e_hw *hw, * Reads one 16 bit word from the Shadow RAM. **/ i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, - u16 *data) + u16 *data) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; + + if (hw->flags & I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK) + ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); - ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); if (ret_code) return ret_code; - ret_code = __i40e_read_nvm_word(hw, offset, data); - i40e_release_nvm(hw); - + if (hw->flags & I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK) + i40e_release_nvm(hw); return ret_code; } +/** + * i40e_read_nvm_module_data - Reads NVM Buffer to specified memory location + * @hw: Pointer to the HW structure + * @module_ptr: Pointer to module in words with respect to NVM beginning + * @module_offset: Offset in words from module start + * @data_offset: Offset in words from reading data area start + * @words_data_size: Words to read from NVM + * @data_ptr: Pointer to memory location where resulting buffer will be stored + **/ +enum i40e_status_code +i40e_read_nvm_module_data(struct i40e_hw *hw, u8 module_ptr, u16 module_offset, + u16 data_offset, u16 words_data_size, u16 *data_ptr) +{ + i40e_status status; + u16 specific_ptr = 0; + u16 ptr_value = 0; + u16 offset = 0; + + if (module_ptr != 0) { + status = i40e_read_nvm_word(hw, module_ptr, &ptr_value); + if (status != I40E_SUCCESS) { + i40e_debug(hw, I40E_DEBUG_ALL, + "Reading nvm word failed.Error code: %d.\n", + status); + return I40E_ERR_NVM; + } + } +#define I40E_NVM_INVALID_PTR_VAL 0x7FFF +#define I40E_NVM_INVALID_VAL 0xFFFF + + /* Pointer not initialized */ + if (ptr_value == I40E_NVM_INVALID_PTR_VAL || + ptr_value == I40E_NVM_INVALID_VAL) { + i40e_debug(hw, I40E_DEBUG_ALL, "Pointer not initialized.\n"); + return I40E_ERR_BAD_PTR; + } + + /* Check whether the module is in SR mapped area or outside */ + if (ptr_value & I40E_PTR_TYPE) { + /* Pointer points outside of the Shared RAM mapped area */ + i40e_debug(hw, I40E_DEBUG_ALL, + "Reading nvm data failed. Pointer points outside of the Shared RAM mapped area.\n"); + + return I40E_ERR_PARAM; + } else { + /* Read from the Shadow RAM */ + + status = i40e_read_nvm_word(hw, ptr_value + module_offset, + &specific_ptr); + if (status != I40E_SUCCESS) { + i40e_debug(hw, I40E_DEBUG_ALL, + "Reading nvm word failed.Error code: %d.\n", + status); + return I40E_ERR_NVM; + } + + offset = ptr_value + module_offset + specific_ptr + + data_offset; + + status = i40e_read_nvm_buffer(hw, offset, &words_data_size, + data_ptr); + if (status != I40E_SUCCESS) { + i40e_debug(hw, I40E_DEBUG_ALL, + "Reading nvm buffer failed.Error code: %d.\n", + status); + } + } + + return status; +} + /** * i40e_read_nvm_buffer_srctl - Reads Shadow RAM buffer via SRCTL register * @hw: pointer to the HW structure @@ -356,16 +403,16 @@ i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, * and followed by the release. **/ static i40e_status i40e_read_nvm_buffer_srctl(struct i40e_hw *hw, u16 offset, - u16 *words, u16 *data) + u16 *words, u16 *data) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; u16 index, word; - /* Loop thru the selected region */ + /* Loop through the selected region */ for (word = 0; word < *words; word++) { index = offset + word; ret_code = i40e_read_nvm_word_srctl(hw, index, &data[word]); - if (ret_code) + if (ret_code != I40E_SUCCESS) break; } @@ -387,7 +434,7 @@ static i40e_status i40e_read_nvm_buffer_srctl(struct i40e_hw *hw, u16 offset, * and followed by the release. **/ static i40e_status i40e_read_nvm_buffer_aq(struct i40e_hw *hw, u16 offset, - u16 *words, u16 *data) + u16 *words, u16 *data) { i40e_status ret_code; u16 read_size = *words; @@ -414,7 +461,7 @@ static i40e_status i40e_read_nvm_buffer_aq(struct i40e_hw *hw, u16 offset, ret_code = i40e_read_nvm_aq(hw, 0x0, offset, read_size, data + words_read, last_cmd); - if (ret_code) + if (ret_code != I40E_SUCCESS) goto read_nvm_buffer_aq_exit; /* Increment counter for words already read and move offset to @@ -425,7 +472,7 @@ static i40e_status i40e_read_nvm_buffer_aq(struct i40e_hw *hw, u16 offset, } while (words_read < *words); for (i = 0; i < *words; i++) - data[i] = le16_to_cpu(((__le16 *)data)[i]); + data[i] = LE16_TO_CPU(((__le16 *)data)[i]); read_nvm_buffer_aq_exit: *words = words_read; @@ -433,7 +480,7 @@ read_nvm_buffer_aq_exit: } /** - * __i40e_read_nvm_buffer - Reads nvm buffer, caller must acquire lock + * __i40e_read_nvm_buffer - Reads NVM buffer, caller must acquire lock * @hw: pointer to the HW structure * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). * @words: (in) number of words to read; (out) number of words actually read @@ -443,15 +490,42 @@ read_nvm_buffer_aq_exit: * method. **/ static i40e_status __i40e_read_nvm_buffer(struct i40e_hw *hw, - u16 offset, u16 *words, - u16 *data) + u16 offset, u16 *words, + u16 *data) { - i40e_status ret_code = 0; - if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) - ret_code = i40e_read_nvm_buffer_aq(hw, offset, words, data); - else + return i40e_read_nvm_buffer_aq(hw, offset, words, data); + + return i40e_read_nvm_buffer_srctl(hw, offset, words, data); +} + +/** + * i40e_read_nvm_buffer - Reads Shadow RAM buffer and acquire lock if necessary + * @hw: pointer to the HW structure + * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). + * @words: (in) number of words to read; (out) number of words actually read + * @data: words read from the Shadow RAM + * + * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() + * method. The buffer read is preceded by the NVM ownership take + * and followed by the release. + **/ +i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, + u16 *words, u16 *data) +{ + i40e_status ret_code = I40E_SUCCESS; + + if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) { + ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); + if (!ret_code) { + ret_code = i40e_read_nvm_buffer_aq(hw, offset, words, + data); + i40e_release_nvm(hw); + } + } else { ret_code = i40e_read_nvm_buffer_srctl(hw, offset, words, data); + } + return ret_code; } @@ -467,8 +541,8 @@ static i40e_status __i40e_read_nvm_buffer(struct i40e_hw *hw, * Writes a 16 bit words buffer to the Shadow RAM using the admin command. **/ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer, - u32 offset, u16 words, void *data, - bool last_command) + u32 offset, u16 words, void *data, + bool last_command) { i40e_status ret_code = I40E_ERR_NVM; struct i40e_asq_cmd_details cmd_details; @@ -482,25 +556,20 @@ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer, * Firmware will check the module-based model. */ if ((offset + words) > hw->nvm.sr_size) - i40e_debug(hw, I40E_DEBUG_NVM, - "NVM write error: offset %d beyond Shadow RAM limit %d\n", - (offset + words), hw->nvm.sr_size); + hw_dbg(hw, "NVM write error: offset beyond Shadow RAM limit.\n"); else if (words > I40E_SR_SECTOR_SIZE_IN_WORDS) /* We can write only up to 4KB (one sector), in one AQ write */ - i40e_debug(hw, I40E_DEBUG_NVM, - "NVM write fail error: tried to write %d words, limit is %d.\n", - words, I40E_SR_SECTOR_SIZE_IN_WORDS); + hw_dbg(hw, "NVM write fail error: cannot write more than 4KB in a single write.\n"); else if (((offset + (words - 1)) / I40E_SR_SECTOR_SIZE_IN_WORDS) != (offset / I40E_SR_SECTOR_SIZE_IN_WORDS)) /* A single write cannot spread over two sectors */ - i40e_debug(hw, I40E_DEBUG_NVM, - "NVM write error: cannot spread over two sectors in a single write offset=%d words=%d\n", - offset, words); + hw_dbg(hw, "NVM write error: cannot spread over two sectors in a single write.\n"); else ret_code = i40e_aq_update_nvm(hw, module_pointer, 2 * offset, /*bytes*/ 2 * words, /*bytes*/ - data, last_command, &cmd_details); + data, last_command, 0, + &cmd_details); return ret_code; } @@ -518,7 +587,7 @@ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer, static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw, u16 *checksum) { - i40e_status ret_code; + i40e_status ret_code = I40E_SUCCESS; struct i40e_virt_mem vmem; u16 pcie_alt_module = 0; u16 checksum_local = 0; @@ -534,7 +603,7 @@ static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw, /* read pointer to VPD area */ ret_code = __i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module); - if (ret_code) { + if (ret_code != I40E_SUCCESS) { ret_code = I40E_ERR_NVM_CHECKSUM; goto i40e_calc_nvm_checksum_exit; } @@ -542,7 +611,7 @@ static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw, /* read pointer to PCIe Alt Auto-load module */ ret_code = __i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR, &pcie_alt_module); - if (ret_code) { + if (ret_code != I40E_SUCCESS) { ret_code = I40E_ERR_NVM_CHECKSUM; goto i40e_calc_nvm_checksum_exit; } @@ -556,7 +625,7 @@ static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw, u16 words = I40E_SR_SECTOR_SIZE_IN_WORDS; ret_code = __i40e_read_nvm_buffer(hw, i, &words, data); - if (ret_code) { + if (ret_code != I40E_SUCCESS) { ret_code = I40E_ERR_NVM_CHECKSUM; goto i40e_calc_nvm_checksum_exit; } @@ -598,16 +667,15 @@ i40e_calc_nvm_checksum_exit: **/ i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw) { - i40e_status ret_code; + i40e_status ret_code = I40E_SUCCESS; u16 checksum; __le16 le_sum; ret_code = i40e_calc_nvm_checksum(hw, &checksum); - if (!ret_code) { - le_sum = cpu_to_le16(checksum); + le_sum = CPU_TO_LE16(checksum); + if (ret_code == I40E_SUCCESS) ret_code = i40e_write_nvm_aq(hw, 0x00, I40E_SR_SW_CHECKSUM_WORD, 1, &le_sum, true); - } return ret_code; } @@ -623,7 +691,7 @@ i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw) i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, u16 *checksum) { - i40e_status ret_code = 0; + i40e_status ret_code = I40E_SUCCESS; u16 checksum_sr = 0; u16 checksum_local = 0; @@ -655,42 +723,51 @@ i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, } static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno); + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno); + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - int *perrno); + struct i40e_nvm_access *cmd, + int *perrno); static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - int *perrno); + struct i40e_nvm_access *cmd, + int *perrno); static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno); + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno); + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno); + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno); -static inline u8 i40e_nvmupd_get_module(u32 val) + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); +static i40e_status i40e_nvmupd_get_aq_event(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); +static INLINE u8 i40e_nvmupd_get_module(u32 val) { return (u8)(val & I40E_NVM_MOD_PNT_MASK); } -static inline u8 i40e_nvmupd_get_transaction(u32 val) +static INLINE u8 i40e_nvmupd_get_transaction(u32 val) { return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT); } -static const char * const i40e_nvm_update_state_str[] = { +static INLINE u8 i40e_nvmupd_get_preservation_flags(u32 val) +{ + return (u8)((val & I40E_NVM_PRESERVATION_FLAGS_MASK) >> + I40E_NVM_PRESERVATION_FLAGS_SHIFT); +} + +static const char *i40e_nvm_update_state_str[] = { "I40E_NVMUPD_INVALID", "I40E_NVMUPD_READ_CON", "I40E_NVMUPD_READ_SNT", @@ -707,6 +784,8 @@ static const char * const i40e_nvm_update_state_str[] = { "I40E_NVMUPD_STATUS", "I40E_NVMUPD_EXEC_AQ", "I40E_NVMUPD_GET_AQ_RESULT", + "I40E_NVMUPD_GET_AQ_EVENT", + "I40E_NVMUPD_GET_FEATURES", }; /** @@ -719,8 +798,8 @@ static const char * const i40e_nvm_update_state_str[] = { * Dispatches command depending on what update state is current **/ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno) + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) { i40e_status status; enum i40e_nvmupd_cmd upd_cmd; @@ -764,7 +843,32 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, if (hw->nvmupd_state == I40E_NVMUPD_STATE_ERROR) hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; - return 0; + return I40E_SUCCESS; + } + + /* + * A supported features request returns immediately + * rather than going into state machine + */ + if (upd_cmd == I40E_NVMUPD_FEATURES) { + if (cmd->data_size < hw->nvmupd_features.size) { + *perrno = -EFAULT; + return I40E_ERR_BUF_TOO_SHORT; + } + + /* + * If buffer is bigger than i40e_nvmupd_features structure, + * make sure the trailing bytes are set to 0x0. + */ + if (cmd->data_size > hw->nvmupd_features.size) + i40e_memset(bytes + hw->nvmupd_features.size, 0x0, + cmd->data_size - hw->nvmupd_features.size, + I40E_NONDMA_MEM); + + i40e_memcpy(bytes, &hw->nvmupd_features, + hw->nvmupd_features.size, I40E_NONDMA_MEM); + + return I40E_SUCCESS; } /* Clear status even it is not read and log */ @@ -782,7 +886,7 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, * ~5ms for most commands. However lock is held for ~60ms for * NVMUPD_CSUM_LCB command. */ - mutex_lock(&hw->aq.arq_mutex); + i40e_acquire_spinlock(&hw->aq.arq_spinlock); switch (hw->nvmupd_state) { case I40E_NVMUPD_STATE_INIT: status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno); @@ -802,9 +906,9 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, * the wait info and return before doing anything else */ if (cmd->offset == 0xffff) { - i40e_nvmupd_check_wait_event(hw, hw->nvm_wait_opcode); - status = 0; - goto exit; + i40e_nvmupd_clear_wait_state(hw); + status = I40E_SUCCESS; + break; } status = I40E_ERR_NOT_READY; @@ -819,8 +923,8 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, *perrno = -ESRCH; break; } -exit: - mutex_unlock(&hw->aq.arq_mutex); + + i40e_release_spinlock(&hw->aq.arq_spinlock); return status; } @@ -835,10 +939,10 @@ exit: * state. Reject all other commands. **/ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno) + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; enum i40e_nvmupd_cmd upd_cmd; upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); @@ -948,6 +1052,10 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, status = i40e_nvmupd_get_aq_result(hw, cmd, bytes, perrno); break; + case I40E_NVMUPD_GET_AQ_EVENT: + status = i40e_nvmupd_get_aq_event(hw, cmd, bytes, perrno); + break; + default: i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: bad cmd %s in init state\n", @@ -970,10 +1078,10 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, * change in state; reject all other commands. **/ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno) + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; enum i40e_nvmupd_cmd upd_cmd; upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); @@ -1012,10 +1120,10 @@ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, * change in state; reject all other commands **/ static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno) + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; enum i40e_nvmupd_cmd upd_cmd; bool retry_attempt = false; @@ -1122,38 +1230,55 @@ retry: } /** - * i40e_nvmupd_check_wait_event - handle NVM update operation events + * i40e_nvmupd_clear_wait_state - clear wait state on hw * @hw: pointer to the hardware structure - * @opcode: the event that just happened **/ -void i40e_nvmupd_check_wait_event(struct i40e_hw *hw, u16 opcode) +void i40e_nvmupd_clear_wait_state(struct i40e_hw *hw) { - if (opcode == hw->nvm_wait_opcode) { - i40e_debug(hw, I40E_DEBUG_NVM, - "NVMUPD: clearing wait on opcode 0x%04x\n", opcode); - if (hw->nvm_release_on_done) { - i40e_release_nvm(hw); - hw->nvm_release_on_done = false; - } - hw->nvm_wait_opcode = 0; + i40e_debug(hw, I40E_DEBUG_NVM, + "NVMUPD: clearing wait on opcode 0x%04x\n", + hw->nvm_wait_opcode); - if (hw->aq.arq_last_status) { - hw->nvmupd_state = I40E_NVMUPD_STATE_ERROR; - return; - } + if (hw->nvm_release_on_done) { + i40e_release_nvm(hw); + hw->nvm_release_on_done = false; + } + hw->nvm_wait_opcode = 0; - switch (hw->nvmupd_state) { - case I40E_NVMUPD_STATE_INIT_WAIT: - hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; - break; + if (hw->aq.arq_last_status) { + hw->nvmupd_state = I40E_NVMUPD_STATE_ERROR; + return; + } - case I40E_NVMUPD_STATE_WRITE_WAIT: - hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING; - break; + switch (hw->nvmupd_state) { + case I40E_NVMUPD_STATE_INIT_WAIT: + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + break; - default: - break; - } + case I40E_NVMUPD_STATE_WRITE_WAIT: + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING; + break; + + default: + break; + } +} + +/** + * i40e_nvmupd_check_wait_event - handle NVM update operation events + * @hw: pointer to the hardware structure + * @opcode: the event that just happened + * @desc: AdminQ descriptor + **/ +void i40e_nvmupd_check_wait_event(struct i40e_hw *hw, u16 opcode, + struct i40e_aq_desc *desc) +{ + u32 aq_desc_len = sizeof(struct i40e_aq_desc); + + if (opcode == hw->nvm_wait_opcode) { + i40e_memcpy(&hw->nvm_aq_event_desc, desc, + aq_desc_len, I40E_NONDMA_TO_NONDMA); + i40e_nvmupd_clear_wait_state(hw); } } @@ -1166,8 +1291,8 @@ void i40e_nvmupd_check_wait_event(struct i40e_hw *hw, u16 opcode) * Return one of the valid command types or I40E_NVMUPD_INVALID **/ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - int *perrno) + struct i40e_nvm_access *cmd, + int *perrno) { enum i40e_nvmupd_cmd upd_cmd; u8 module, transaction; @@ -1204,10 +1329,23 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, upd_cmd = I40E_NVMUPD_READ_SA; break; case I40E_NVM_EXEC: - if (module == 0xf) - upd_cmd = I40E_NVMUPD_STATUS; - else if (module == 0) + switch (module) { + case I40E_NVM_EXEC_GET_AQ_RESULT: upd_cmd = I40E_NVMUPD_GET_AQ_RESULT; + break; + case I40E_NVM_EXEC_FEATURES: + upd_cmd = I40E_NVMUPD_FEATURES; + break; + case I40E_NVM_EXEC_STATUS: + upd_cmd = I40E_NVMUPD_STATUS; + break; + default: + *perrno = -EFAULT; + return I40E_NVMUPD_INVALID; + } + break; + case I40E_NVM_AQE: + upd_cmd = I40E_NVMUPD_GET_AQ_EVENT; break; } break; @@ -1259,8 +1397,8 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, * cmd structure contains identifiers and data buffer **/ static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno) + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) { struct i40e_asq_cmd_details cmd_details; i40e_status status; @@ -1271,6 +1409,9 @@ static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, u32 aq_data_len; i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); + if (cmd->offset == 0xffff) + return I40E_SUCCESS; + memset(&cmd_details, 0, sizeof(cmd_details)); cmd_details.wb_desc = &hw->nvm_wb_desc; @@ -1289,7 +1430,7 @@ static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, /* if data buffer needed, make sure it's ready */ aq_data_len = cmd->data_size - aq_desc_len; - buff_size = max_t(u32, aq_data_len, le16_to_cpu(aq_desc->datalen)); + buff_size = max(aq_data_len, (u32)LE16_TO_CPU(aq_desc->datalen)); if (buff_size) { if (!hw->nvm_buff.va) { status = i40e_allocate_virt_mem(hw, &hw->nvm_buff, @@ -1302,10 +1443,14 @@ static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, if (hw->nvm_buff.va) { buff = hw->nvm_buff.va; - memcpy(buff, &bytes[aq_desc_len], aq_data_len); + i40e_memcpy(buff, &bytes[aq_desc_len], aq_data_len, + I40E_NONDMA_TO_NONDMA); } } + if (cmd->offset) + memset(&hw->nvm_aq_event_desc, 0, aq_desc_len); + /* and away we go! */ status = i40e_asq_send_command(hw, aq_desc, buff, buff_size, &cmd_details); @@ -1315,6 +1460,7 @@ static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, i40e_stat_str(hw, status), i40e_aq_str(hw, hw->aq.asq_last_status)); *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + return status; } /* should we wait for a followup event? */ @@ -1336,8 +1482,8 @@ static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, * cmd structure contains identifiers and data buffer **/ static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno) + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) { u32 aq_total_len; u32 aq_desc_len; @@ -1347,7 +1493,7 @@ static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); aq_desc_len = sizeof(struct i40e_aq_desc); - aq_total_len = aq_desc_len + le16_to_cpu(hw->nvm_wb_desc.datalen); + aq_total_len = aq_desc_len + LE16_TO_CPU(hw->nvm_wb_desc.datalen); /* check offset range */ if (cmd->offset > aq_total_len) { @@ -1375,13 +1521,13 @@ static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw, __func__, cmd->offset, cmd->offset + len); buff = ((u8 *)&hw->nvm_wb_desc) + cmd->offset; - memcpy(bytes, buff, len); + i40e_memcpy(bytes, buff, len, I40E_NONDMA_TO_NONDMA); bytes += len; remainder -= len; buff = hw->nvm_buff.va; } else { - buff = hw->nvm_buff.va + (cmd->offset - aq_desc_len); + buff = (u8 *)hw->nvm_buff.va + (cmd->offset - aq_desc_len); } if (remainder > 0) { @@ -1389,10 +1535,45 @@ static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "%s: databuf bytes %d to %d\n", __func__, start_byte, start_byte + remainder); - memcpy(bytes, buff, remainder); + i40e_memcpy(bytes, buff, remainder, I40E_NONDMA_TO_NONDMA); } - return 0; + return I40E_SUCCESS; +} + +/** + * i40e_nvmupd_get_aq_event - Get the Admin Queue event from previous exec_aq + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * cmd structure contains identifiers and data buffer + **/ +static i40e_status i40e_nvmupd_get_aq_event(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + u32 aq_total_len; + u32 aq_desc_len; + + i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); + + aq_desc_len = sizeof(struct i40e_aq_desc); + aq_total_len = aq_desc_len + LE16_TO_CPU(hw->nvm_aq_event_desc.datalen); + + /* check copylength range */ + if (cmd->data_size > aq_total_len) { + i40e_debug(hw, I40E_DEBUG_NVM, + "%s: copy length %d too big, trimming to %d\n", + __func__, cmd->data_size, aq_total_len); + cmd->data_size = aq_total_len; + } + + i40e_memcpy(bytes, &hw->nvm_aq_event_desc, cmd->data_size, + I40E_NONDMA_TO_NONDMA); + + return I40E_SUCCESS; } /** @@ -1405,8 +1586,8 @@ static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw, * cmd structure contains identifiers and data buffer **/ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno) + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) { struct i40e_asq_cmd_details cmd_details; i40e_status status; @@ -1444,10 +1625,10 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, * module, offset, data_size and data are in cmd structure **/ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - int *perrno) + struct i40e_nvm_access *cmd, + int *perrno) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; struct i40e_asq_cmd_details cmd_details; u8 module, transaction; bool last; @@ -1484,24 +1665,26 @@ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, * module, offset, data_size and data are in cmd structure **/ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *perrno) + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) { - i40e_status status = 0; + i40e_status status = I40E_SUCCESS; struct i40e_asq_cmd_details cmd_details; u8 module, transaction; + u8 preservation_flags; bool last; transaction = i40e_nvmupd_get_transaction(cmd->config); module = i40e_nvmupd_get_module(cmd->config); last = (transaction & I40E_NVM_LCB); + preservation_flags = i40e_nvmupd_get_preservation_flags(cmd->config); memset(&cmd_details, 0, sizeof(cmd_details)); cmd_details.wb_desc = &hw->nvm_wb_desc; status = i40e_aq_update_nvm(hw, module, cmd->offset, (u16)cmd->data_size, bytes, last, - &cmd_details); + preservation_flags, &cmd_details); if (status) { i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n", diff --git a/drivers/net/ethernet/intel/i40e/i40e_osdep.h b/drivers/net/ethernet/intel/i40e/i40e_osdep.h index 80e66da6b..f6ac4b2bd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_osdep.h +++ b/drivers/net/ethernet/intel/i40e/i40e_osdep.h @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_OSDEP_H_ #define _I40E_OSDEP_H_ @@ -34,14 +11,41 @@ #include #include -/* get readq/writeq support for 32 bit kernels, use the low-first version */ -#include +#include +#include + +#ifndef readq +static inline __u64 readq(const volatile void __iomem *addr) +{ + const volatile u32 __iomem *p = addr; + u32 low, high; + + low = readl(p); + high = readl(p + 1); + + return low + ((u64)high << 32); +} +#endif + +#ifndef writeq +static inline void writeq(__u64 val, volatile void __iomem *addr) +{ + writel(val, addr); + writel(val >> 32, addr + 4); +} +#endif +#include "kcompat.h" /* File to be the magic between shared code and * actual OS primitives */ -#define hw_dbg(hw, S, A...) do {} while (0) +#define hw_dbg(h, s, ...) do { \ + pr_debug("i40e %02x:%02x.%x " s, \ + (h)->bus.bus_id, (h)->bus.device, \ + (h)->bus.func, ##__VA_ARGS__); \ +} while (0) + #define wr32(a, reg, value) writel((value), ((a)->hw_addr + (reg))) #define rd32(a, reg) readl((a)->hw_addr + (reg)) @@ -49,7 +53,6 @@ #define wr64(a, reg, value) writeq((value), ((a)->hw_addr + (reg))) #define rd64(a, reg) readq((a)->hw_addr + (reg)) #define i40e_flush(a) readl((a)->hw_addr + I40E_GLGEN_STAT) - /* memory allocation tracking */ struct i40e_dma_mem { void *va; @@ -58,7 +61,8 @@ struct i40e_dma_mem { }; #define i40e_allocate_dma_mem(h, m, unused, s, a) \ - i40e_allocate_dma_mem_d(h, m, s, a) + i40e_allocate_dma_mem_d(h, m, unused, s, a) + #define i40e_free_dma_mem(h, m) i40e_free_dma_mem_d(h, m) struct i40e_virt_mem { @@ -77,5 +81,40 @@ do { \ (h)->bus.func, ##__VA_ARGS__); \ } while (0) +/* these things are all directly replaced with sed during the kernel build */ +#define INLINE inline + + +#define CPU_TO_LE16(o) cpu_to_le16(o) +#define CPU_TO_LE32(s) cpu_to_le32(s) +#define CPU_TO_LE64(h) cpu_to_le64(h) +#define LE16_TO_CPU(a) le16_to_cpu(a) +#define LE32_TO_CPU(c) le32_to_cpu(c) +#define LE64_TO_CPU(k) le64_to_cpu(k) + +/* SW spinlock */ +struct i40e_spinlock { + struct mutex spinlock; +}; + +static inline void i40e_no_action(struct i40e_spinlock *sp) +{ + /* nothing */ +} + +/* the locks are initialized in _probe and destroyed in _remove + * so make sure NOT to implement init/destroy here, as to + * avoid the i40e_init_adminq code trying to reinitialize + * the persistent lock memory + */ +#define i40e_init_spinlock(_sp) i40e_no_action(_sp) +#define i40e_acquire_spinlock(_sp) i40e_acquire_spinlock_d(_sp) +#define i40e_release_spinlock(_sp) i40e_release_spinlock_d(_sp) +#define i40e_destroy_spinlock(_sp) i40e_no_action(_sp) + + +#define i40e_memset(a, b, c, d) memset((a), (b), (c)) +#define i40e_memcpy(a, b, c, d) memcpy((a), (b), (c)) + typedef enum i40e_status_code i40e_status; #endif /* _I40E_OSDEP_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index a39b13197..0f6120609 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -1,35 +1,12 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2016 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_PROTOTYPE_H_ #define _I40E_PROTOTYPE_H_ #include "i40e_type.h" #include "i40e_alloc.h" -#include +#include "virtchnl.h" /* Prototypes for shared code functions that are not in * the standard function pointer structures. These are @@ -45,6 +22,13 @@ void i40e_adminq_init_ring_data(struct i40e_hw *hw); i40e_status i40e_clean_arq_element(struct i40e_hw *hw, struct i40e_arq_event_info *e, u16 *events_pending); +enum i40e_status_code +i40e_asq_send_command_atomic(struct i40e_hw *hw, + struct i40e_aq_desc *desc, + void *buff, /* can be NULL */ + u16 buff_size, + struct i40e_asq_cmd_details *cmd_details, + bool is_atomic_context); i40e_status i40e_asq_send_command(struct i40e_hw *hw, struct i40e_aq_desc *desc, void *buff, /* can be NULL */ @@ -58,29 +42,39 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void i40e_idle_aq(struct i40e_hw *hw); bool i40e_check_asq_alive(struct i40e_hw *hw); i40e_status i40e_aq_queue_shutdown(struct i40e_hw *hw, bool unloading); -const char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err); -const char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err); i40e_status i40e_aq_get_rss_lut(struct i40e_hw *hw, u16 seid, - bool pf_lut, u8 *lut, u16 lut_size); + bool pf_lut, u8 *lut, u16 lut_size); i40e_status i40e_aq_set_rss_lut(struct i40e_hw *hw, u16 seid, - bool pf_lut, u8 *lut, u16 lut_size); + bool pf_lut, u8 *lut, u16 lut_size); i40e_status i40e_aq_get_rss_key(struct i40e_hw *hw, - u16 seid, - struct i40e_aqc_get_set_rss_key_data *key); + u16 seid, + struct i40e_aqc_get_set_rss_key_data *key); i40e_status i40e_aq_set_rss_key(struct i40e_hw *hw, - u16 seid, - struct i40e_aqc_get_set_rss_key_data *key); + u16 seid, + struct i40e_aqc_get_set_rss_key_data *key); +const char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err); +const char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err); u32 i40e_led_get(struct i40e_hw *hw); void i40e_led_set(struct i40e_hw *hw, u32 mode, bool blink); i40e_status i40e_led_set_phy(struct i40e_hw *hw, bool on, - u16 led_addr, u32 mode); + u16 led_addr, u32 mode); i40e_status i40e_led_get_phy(struct i40e_hw *hw, u16 *led_addr, - u16 *val); + u16 *val); i40e_status i40e_blink_phy_link_led(struct i40e_hw *hw, - u32 time, u32 interval); - + u32 time, u32 interval); +i40e_status i40e_get_phy_lpi_status(struct i40e_hw *hw, + struct i40e_hw_port_stats *stats); +i40e_status i40e_get_lpi_counters(struct i40e_hw *hw, u32 *tx_counter, + u32 *rx_counter, bool *is_clear); +i40e_status i40e_lpi_stat_update(struct i40e_hw *hw, + bool offset_loaded, u64 *tx_offset, + u64 *tx_stat, u64 *rx_offset, + u64 *rx_stat); +i40e_status i40e_get_lpi_duration(struct i40e_hw *hw, + struct i40e_hw_port_stats *stat, + u64 *tx_duration, u64 *rx_duration); /* admin send queue commands */ i40e_status i40e_aq_get_firmware_version(struct i40e_hw *hw, @@ -89,8 +83,8 @@ i40e_status i40e_aq_get_firmware_version(struct i40e_hw *hw, u16 *api_major_version, u16 *api_minor_version, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_debug_write_register(struct i40e_hw *hw, - u32 reg_addr, u64 reg_val, - struct i40e_asq_cmd_details *cmd_details); + u32 reg_addr, u64 reg_val, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_debug_read_register(struct i40e_hw *hw, u32 reg_addr, u64 *reg_val, struct i40e_asq_cmd_details *cmd_details); @@ -99,23 +93,22 @@ i40e_status i40e_aq_set_phy_debug(struct i40e_hw *hw, u8 cmd_flags, i40e_status i40e_aq_set_default_vsi(struct i40e_hw *hw, u16 vsi_id, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_clear_default_vsi(struct i40e_hw *hw, u16 vsi_id, - struct i40e_asq_cmd_details *cmd_details); -enum i40e_status_code i40e_aq_get_phy_capabilities(struct i40e_hw *hw, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_get_phy_capabilities(struct i40e_hw *hw, bool qualified_modules, bool report_init, struct i40e_aq_get_phy_abilities_resp *abilities, struct i40e_asq_cmd_details *cmd_details); -enum i40e_status_code i40e_aq_set_phy_config(struct i40e_hw *hw, +i40e_status i40e_aq_set_phy_config(struct i40e_hw *hw, struct i40e_aq_set_phy_config *config, struct i40e_asq_cmd_details *cmd_details); -enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, +i40e_status i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, bool atomic_reset); i40e_status i40e_aq_set_phy_int_mask(struct i40e_hw *hw, u16 mask, - struct i40e_asq_cmd_details *cmd_details); -i40e_status i40e_aq_clear_pxe_mode(struct i40e_hw *hw, struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_clear_pxe_mode(struct i40e_hw *hw, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_set_link_restart_an(struct i40e_hw *hw, - bool enable_link, - struct i40e_asq_cmd_details *cmd_details); + bool enable_link, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_get_link_info(struct i40e_hw *hw, bool enable_lse, struct i40e_link_status *link, struct i40e_asq_cmd_details *cmd_details); @@ -136,13 +129,14 @@ i40e_status i40e_aq_set_vsi_unicast_promiscuous(struct i40e_hw *hw, bool rx_only_promisc); i40e_status i40e_aq_set_vsi_multicast_promiscuous(struct i40e_hw *hw, u16 vsi_id, bool set, struct i40e_asq_cmd_details *cmd_details); -enum i40e_status_code i40e_aq_set_vsi_mc_promisc_on_vlan(struct i40e_hw *hw, - u16 seid, bool enable, - u16 vid, +i40e_status i40e_aq_set_vsi_full_promiscuous(struct i40e_hw *hw, + u16 seid, bool set, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_set_vsi_mc_promisc_on_vlan(struct i40e_hw *hw, + u16 seid, bool enable, u16 vid, struct i40e_asq_cmd_details *cmd_details); -enum i40e_status_code i40e_aq_set_vsi_uc_promisc_on_vlan(struct i40e_hw *hw, - u16 seid, bool enable, - u16 vid, +i40e_status i40e_aq_set_vsi_uc_promisc_on_vlan(struct i40e_hw *hw, + u16 seid, bool enable, u16 vid, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_set_vsi_bc_promisc_on_vlan(struct i40e_hw *hw, u16 seid, bool enable, u16 vid, @@ -188,9 +182,8 @@ i40e_status i40e_aq_get_switch_config(struct i40e_hw *hw, struct i40e_aqc_get_switch_config_resp *buf, u16 buf_size, u16 *start_seid, struct i40e_asq_cmd_details *cmd_details); -enum i40e_status_code i40e_aq_set_switch_config(struct i40e_hw *hw, - u16 flags, - u16 valid_flags, +i40e_status i40e_aq_set_switch_config(struct i40e_hw *hw, + u16 flags, u16 valid_flags, u8 mode, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_request_resource(struct i40e_hw *hw, enum i40e_aq_resources_ids resource, @@ -206,36 +199,72 @@ i40e_status i40e_aq_read_nvm(struct i40e_hw *hw, u8 module_pointer, bool last_command, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_erase_nvm(struct i40e_hw *hw, u8 module_pointer, - u32 offset, u16 length, bool last_command, - struct i40e_asq_cmd_details *cmd_details); + u32 offset, u16 length, bool last_command, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_read_nvm_config(struct i40e_hw *hw, + u8 cmd_flags, u32 field_id, void *data, + u16 buf_size, u16 *element_count, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_write_nvm_config(struct i40e_hw *hw, + u8 cmd_flags, void *data, u16 buf_size, + u16 element_count, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_oem_post_update(struct i40e_hw *hw, + void *buff, u16 buff_size, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_discover_capabilities(struct i40e_hw *hw, void *buff, u16 buff_size, u16 *data_size, enum i40e_admin_queue_opc list_type_opc, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_update_nvm(struct i40e_hw *hw, u8 module_pointer, u32 offset, u16 length, void *data, - bool last_command, + bool last_command, u8 preservation_flags, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_rearrange_nvm(struct i40e_hw *hw, + u8 rearrange_nvm, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_nvm_progress(struct i40e_hw *hw, u8 *progress, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_get_lldp_mib(struct i40e_hw *hw, u8 bridge_type, u8 mib_type, void *buff, u16 buff_size, u16 *local_len, u16 *remote_len, struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_set_lldp_mib(struct i40e_hw *hw, + u8 mib_type, void *buff, u16 buff_size, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_cfg_lldp_mib_change_event(struct i40e_hw *hw, bool enable_update, struct i40e_asq_cmd_details *cmd_details); +enum i40e_status_code +i40e_aq_restore_lldp(struct i40e_hw *hw, u8 *setting, bool restore, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_stop_lldp(struct i40e_hw *hw, bool shutdown_agent, + bool persist, struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_set_dcb_parameters(struct i40e_hw *hw, + bool dcb_enable, + struct i40e_asq_cmd_details + *cmd_details); i40e_status i40e_aq_start_lldp(struct i40e_hw *hw, + bool persist, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_get_cee_dcb_config(struct i40e_hw *hw, - void *buff, u16 buff_size, - struct i40e_asq_cmd_details *cmd_details); + void *buff, u16 buff_size, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_start_stop_dcbx(struct i40e_hw *hw, + bool start_agent, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_add_udp_tunnel(struct i40e_hw *hw, u16 udp_port, u8 protocol_index, u8 *filter_index, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_del_udp_tunnel(struct i40e_hw *hw, u8 index, struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_get_switch_resource_alloc(struct i40e_hw *hw, + u8 *num_entries, + struct i40e_aqc_switch_resource_alloc_element_resp *buf, + u16 count, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_delete_element(struct i40e_hw *hw, u16 seid, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_mac_address_write(struct i40e_hw *hw, @@ -282,9 +311,31 @@ i40e_status i40e_aq_query_switch_comp_bw_config(struct i40e_hw *hw, struct i40e_aqc_query_switching_comp_bw_config_resp *bw_data, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_resume_port_tx(struct i40e_hw *hw, - struct i40e_asq_cmd_details *cmd_details); + struct i40e_asq_cmd_details *cmd_details); +enum i40e_status_code +i40e_aq_add_cloud_filters_bb(struct i40e_hw *hw, u16 seid, + struct i40e_aqc_cloud_filters_element_bb *filters, + u8 filter_count); +enum i40e_status_code +i40e_aq_add_cloud_filters(struct i40e_hw *hw, u16 vsi, + struct i40e_aqc_cloud_filters_element_data *filters, + u8 filter_count); +enum i40e_status_code +i40e_aq_rem_cloud_filters(struct i40e_hw *hw, u16 vsi, + struct i40e_aqc_cloud_filters_element_data *filters, + u8 filter_count); +enum i40e_status_code +i40e_aq_rem_cloud_filters_bb(struct i40e_hw *hw, u16 seid, + struct i40e_aqc_cloud_filters_element_bb *filters, + u8 filter_count); i40e_status i40e_read_lldp_cfg(struct i40e_hw *hw, - struct i40e_lldp_variables *lldp_cfg); + struct i40e_lldp_variables *lldp_cfg); +i40e_status i40e_aq_replace_cloud_filters(struct i40e_hw *hw, + struct i40e_aqc_replace_cloud_filters_cmd *filters, + struct i40e_aqc_replace_cloud_filters_cmd_buf *cmd_buf); +i40e_status i40e_aq_alternate_read(struct i40e_hw *hw, + u32 reg_addr0, u32 *reg_val0, + u32 reg_addr1, u32 *reg_val1); /* i40e_common */ i40e_status i40e_init_shared_code(struct i40e_hw *hw); i40e_status i40e_pf_reset(struct i40e_hw *hw); @@ -294,15 +345,13 @@ i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up); i40e_status i40e_update_link_info(struct i40e_hw *hw); i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr); i40e_status i40e_read_bw_from_alt_ram(struct i40e_hw *hw, - u32 *max_bw, u32 *min_bw, bool *min_valid, - bool *max_valid); + u32 *max_bw, u32 *min_bw, bool *min_valid, bool *max_valid); i40e_status i40e_aq_configure_partition_bw(struct i40e_hw *hw, struct i40e_aqc_configure_partition_bw_data *bw_data, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_get_port_mac_addr(struct i40e_hw *hw, u8 *mac_addr); i40e_status i40e_read_pba_string(struct i40e_hw *hw, u8 *pba_num, - u32 pba_num_size); -i40e_status i40e_validate_mac_addr(u8 *mac_addr); + u32 pba_num_size); void i40e_pre_tx_queue_cfg(struct i40e_hw *hw, u32 queue, bool enable); /* prototype for functions used for NVM access */ i40e_status i40e_init_nvm(struct i40e_hw *hw); @@ -311,23 +360,70 @@ i40e_status i40e_acquire_nvm(struct i40e_hw *hw, void i40e_release_nvm(struct i40e_hw *hw); i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, u16 *data); +enum i40e_status_code +i40e_read_nvm_module_data(struct i40e_hw *hw, u8 module_ptr, u16 module_offset, + u16 data_offset, u16 words_data_size, u16 *data_ptr); +i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, + u16 *words, u16 *data); i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw); i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, u16 *checksum); i40e_status i40e_nvmupd_command(struct i40e_hw *hw, - struct i40e_nvm_access *cmd, - u8 *bytes, int *); -void i40e_nvmupd_check_wait_event(struct i40e_hw *hw, u16 opcode); + struct i40e_nvm_access *cmd, + u8 *bytes, int *); +void i40e_nvmupd_check_wait_event(struct i40e_hw *hw, u16 opcode, + struct i40e_aq_desc *desc); +void i40e_nvmupd_clear_wait_state(struct i40e_hw *hw); void i40e_set_pci_config_data(struct i40e_hw *hw, u16 link_status); +i40e_status i40e_set_mac_type(struct i40e_hw *hw); + extern struct i40e_rx_ptype_decoded i40e_ptype_lookup[]; -static inline struct i40e_rx_ptype_decoded decode_rx_desc_ptype(u8 ptype) +static INLINE struct i40e_rx_ptype_decoded decode_rx_desc_ptype(u8 ptype) { return i40e_ptype_lookup[ptype]; } -/* prototype for functions used for SW locks */ +/** + * i40e_virtchnl_link_speed - Convert AdminQ link_speed to virtchnl definition + * @link_speed: the speed to convert + * + * Returns the link_speed in terms of the virtchnl interface, for use in + * converting link_speed as reported by the AdminQ into the format used for + * talking to virtchnl devices. If we can't represent the link speed properly, + * report LINK_SPEED_UNKNOWN. + **/ +static INLINE enum virtchnl_link_speed +i40e_virtchnl_link_speed(enum i40e_aq_link_speed link_speed) +{ + switch (link_speed) { + case I40E_LINK_SPEED_100MB: + return VIRTCHNL_LINK_SPEED_100MB; + case I40E_LINK_SPEED_1GB: + return VIRTCHNL_LINK_SPEED_1GB; + case I40E_LINK_SPEED_2_5GB: + return VIRTCHNL_LINK_SPEED_2_5GB; + case I40E_LINK_SPEED_5GB: + return VIRTCHNL_LINK_SPEED_5GB; + case I40E_LINK_SPEED_10GB: + return VIRTCHNL_LINK_SPEED_10GB; + case I40E_LINK_SPEED_40GB: + return VIRTCHNL_LINK_SPEED_40GB; + case I40E_LINK_SPEED_20GB: + return VIRTCHNL_LINK_SPEED_20GB; + case I40E_LINK_SPEED_25GB: + return VIRTCHNL_LINK_SPEED_25GB; + case I40E_LINK_SPEED_UNKNOWN: + default: + return VIRTCHNL_LINK_SPEED_UNKNOWN; + } +} +/* prototype for functions used for SW spinlocks */ +void i40e_init_spinlock(struct i40e_spinlock *sp); +void i40e_acquire_spinlock(struct i40e_spinlock *sp); +void i40e_release_spinlock(struct i40e_spinlock *sp); +void i40e_destroy_spinlock(struct i40e_spinlock *sp); /* i40e_common for VF drivers*/ void i40e_vf_parse_hw_config(struct i40e_hw *hw, @@ -346,10 +442,10 @@ i40e_status i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw, struct i40e_control_filter_stats *stats, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_debug_dump(struct i40e_hw *hw, u8 cluster_id, - u8 table_id, u32 start_index, u16 buff_size, - void *buff, u16 *ret_buff_size, - u8 *ret_next_table, u32 *ret_next_index, - struct i40e_asq_cmd_details *cmd_details); + u8 table_id, u32 start_index, u16 buff_size, + void *buff, u16 *ret_buff_size, + u8 *ret_next_table, u32 *ret_next_index, + struct i40e_asq_cmd_details *cmd_details); void i40e_add_filter_to_drop_tx_flow_control_frames(struct i40e_hw *hw, u16 vsi_seid); i40e_status i40e_aq_rx_ctl_read_register(struct i40e_hw *hw, @@ -360,35 +456,84 @@ i40e_status i40e_aq_rx_ctl_write_register(struct i40e_hw *hw, u32 reg_addr, u32 reg_val, struct i40e_asq_cmd_details *cmd_details); void i40e_write_rx_ctl(struct i40e_hw *hw, u32 reg_addr, u32 reg_val); +enum i40e_status_code +i40e_aq_set_phy_register_ext(struct i40e_hw *hw, + u8 phy_select, u8 dev_addr, bool page_change, + bool set_mdio, u8 mdio_num, + u32 reg_addr, u32 reg_val, + struct i40e_asq_cmd_details *cmd_details); +enum i40e_status_code +i40e_aq_get_phy_register_ext(struct i40e_hw *hw, + u8 phy_select, u8 dev_addr, bool page_change, + bool set_mdio, u8 mdio_num, + u32 reg_addr, u32 *reg_val, + struct i40e_asq_cmd_details *cmd_details); + +/* Convenience wrappers for most common use case */ +#define i40e_aq_set_phy_register(hw, ps, da, pc, ra, rv, cd) \ + i40e_aq_set_phy_register_ext(hw, ps, da, pc, false, 0, ra, rv, cd) +#define i40e_aq_get_phy_register(hw, ps, da, pc, ra, rv, cd) \ + i40e_aq_get_phy_register_ext(hw, ps, da, pc, false, 0, ra, rv, cd) + +enum i40e_status_code +i40e_aq_run_phy_activity(struct i40e_hw *hw, u16 activity_id, u32 opcode, + u32 *cmd_status, u32 *data0, u32 *data1, + struct i40e_asq_cmd_details *cmd_details); + +i40e_status i40e_aq_set_arp_proxy_config(struct i40e_hw *hw, + struct i40e_aqc_arp_proxy_data *proxy_config, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_set_ns_proxy_table_entry(struct i40e_hw *hw, + struct i40e_aqc_ns_proxy_data *ns_proxy_table_entry, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_set_clear_wol_filter(struct i40e_hw *hw, + u8 filter_index, + struct i40e_aqc_set_wol_filter_data *filter, + bool set_filter, bool no_wol_tco, + bool filter_valid, bool no_wol_tco_valid, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_get_wake_event_reason(struct i40e_hw *hw, + u16 *wake_reason, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_clear_all_wol_filters(struct i40e_hw *hw, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_read_phy_register_clause22(struct i40e_hw *hw, - u16 reg, u8 phy_addr, u16 *value); + u16 reg, u8 phy_addr, u16 *value); i40e_status i40e_write_phy_register_clause22(struct i40e_hw *hw, - u16 reg, u8 phy_addr, u16 value); + u16 reg, u8 phy_addr, u16 value); i40e_status i40e_read_phy_register_clause45(struct i40e_hw *hw, u8 page, u16 reg, u8 phy_addr, u16 *value); i40e_status i40e_write_phy_register_clause45(struct i40e_hw *hw, u8 page, u16 reg, u8 phy_addr, u16 value); -i40e_status i40e_read_phy_register(struct i40e_hw *hw, u8 page, u16 reg, - u8 phy_addr, u16 *value); -i40e_status i40e_write_phy_register(struct i40e_hw *hw, u8 page, u16 reg, - u8 phy_addr, u16 value); +i40e_status i40e_read_phy_register(struct i40e_hw *hw, + u8 page, u16 reg, u8 phy_addr, u16 *value); +i40e_status i40e_write_phy_register(struct i40e_hw *hw, + u8 page, u16 reg, u8 phy_addr, u16 value); u8 i40e_get_phy_address(struct i40e_hw *hw, u8 dev_num); i40e_status i40e_blink_phy_link_led(struct i40e_hw *hw, - u32 time, u32 interval); -i40e_status i40e_aq_write_ppp(struct i40e_hw *hw, void *buff, - u16 buff_size, u32 track_id, - u32 *error_offset, u32 *error_info, - struct i40e_asq_cmd_details *cmd_details); -i40e_status i40e_aq_get_ppp_list(struct i40e_hw *hw, void *buff, - u16 buff_size, u8 flags, - struct i40e_asq_cmd_details *cmd_details); + u32 time, u32 interval); +i40e_status i40e_aq_write_ddp(struct i40e_hw *hw, void *buff, + u16 buff_size, u32 track_id, + u32 *error_offset, u32 *error_info, + struct i40e_asq_cmd_details * + cmd_details); +i40e_status i40e_aq_get_ddp_list(struct i40e_hw *hw, void *buff, + u16 buff_size, u8 flags, + struct i40e_asq_cmd_details * + cmd_details); struct i40e_generic_seg_header * i40e_find_segment_in_package(u32 segment_type, struct i40e_package_header *pkg_header); +struct i40e_profile_section_header * +i40e_find_section_in_profile(u32 section_type, + struct i40e_profile_segment *profile); enum i40e_status_code i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *i40e_seg, u32 track_id); enum i40e_status_code +i40e_rollback_profile(struct i40e_hw *hw, struct i40e_profile_segment *i40e_seg, + u32 track_id); +enum i40e_status_code i40e_add_pinfo_to_list(struct i40e_hw *hw, struct i40e_profile_segment *profile, u8 *profile_info_sec, u32 track_id); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index d8456c381..44373fb7a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -1,31 +1,15 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ +/* this lets the macros that return timespec64 or structs compile cleanly with + * W=2 + */ +#pragma GCC diagnostic ignored "-Waggregate-return" #include "i40e.h" +#ifdef HAVE_PTP_1588_CLOCK #include +#include + /* The XL710 timesync is very much like Intel's 82599 design when it comes to * the fundamental clock design. However, the clock operations are much simpler @@ -39,14 +23,210 @@ * At 1Gb link, the period is multiplied by 20. (32ns) * 1588 functionality is not supported at 100Mbps. */ -#define I40E_PTP_40GB_INCVAL 0x0199999999ULL -#define I40E_PTP_10GB_INCVAL 0x0333333333ULL -#define I40E_PTP_1GB_INCVAL 0x2000000000ULL +#define I40E_PTP_40GB_INCVAL 0x0199999999ULL +#define I40E_PTP_10GB_INCVAL_MULT 2 +#define I40E_PTP_1GB_INCVAL_MULT 20 +#define I40E_ISGN 0x80000000 #define I40E_PRTTSYN_CTL1_TSYNTYPE_V1 BIT(I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT) #define I40E_PRTTSYN_CTL1_TSYNTYPE_V2 (2 << \ I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT) +#define I40E_SUBDEV_ID_25G_PTP_PIN 0xB +#define to_dev(obj) container_of(obj, struct device, kobj) + +enum i40e_ptp_pin { + SDP3_2 = 0, + SDP3_3, + GPIO_4 +}; + +static struct ptp_pin_desc sdp_desc[] = { +/* name idx func chan */ + {"SDP3_2", SDP3_2, PTP_PF_NONE, 0}, + {"SDP3_3", SDP3_3, PTP_PF_NONE, 1}, + {"GPIO_4", GPIO_4, PTP_PF_NONE, 1}, +}; + +#ifndef HAVE_PTP_1588_CLOCK_PINS +static ssize_t i40e_sysfs_ptp_pins_read(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf); + +static ssize_t i40e_sysfs_ptp_pins_write(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count); + +static struct kobj_attribute ptp_pins_attribute = __ATTR(pins, 0660, + i40e_sysfs_ptp_pins_read, i40e_sysfs_ptp_pins_write); +#endif /* HAVE_PTP_1588_CLOCK_PINS */ + +enum i40e_ptp_gpio_pin_state { + end = -2, + invalid, + off, + in_A, + in_B, + out_A, + out_B, +}; + +static const char * const i40e_ptp_gpio_pin_state2str[] = { + "off", "in_A", "in_B", "out_A", "out_B" +}; + +enum i40e_ptp_led_pin_state { + low = 0, + high, +}; + +struct i40e_ptp_pins_settings { + enum i40e_ptp_gpio_pin_state sdp3_2; + enum i40e_ptp_gpio_pin_state sdp3_3; + enum i40e_ptp_gpio_pin_state gpio_4; + enum i40e_ptp_led_pin_state led2_0; + enum i40e_ptp_led_pin_state led2_1; + enum i40e_ptp_led_pin_state led3_0; + enum i40e_ptp_led_pin_state led3_1; +}; + +static const struct i40e_ptp_pins_settings i40e_ptp_pin_led_allowed_states [] = { + {off, off, off, high, high, high, high}, + {off, in_A, off, high, high, high, low}, + {off, out_A, off, high, low, high, high}, + {off, in_B, off, high, high, high, low}, + {off, out_B, off, high, low, high, high}, + {in_A, off, off, high, high, high, low}, + {in_A, in_B, off, high, high, high, low}, + {in_A, out_B, off, high, low, high, high}, + {out_A, off, off, high, low, high, high}, + {out_A, in_B, off, high, low, high, high}, + {in_B, off, off, high, high, high, low}, + {in_B, in_A, off, high, high, high, low}, + {in_B, out_A, off, high, low, high, high}, + {out_B, off, off, high, low, high, high}, + {out_B, in_A, off, high, low, high, high}, + {off, off, in_A, high, high, low, high}, + {off, out_A, in_A, high, low, low, high}, + {off, in_B, in_A, high, high, low, low}, + {off, out_B, in_A, high, low, low, high}, + {out_A, off, in_A, high, low, low, high}, + {out_A, in_B, in_A, high, low, low, high}, + {in_B, off, in_A, high, high, low, low}, + {in_B, out_A, in_A, high, low, low, high}, + {out_B, off, in_A, high, low, low, high}, + {off, off, out_A, low, high, high, high}, + {off, in_A, out_A, low, high, high, low}, + {off, in_B, out_A, low, high, high, low}, + {off, out_B, out_A, low, low, high, high}, + {in_A, off, out_A, low, high, high, low}, + {in_A, in_B, out_A, low, high, high, low}, + {in_A, out_B, out_A, low, low, high, high}, + {in_B, off, out_A, low, high, high, low}, + {in_B, in_A, out_A, low, high, high, low}, + {out_B, off, out_A, low, low, high, high}, + {out_B, in_A, out_A, low, low, high, high}, + {off, off, in_B, high, high, low, high}, + {off, in_A, in_B, high, high, low, low}, + {off, out_A, in_B, high, low, low, high}, + {off, out_B, in_B, high, low, low, high}, + {in_A, off, in_B, high, high, low, low}, + {in_A, out_B, in_B, high, low, low, high}, + {out_A, off, in_B, high, low, low, high}, + {out_B, off, in_B, high, low, low, high}, + {out_B, in_A, in_B, high, low, low, high}, + {off, off, out_B, low, high, high, high}, + {off, in_A, out_B, low, high, high, low}, + {off, out_A, out_B, low, low, high, high}, + {off, in_B, out_B, low, high, high, low}, + {in_A, off, out_B, low, high, high, low}, + {in_A, in_B, out_B, low, high, high, low}, + {out_A, off, out_B, low, low, high, high}, + {out_A, in_B, out_B, low, low, high, high}, + {in_B, off, out_B, low, high, high, low}, + {in_B, in_A, out_B, low, high, high, low}, + {in_B, out_A, out_B, low, low, high, high}, + {end, end, end, end, end, end, end} +}; + +static int i40e_ptp_set_pins(struct i40e_pf *pf, + struct i40e_ptp_pins_settings *pins); +/** + * i40e_ptp_extts0_work - workqueue task function + * @work: workqueue task structure + * + * Service for PTP external clock event + **/ +void i40e_ptp_extts0_work(struct work_struct *work) +{ + struct i40e_pf *pf = container_of(work, struct i40e_pf, + ptp_extts0_work); + struct i40e_hw *hw = &pf->hw; + struct ptp_clock_event event; + u32 hi, lo; + + /* Event time is captured by one of the two matched registers + * PRTTSYN_EVNT_L: 32 LSB of sampled time event + * PRTTSYN_EVNT_H: 32 MSB of sampled time event + * Event is defined in PRTTSYN_EVNT_0 register + */ + lo = rd32(hw, I40E_PRTTSYN_EVNT_L(0)); + hi = rd32(hw, I40E_PRTTSYN_EVNT_H(0)); + + event.timestamp = (((u64)hi) << 32) | lo; + + event.type = PTP_CLOCK_EXTTS; + event.index = 0; + + /* fire event */ + ptp_clock_event(pf->ptp_clock, &event); +} + +/** + * i40e_is_ptp_pin_dev - check if device supports PTP pins + * @hw: pointer to the hardware structure + * + * Return true if device supports PTP pins, false otherwise. + */ +static bool i40e_is_ptp_pin_dev(const struct i40e_hw* const hw) +{ + return I40E_DEV_ID_25G_SFP28 == hw->device_id && + I40E_SUBDEV_ID_25G_PTP_PIN == hw->subsystem_device_id; +} + +/** + * i40_ptp_reset_timing_events - Reset PTP timing events + * @pf: Board private structure + * + * This function resets timing events for pf. + **/ +static void i40_ptp_reset_timing_events(struct i40e_pf *pf) +{ + u32 i; + + spin_lock_bh(&pf->ptp_rx_lock); + for(i = 0; i <= I40E_PRTTSYN_RXTIME_L_MAX_INDEX; i++ ) { + /* reading and automatically clearing timing events registers */ + rd32(&pf->hw, I40E_PRTTSYN_RXTIME_L(i)); + rd32(&pf->hw, I40E_PRTTSYN_RXTIME_H(i)); + pf->latch_events[i] = 0; + } + /* reading and automatically clearing timing events registers */ + rd32(&pf->hw, I40E_PRTTSYN_TXTIME_L); + rd32(&pf->hw, I40E_PRTTSYN_TXTIME_H); + + pf->tx_hwtstamp_timeouts = 0; + pf->tx_hwtstamp_skipped = 0; + pf->rx_hwtstamp_cleared = 0; + pf->latch_event_flags = 0; + spin_unlock_bh(&pf->ptp_rx_lock); +} + +int i40e_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, + enum ptp_pin_function func, unsigned int chan) +{ + return 0; +} /** * i40e_ptp_read - Read the PHC time from the device * @pf: Board private structure @@ -88,8 +268,8 @@ static void i40e_ptp_write(struct i40e_pf *pf, const struct timespec64 *ts) /* The timer will not update until the high register is written, so * write the low register first. */ - wr32(hw, I40E_PRTTSYN_TIME_L, ns & 0xFFFFFFFF); - wr32(hw, I40E_PRTTSYN_TIME_H, ns >> 32); + wr32(hw, I40E_PRTTSYN_TIME_L, (u32)ns); + wr32(hw, I40E_PRTTSYN_TIME_H, (u32)(ns >> 32)); } /** @@ -129,42 +309,103 @@ static int i40e_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) ppb = -ppb; } - smp_mb(); /* Force any pending update before accessing. */ - adj = ACCESS_ONCE(pf->ptp_base_adj); - - freq = adj; + freq = I40E_PTP_40GB_INCVAL; freq *= ppb; diff = div_u64(freq, 1000000000ULL); if (neg_adj) - adj -= diff; + adj = I40E_PTP_40GB_INCVAL - diff; else - adj += diff; + adj = I40E_PTP_40GB_INCVAL + diff; + + /* At some link speeds, the base incval is so large that directly + * multiplying by ppb would result in arithmetic overflow even when + * using a u64. Avoid this by instead calculating the new incval + * always in terms of the 40GbE clock rate and then multiplying by the + * link speed factor afterwards. This does result in slightly lower + * precision at lower link speeds, but it is fairly minor. + */ + smp_mb(); /* Force any pending update before accessing. */ + adj *= READ_ONCE(pf->ptp_adj_mult); - wr32(hw, I40E_PRTTSYN_INC_L, adj & 0xFFFFFFFF); - wr32(hw, I40E_PRTTSYN_INC_H, adj >> 32); + wr32(hw, I40E_PRTTSYN_INC_L, (u32)adj); + wr32(hw, I40E_PRTTSYN_INC_H, (u32)(adj >> 32)); return 0; } +/** + * i40e_ptp_set_1pps_signal_hw - configure 1PPS PTP signal for pins + * @pf: the PF private data structure + * + * Configure 1PPS signal used for PTP pins + **/ +static void i40e_ptp_set_1pps_signal_hw(struct i40e_pf *pf) +{ + struct i40e_hw *hw = &pf->hw; + struct timespec64 now; + u64 ns; + + wr32(hw, I40E_PRTTSYN_AUX_0(1), 0); + wr32(hw, I40E_PRTTSYN_AUX_1(1), + I40E_PRTTSYN_AUX_1_MAX_INDEX); + wr32(hw, I40E_PRTTSYN_AUX_0(1), + I40E_PRTTSYN_AUX_0_MAX_INDEX); + + i40e_ptp_read(pf, &now); + now.tv_sec += I40E_PTP_2_SEC_DELAY; + now.tv_nsec = 0; + ns = timespec64_to_ns(&now); + + /* I40E_PRTTSYN_TGT_L(1) */ + wr32(hw, I40E_PRTTSYN_TGT_L(1), ns & 0xFFFFFFFF); + /* I40E_PRTTSYN_TGT_H(1) */ + wr32(hw, I40E_PRTTSYN_TGT_H(1), ns >> 32); + wr32(hw, I40E_PRTTSYN_CLKO(1), I40E_PTP_HALF_SECOND); + wr32(hw, I40E_PRTTSYN_AUX_1(1), + I40E_PRTTSYN_AUX_1_MAX_INDEX); + wr32(hw, I40E_PRTTSYN_AUX_0(1), 7); +} + /** * i40e_ptp_adjtime - Adjust the PHC time * @ptp: The PTP clock structure * @delta: Offset in nanoseconds to adjust the PHC time by * - * Adjust the frequency of the PHC by the indicated parts per billion from the - * base frequency. + * Adjust the current clock time by a delta specified in nanoseconds. **/ static int i40e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps); - struct timespec64 now; + struct i40e_hw *hw = &pf->hw; mutex_lock(&pf->tmreg_lock); - i40e_ptp_read(pf, &now); - timespec64_add_ns(&now, delta); - i40e_ptp_write(pf, (const struct timespec64 *)&now); + if (delta > -999999900LL && delta < 999999900LL) { + int neg_adj = 0; + u32 timadj; + u64 tohw; + + if (delta < 0) { + neg_adj = 1; + tohw = -delta; + } else { + tohw = delta; + } + + timadj = tohw & 0x3FFFFFFF; + if (neg_adj) + timadj |= I40E_ISGN; + wr32(hw, I40E_PRTTSYN_ADJ, timadj); + } else { + struct timespec64 then, now; + + then = ns_to_timespec64(delta); + i40e_ptp_read(pf, &now); + now = timespec64_add(now, then); + i40e_ptp_write(pf, (const struct timespec64 *)&now); + i40e_ptp_set_1pps_signal_hw(pf); + } mutex_unlock(&pf->tmreg_lock); @@ -174,7 +415,7 @@ static int i40e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) /** * i40e_ptp_gettime - Get the time of the PHC * @ptp: The PTP clock structure - * @ts: timespec structure to hold the current time value + * @ts: timespec64 structure to hold the current time value * * Read the device clock and return the correct value on ns, after converting it * into a timespec struct. @@ -186,14 +427,13 @@ static int i40e_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) mutex_lock(&pf->tmreg_lock); i40e_ptp_read(pf, ts); mutex_unlock(&pf->tmreg_lock); - return 0; } /** * i40e_ptp_settime - Set the time of the PHC * @ptp: The PTP clock structure - * @ts: timespec structure that holds the new time value + * @ts: timespec64 structure that holds the new time value * * Set the device clock to the user input value. The conversion from timespec * to ns happens in the write function. @@ -206,23 +446,155 @@ static int i40e_ptp_settime(struct ptp_clock_info *ptp, mutex_lock(&pf->tmreg_lock); i40e_ptp_write(pf, ts); mutex_unlock(&pf->tmreg_lock); + return 0; +} + +#ifndef HAVE_PTP_CLOCK_INFO_GETTIME64 +/** + * i40e_ptp_gettime32 - Get the time of the PHC + * @ptp: The PTP clock structure + * @ts: timespec structure to hold the current time value + * + * Read the device clock and return the correct value on ns, after converting it + * into a timespec struct. + **/ +static int i40e_ptp_gettime32(struct ptp_clock_info *ptp, struct timespec *ts) +{ + struct timespec64 ts64; + int err; + err = i40e_ptp_gettime(ptp, &ts64); + if (err) + return err; + + *ts = timespec64_to_timespec(ts64); return 0; } /** - * i40e_ptp_feature_enable - Enable/disable ancillary features of the PHC subsystem + * i40e_ptp_settime32 - Set the time of the PHC * @ptp: The PTP clock structure - * @rq: The requested feature to change - * @on: Enable/disable flag + * @ts: timespec structure that holds the new time value * - * The XL710 does not support any of the ancillary features of the PHC - * subsystem, so this function may just return. + * Set the device clock to the user input value. The conversion from timespec + * to ns happens in the write function. + **/ +static int i40e_ptp_settime32(struct ptp_clock_info *ptp, + const struct timespec *ts) +{ + struct timespec64 ts64 = timespec_to_timespec64(*ts); + + return i40e_ptp_settime(ptp, &ts64); +} +#endif + +static int i40e_pps_configure(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, + int on) +{ + return 0; +} + +enum i40e_ptp_gpio_pin_state i40e_pin_state(int index, int func) +{ + enum i40e_ptp_gpio_pin_state state = off; + + if (index == 0 && func == PTP_PF_EXTTS) + state = in_A; + if (index == 1 && func == PTP_PF_EXTTS) + state = in_B; + if (index == 0 && func == PTP_PF_PEROUT) + state = out_A; + if (index == 1 && func == PTP_PF_PEROUT) + state = out_B; + + return state; +} + +int i40e_ptp_enable_pin(struct i40e_pf *pf, unsigned int chan, + enum ptp_pin_function func, int on) +{ + struct i40e_ptp_pins_settings pins; + enum i40e_ptp_gpio_pin_state *pin = NULL; + int pin_index; + + /* Preserve previous state of pins that we don't touch */ + pins.sdp3_2 = pf->ptp_pins->sdp3_2; + pins.sdp3_3 = pf->ptp_pins->sdp3_3; + pins.gpio_4 = pf->ptp_pins->gpio_4; + + /* If we want to turn on the pin - find the corresponding one based on + * the given index. If we want to turn the function off - we need to + * find which pin had it assigned, we can't use ptp_find_pin here + * because it tries to lock the pincfg_mux which is locked by + * ptp_pin_store() that calls here. + */ + if (on) { + pin_index = ptp_find_pin(pf->ptp_clock, func, chan); + if (pin_index < 0) + return -EBUSY; + + switch (pin_index) { + case SDP3_2: + pin = &pins.sdp3_2; + break; + case SDP3_3: + pin = &pins.sdp3_3; + break; + case GPIO_4: + pin = &pins.gpio_4; + break; + default: + return -EINVAL; + } + + *pin = i40e_pin_state(chan, func); + } else { + if (pins.sdp3_2 == i40e_pin_state(chan, func)) + pins.sdp3_2 = off; + if (pins.sdp3_3 == i40e_pin_state(chan, func)) + pins.sdp3_3 = off; + if (pins.gpio_4 == i40e_pin_state(chan, func)) + pins.gpio_4 = off; + } + + return i40e_ptp_set_pins(pf, &pins) ? -EINVAL : 0; +} + +/** + * i40e_ptp_feature_enable - Enable external clock pins + * @ptp: The PTP clock structure + * @rq: The PTP clock request structure + * @on: To turn feature on/off + * + * Setting on/off PTP PPS feature for pin. **/ static int i40e_ptp_feature_enable(struct ptp_clock_info *ptp, - struct ptp_clock_request *rq, int on) + struct ptp_clock_request *rq, + int on) { - return -EOPNOTSUPP; + struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps); + + enum ptp_pin_function func; + unsigned int chan; + + /* TODO: Implement flags handling for EXTTS and PEROUT */ + switch (rq->type) { + case PTP_CLK_REQ_EXTTS: + func = PTP_PF_EXTTS; + chan = rq->extts.index; + break; + case PTP_CLK_REQ_PEROUT: + func = PTP_PF_PEROUT; + chan = rq->perout.index; + break; + case PTP_CLK_REQ_PPS: + return i40e_pps_configure(ptp, rq, on); + default: + return -EOPNOTSUPP; + } + + return i40e_ptp_enable_pin(pf, chan, func, on); } /** @@ -269,7 +641,6 @@ static u32 i40e_ptp_get_rx_events(struct i40e_pf *pf) /** * i40e_ptp_rx_hang - Detect error case when Rx timestamp registers are hung * @pf: The PF private data structure - * @vsi: The VSI with the rings relevant to 1588 * * This watchdog task is scheduled to detect error case where hardware has * dropped an Rx packet that was timestamped when the ring is full. The @@ -333,10 +704,12 @@ void i40e_ptp_rx_hang(struct i40e_pf *pf) * This watchdog task is run periodically to make sure that we clear the Tx * timestamp logic if we don't obtain a timestamp in a reasonable amount of * time. It is unexpected in the normal case but if it occurs it results in - * permanently prevent timestamps of future packets + * permanently preventing timestamps of future packets. **/ void i40e_ptp_tx_hang(struct i40e_pf *pf) { + struct sk_buff *skb; + if (!(pf->flags & I40E_FLAG_PTP) || !pf->ptp_tx) return; @@ -349,9 +722,12 @@ void i40e_ptp_tx_hang(struct i40e_pf *pf) * within a second it is reasonable to assume that we never will. */ if (time_is_before_jiffies(pf->ptp_tx_start + HZ)) { - dev_kfree_skb_any(pf->ptp_tx_skb); + skb = pf->ptp_tx_skb; pf->ptp_tx_skb = NULL; clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state); + + /* Free the skb after we clear the bitlock */ + dev_kfree_skb_any(skb); pf->tx_hwtstamp_timeouts++; } } @@ -461,6 +837,7 @@ void i40e_ptp_set_increment(struct i40e_pf *pf) struct i40e_link_status *hw_link_info; struct i40e_hw *hw = &pf->hw; u64 incval; + u32 mult; hw_link_info = &hw->phy.link_info; @@ -468,10 +845,10 @@ void i40e_ptp_set_increment(struct i40e_pf *pf) switch (hw_link_info->link_speed) { case I40E_LINK_SPEED_10GB: - incval = I40E_PTP_10GB_INCVAL; + mult = I40E_PTP_10GB_INCVAL_MULT; break; case I40E_LINK_SPEED_1GB: - incval = I40E_PTP_1GB_INCVAL; + mult = I40E_PTP_1GB_INCVAL_MULT; break; case I40E_LINK_SPEED_100MB: { @@ -482,31 +859,36 @@ void i40e_ptp_set_increment(struct i40e_pf *pf) "1588 functionality is not supported at 100 Mbps. Stopping the PHC.\n"); warn_once++; } - incval = 0; + mult = 0; break; } case I40E_LINK_SPEED_40GB: default: - incval = I40E_PTP_40GB_INCVAL; + mult = 1; break; } + /* The increment value is calculated by taking the base 40GbE incvalue + * and multiplying it by a factor based on the link speed. + */ + incval = I40E_PTP_40GB_INCVAL * mult; + /* Write the new increment value into the increment register. The * hardware will not update the clock until both registers have been * written. */ - wr32(hw, I40E_PRTTSYN_INC_L, incval & 0xFFFFFFFF); - wr32(hw, I40E_PRTTSYN_INC_H, incval >> 32); + wr32(hw, I40E_PRTTSYN_INC_L, (u32)incval); + wr32(hw, I40E_PRTTSYN_INC_H, (u32)(incval >> 32)); /* Update the base adjustement value. */ - ACCESS_ONCE(pf->ptp_base_adj) = incval; + WRITE_ONCE(pf->ptp_adj_mult, mult); smp_mb(); /* Force the above update. */ } /** * i40e_ptp_get_ts_config - ioctl interface to read the HW timestamping * @pf: Board private structure - * @ifreq: ioctl data + * @ifr: ioctl data * * Obtain the current hardware timestamping settigs as requested. To do this, * keep a shadow copy of the timestamp settings rather than attempting to @@ -523,6 +905,296 @@ int i40e_ptp_get_ts_config(struct i40e_pf *pf, struct ifreq *ifr) -EFAULT : 0; } +/** + * i40e_ptp_free_pins - free memory used by PTP pins + * @pf: Board private structure + * + * Release memory allocated for PTP pins. + **/ +void i40e_ptp_free_pins(struct i40e_pf *pf) { + if (pf->hw.pf_id == 0 && i40e_is_ptp_pin_dev(&pf->hw)) { + kfree(pf->ptp_pins); + kfree(pf->ptp_caps.pin_config); + pf->ptp_pins = NULL; + kobject_put(pf->ptp_kobj); + } +} + +/** + * i40e_ptp_set_pin_out_hw - Set HW GPIO pin, cares only for outputs + * @pf: Board private structure + * + * This function sets GPIO pin for PTP, cares only for outputs + **/ +static void i40e_ptp_set_pin_out_hw(struct i40e_pf *pf, + unsigned int pin, + enum i40e_ptp_gpio_pin_state state) +{ + struct i40e_hw *hw = &pf->hw; + + switch (state) { + case out_A: + wr32(hw, I40E_GLGEN_GPIO_CTL(pin), + I40E_PORT_0_OUT_HIGH_TIMESYNC_0); + break; + case out_B: + wr32(hw, I40E_GLGEN_GPIO_CTL(pin), + I40E_PORT_1_OUT_HIGH_TIMESYNC_1); + break; + default: + break; + } +} +/** + * i40e_ptp_set_pin_in_hw - Set HW GPIO pin, cares only for inputs + * @pf: Board private structure + * + * This function sets GPIO pin for PTP, cares only for inputs + **/ +static void i40e_ptp_set_pin_in_hw(struct i40e_pf *pf, + unsigned int pin, + enum i40e_ptp_gpio_pin_state state) +{ + struct i40e_hw *hw = &pf->hw; + + switch (state) { + case off: + wr32(hw, I40E_GLGEN_GPIO_CTL(pin), 0); + break; + case in_A: + wr32(hw, I40E_GLGEN_GPIO_CTL(pin), I40E_PORT_0_TIMESYNC_1); + break; + case in_B: + wr32(hw, I40E_GLGEN_GPIO_CTL(pin), I40E_PORT_1_TIMESYNC_1); + break; + default: + break; + } +} + +/** + * i40e_ptp_set_pins_hw - Set HW GPIO pins + * @pf: Board private structure + * + * This function sets GPIO pins for PTP + **/ +static void i40e_ptp_set_pins_hw(struct i40e_pf *pf) +{ + const struct i40e_ptp_pins_settings *pins = pf->ptp_pins; + struct i40e_hw *hw = &pf->hw; + + if (!i40e_is_ptp_pin_dev(hw)) { + dev_warn(&pf->pdev->dev, + "PTP external clock not supported.\n"); + return; + } + + if (!pins || pf->hw.pf_id) { + dev_warn(&pf->pdev->dev, + "PTP PIN setting allowed for PF0 only.\n"); + return; + } + + /* setting SDP PTP pins first to the low/off state */ + i40e_ptp_set_pin_in_hw(pf, I40E_SDP3_2, off); + i40e_ptp_set_pin_in_hw(pf, I40E_SDP3_3, off); + i40e_ptp_set_pin_in_hw(pf, I40E_GPIO_4, off); + + i40e_ptp_set_pin_out_hw(pf, I40E_SDP3_2, pins->sdp3_2); + i40e_ptp_set_pin_out_hw(pf, I40E_SDP3_3, pins->sdp3_3); + i40e_ptp_set_pin_out_hw(pf, I40E_GPIO_4, pins->gpio_4); + + i40e_ptp_set_pin_in_hw(pf, I40E_SDP3_2, pins->sdp3_2); + i40e_ptp_set_pin_in_hw(pf, I40E_SDP3_3, pins->sdp3_3); + i40e_ptp_set_pin_in_hw(pf, I40E_GPIO_4, pins->gpio_4); + + switch (pf->ptp_pins->led2_0) { + case low: wr32(hw, I40E_GLGEN_GPIO_SET,I40E_DRIVE_SDP_ON | + I40E_LED2_0); break; + case high: wr32(hw, I40E_GLGEN_GPIO_SET,I40E_DRIVE_SDP_ON | + I40E_GPIO_SET_HIGH | I40E_LED2_0); break; + } + switch (pf->ptp_pins->led2_1) { + case low: wr32(hw, I40E_GLGEN_GPIO_SET,I40E_DRIVE_SDP_ON | + I40E_LED2_1); break; + case high: wr32(hw, I40E_GLGEN_GPIO_SET,I40E_DRIVE_SDP_ON | + I40E_GPIO_SET_HIGH | I40E_LED2_1); break; + } + switch (pf->ptp_pins->led3_0) { + case low: wr32(hw, I40E_GLGEN_GPIO_SET,I40E_DRIVE_SDP_ON | + I40E_LED3_0); break; + case high: wr32(hw, I40E_GLGEN_GPIO_SET,I40E_DRIVE_SDP_ON | + I40E_GPIO_SET_HIGH | I40E_LED3_0); break; + } + switch (pf->ptp_pins->led3_1) { + case low: wr32(hw, I40E_GLGEN_GPIO_SET,I40E_DRIVE_SDP_ON | + I40E_LED3_1); break; + case high: wr32(hw, I40E_GLGEN_GPIO_SET,I40E_DRIVE_SDP_ON | + I40E_GPIO_SET_HIGH | I40E_LED3_1); break; + } + + dev_info(&pf->pdev->dev, + "PTP configuration set to: SDP3_2: %s, SDP3_3: %s, GPIO_4: %s\n", + i40e_ptp_gpio_pin_state2str[pins->sdp3_2], + i40e_ptp_gpio_pin_state2str[pins->sdp3_3], + i40e_ptp_gpio_pin_state2str[pins->gpio_4]); +} + +/** + * i40e_ptp_set_pins - set PTP pins in HW + * @pf: Board private structure + * @pins: PTP pins to be applied + * + * Validate and set PTP pins in HW for specific PF. + * Return 0 on success or negative value on error. + **/ +static int i40e_ptp_set_pins(struct i40e_pf *pf, + struct i40e_ptp_pins_settings *pins) +{ + int i = 0; + + if (!i40e_is_ptp_pin_dev(&pf->hw)) { + dev_warn(&pf->pdev->dev, + "PTP external clock not supported.\n"); + return -ENOTSUPP; + } + + if (!pf->ptp_pins || pf->hw.pf_id) { + dev_warn(&pf->pdev->dev, + "PTP PIN setting allowed for PF0 only.\n"); + return -ENOTSUPP; + } + + if (pins->sdp3_2 == invalid) + pins->sdp3_2 = pf->ptp_pins->sdp3_2; + if (pins->sdp3_3 == invalid) + pins->sdp3_3 = pf->ptp_pins->sdp3_3; + if (pins->gpio_4 == invalid) + pins->gpio_4 = pf->ptp_pins->gpio_4; + while (i40e_ptp_pin_led_allowed_states[i].sdp3_2 != end) { + if (pins->sdp3_2 == i40e_ptp_pin_led_allowed_states[i].sdp3_2 && + pins->sdp3_3 == i40e_ptp_pin_led_allowed_states[i].sdp3_3 && + pins->gpio_4 == i40e_ptp_pin_led_allowed_states[i].gpio_4) { + pins->led2_0 = i40e_ptp_pin_led_allowed_states[i].led2_0; + pins->led2_1 = i40e_ptp_pin_led_allowed_states[i].led2_1; + pins->led3_0 = i40e_ptp_pin_led_allowed_states[i].led3_0; + pins->led3_1 = i40e_ptp_pin_led_allowed_states[i].led3_1; + break; + } + i++; + } + if (i40e_ptp_pin_led_allowed_states[i].sdp3_2 == end) { + dev_warn(&pf->pdev->dev, + "Unsupported PTP pin configuration: SDP3_2: %s, SDP3_3: %s, GPIO_4: %s\n", + i40e_ptp_gpio_pin_state2str[pins->sdp3_2], + i40e_ptp_gpio_pin_state2str[pins->sdp3_3], + i40e_ptp_gpio_pin_state2str[pins->gpio_4]); + + return -EPERM; + } + memcpy(pf->ptp_pins, pins, sizeof(*pins)); + i40e_ptp_set_pins_hw(pf); + i40_ptp_reset_timing_events(pf); + + return 0; +} + +/** + * i40e_ptp_set_pins_ioctl - ioctl interface to set the HW timestamping + * gpio pins + * @pf: board private structure + * @ifr: ioctl data + * + * Set the current hardware timestamping pins for current PF. + **/ +int i40e_ptp_set_pins_ioctl(struct i40e_pf *pf, struct ifreq *ifr) +{ + struct i40e_ptp_pins_settings pins; + int err; + + if (!i40e_is_ptp_pin_dev(&pf->hw)) { + dev_warn(&pf->pdev->dev, + "PTP external clock not supported.\n"); + return -ENOTSUPP; + } + + if (!pf->ptp_pins || pf->hw.pf_id) { + dev_warn(&pf->pdev->dev, + "PTP PIN setting allowed for PF0 only.\n"); + return -ENOTSUPP; + } + + err = copy_from_user(&pins, ifr->ifr_data, sizeof(pins)); + if (err) { + dev_warn(&pf->pdev->dev, "Cannot read user data during SIOCSPINS ioctl\n"); + return -EIO; + } + + return i40e_ptp_set_pins(pf, &pins); +} + +/** + * i40e_ptp_alloc_pins - allocate PTP pins structure + * @pf: Board private structure + * + * allocate PTP pins structure + **/ +int i40e_ptp_alloc_pins(struct i40e_pf *pf) +{ + dev_info(&pf->pdev->dev, + "PTP subsystem device ID: %d\n", pf->hw.subsystem_device_id); + + if (pf->hw.pf_id || !i40e_is_ptp_pin_dev(&pf->hw)) + return 0; + + pf->ptp_pins = kzalloc(sizeof(struct i40e_ptp_pins_settings), GFP_KERNEL); + + if (!pf->ptp_pins) { + dev_warn(&pf->pdev->dev, "Cannot allocate memory for PTP pins structure\n"); + return -I40E_ERR_NO_MEMORY; + } + + pf->ptp_pins->sdp3_2 = off; + pf->ptp_pins->sdp3_3 = off; + pf->ptp_pins->gpio_4 = off; + pf->ptp_pins->led2_0 = high; + pf->ptp_pins->led2_1 = high; + pf->ptp_pins->led3_0 = high; + pf->ptp_pins->led3_1 = high; + + i40e_ptp_set_pins_hw(pf); + + return 0; +} + + +/** + * i40e_ptp_get_pins - ioctl interface to read the HW timestamping gpio pins + * @pf: Board private structure + * @ifr: ioctl data + * + * Obtain the current hardware timestamping gpio pins settigs as requested. + **/ +int i40e_ptp_get_pins(struct i40e_pf *pf, struct ifreq *ifr) +{ + if (!i40e_is_ptp_pin_dev(&pf->hw)) { + dev_warn(&pf->pdev->dev, + "PTP external clock not supported.\n"); + return -ENOTSUPP; + } + + if (!pf->ptp_pins || pf->hw.pf_id) { + dev_warn(&pf->pdev->dev, + "PTP PIN reading allowed for PF0 only.\n"); + return -ENOTSUPP; + } + + return copy_to_user(ifr->ifr_data, pf->ptp_pins, + sizeof(*(pf->ptp_pins))) + ? -EFAULT + : 0; +} + /** * i40e_ptp_set_timestamp_mode - setup hardware for requested timestamp mode * @pf: Board private structure @@ -541,6 +1213,21 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, struct i40e_hw *hw = &pf->hw; u32 tsyntype, regval; + /* Selects external trigger to cause event */ + regval = rd32(hw, I40E_PRTTSYN_AUX_0(0)); + /* Bit 17:16 is EVNTLVL, 01B rising edge */ + regval &= 0; + regval |= (1 << I40E_PRTTSYN_AUX_0_EVNTLVL_SHIFT); + /* regval: 0001 0000 0000 0000 0000 */ + wr32(hw, I40E_PRTTSYN_AUX_0(0), regval); + + /* Enabel interrupts */ + regval = rd32(hw, I40E_PRTTSYN_CTL0); + regval |= 1 << I40E_PRTTSYN_CTL0_EVENT_INT_ENA_SHIFT; + wr32(hw, I40E_PRTTSYN_CTL0, regval); + + INIT_WORK(&pf->ptp_extts0_work, i40e_ptp_extts0_work); + /* Reserved for future extensions. */ if (config->flags) return -EINVAL; @@ -599,7 +1286,9 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; } break; +#ifdef HAVE_HWTSTAMP_FILTER_NTP_ALL case HWTSTAMP_FILTER_NTP_ALL: +#endif /* HAVE_HWTSTAMP_FILTER_NTP_ALL */ case HWTSTAMP_FILTER_ALL: default: return -ERANGE; @@ -650,7 +1339,7 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, /** * i40e_ptp_set_ts_config - ioctl interface to control the HW timestamping * @pf: Board private structure - * @ifreq: ioctl data + * @ifr: ioctl data * * Respond to the user filter requests and make the appropriate hardware * changes here. The XL710 cannot support splitting of the Tx/Rx timestamping @@ -683,6 +1372,41 @@ int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr) -EFAULT : 0; } +static int i40e_init_pin_config(struct i40e_pf *pf) +{ + int i; + + if (pf->hw.pf_id != 0) + return -ENOTSUPP; + + pf->ptp_caps.n_pins = 3; + pf->ptp_caps.n_ext_ts = 2; + pf->ptp_caps.pps = 1; + pf->ptp_caps.n_per_out = 2; + + pf->ptp_caps.pin_config = kcalloc(pf->ptp_caps.n_pins, + sizeof(*pf->ptp_caps.pin_config), + GFP_KERNEL); + if (!pf->ptp_caps.pin_config) + return -ENOMEM; + + for (i = 0; i < pf->ptp_caps.n_pins; i++) { + snprintf(pf->ptp_caps.pin_config[i].name, + sizeof(pf->ptp_caps.pin_config[i].name), + "%s", sdp_desc[i].name); + pf->ptp_caps.pin_config[i].index = sdp_desc[i].index; + pf->ptp_caps.pin_config[i].func = PTP_PF_NONE; + pf->ptp_caps.pin_config[i].chan = sdp_desc[i].chan; + } + + pf->ptp_caps.verify = i40e_ptp_verify; + pf->ptp_caps.enable = i40e_ptp_feature_enable; + + pf->ptp_caps.pps = 1; + + return 0; +} + /** * i40e_ptp_create_clock - Create PTP clock device for userspace * @pf: Board private structure @@ -695,20 +1419,29 @@ int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr) **/ static long i40e_ptp_create_clock(struct i40e_pf *pf) { + int err; /* no need to create a clock device if we already have one */ if (!IS_ERR_OR_NULL(pf->ptp_clock)) return 0; - strncpy(pf->ptp_caps.name, i40e_driver_name, sizeof(pf->ptp_caps.name)); + strlcpy(pf->ptp_caps.name, i40e_driver_name, + sizeof(pf->ptp_caps.name) - 1); pf->ptp_caps.owner = THIS_MODULE; pf->ptp_caps.max_adj = 999999999; - pf->ptp_caps.n_ext_ts = 0; - pf->ptp_caps.pps = 0; pf->ptp_caps.adjfreq = i40e_ptp_adjfreq; pf->ptp_caps.adjtime = i40e_ptp_adjtime; +#ifdef HAVE_PTP_CLOCK_INFO_GETTIME64 pf->ptp_caps.gettime64 = i40e_ptp_gettime; pf->ptp_caps.settime64 = i40e_ptp_settime; - pf->ptp_caps.enable = i40e_ptp_feature_enable; +#else + pf->ptp_caps.gettime = i40e_ptp_gettime32; + pf->ptp_caps.settime = i40e_ptp_settime32; +#endif + if (i40e_is_ptp_pin_dev(&pf->hw)) { + err = i40e_init_pin_config(pf); + if (err) + return err; + } /* Attempt to register the clock before enabling the hardware. */ pf->ptp_clock = ptp_clock_register(&pf->ptp_caps, &pf->pdev->dev); @@ -722,9 +1455,202 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf) pf->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; pf->tstamp_config.tx_type = HWTSTAMP_TX_OFF; + /* Set the previous "reset" time to the current Kernel clock time */ + pf->ptp_prev_hw_time = ktime_to_timespec64(ktime_get_real()); + pf->ptp_reset_start = ktime_get(); + return 0; } +/** + * i40e_ptp_save_hw_time - Save the current PTP time as ptp_prev_hw_time + * @pf: Board private structure + * + * Read the current PTP time and save it into pf->ptp_prev_hw_time. This should + * be called at the end of preparing to reset, just before hardware reset + * occurs, in order to preserve the PTP time as close as possible across + * resets. + */ +void i40e_ptp_save_hw_time(struct i40e_pf *pf) +{ + /* don't try to access the PTP clock if it's not enabled */ + if (!(pf->flags & I40E_FLAG_PTP)) + return; + + i40e_ptp_gettime(&pf->ptp_caps, &pf->ptp_prev_hw_time); + /* Get a monotonic starting time for this reset */ + pf->ptp_reset_start = ktime_get(); +} + +/** + * i40e_ptp_restore_hw_time - Restore the ptp_prev_hw_time + delta to PTP regs + * @pf: Board private structure + * + * Restore the PTP hardware clock registers. We previously cached the PTP + * hardware time as pf->ptp_prev_hw_time. To be as accurate as possible, + * update this value based on the time delta since the time was saved, using + * CLOCK_MONOTONIC (via ktime_get()) to calculate the time difference. + * + * This ensures that the hardware clock is restored to nearly what it should + * have been if a reset had not occurred. + */ +void i40e_ptp_restore_hw_time(struct i40e_pf *pf) +{ + ktime_t delta = ktime_sub(ktime_get(), pf->ptp_reset_start); + + /* Update the previous HW time with the ktime delta */ + timespec64_add_ns(&pf->ptp_prev_hw_time, ktime_to_ns(delta)); + + /* Restore the hardware clock registers */ + i40e_ptp_settime(&pf->ptp_caps, &pf->ptp_prev_hw_time); +} + +#ifndef HAVE_PTP_1588_CLOCK_PINS +/** + * __get_pf_pdev - helper function to get the pdev + * @kobj: kobject passed + * @pdev: PCI device information struct + */ +static int __get_pf_pdev(struct kobject *kobj, struct pci_dev **pdev) +{ + struct device *dev; + + if (!kobj->parent) + return -EINVAL; + + /* get pdev */ + dev = to_dev(kobj->parent); + *pdev = to_pci_dev(dev); + + return 0; +} + +/** + * i40e_ptp_pins_to_num - convert PTP pins to integer number + * @pf: PCI physical function + * + * Return PTP pins states from pf as integer number. + **/ +static unsigned int i40e_ptp_pins_to_num(struct i40e_pf *pf) +{ + return pf->ptp_pins->gpio_4 + + pf->ptp_pins->sdp3_3 * 10 + + pf->ptp_pins->sdp3_2 * 100; +} + +/** + * i40e_ptp_set_pins_str - wrapper to set PTP pins in HW from string + * @pf: Board private structure + * @buf: string with PTP pins to be applied + * @count: length of a buf argument + * + * Set the current hardware timestamping pins for current PF. + * Return 0 on success and negative value on error. + **/ +static int i40e_ptp_set_pins_str(struct i40e_pf *pf, const char* buf, + int count) +{ + struct i40e_ptp_pins_settings pins; + const int PIN_STR_LEN = 4; + unsigned long res; + + if (count != PIN_STR_LEN || kstrtoul(buf, 10, &res)) + return -EINVAL; + + pins.sdp3_2 = res / 100 % 10; + pins.sdp3_3 = res / 10 % 10; + pins.gpio_4 = res % 10; + + if (pins.sdp3_2 > out_B || + pins.sdp3_3 > out_B || + pins.gpio_4 > out_B) + return -EINVAL; + + return i40e_ptp_set_pins(pf, &pins) ? -EINVAL : count; +} + + + +/** + * i40e_sysfs_ptp_pins_read - sysfs interface for reading PTP pins status + * @kobj: sysfs node + * @attr: sysfs node attributes + * @buf: string representing PTP pins + * + * Return number of bytes read on success or negative value on failure. + **/ +static ssize_t i40e_sysfs_ptp_pins_read(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct pci_dev *pdev; + struct i40e_pf *pf; + unsigned int pins; + + if(__get_pf_pdev(kobj, &pdev)) + return -EPERM; + + pf = pci_get_drvdata(pdev); + pins = i40e_ptp_pins_to_num(pf); + + dev_info(&pf->pdev->dev, + "PTP pins: SDP3_2: %s, SDP3_3: %s, GPIO_4: %s\n", + i40e_ptp_gpio_pin_state2str[pf->ptp_pins->sdp3_2], + i40e_ptp_gpio_pin_state2str[pf->ptp_pins->sdp3_3], + i40e_ptp_gpio_pin_state2str[pf->ptp_pins->gpio_4]); + + return sprintf(buf, "%.3d\n", pins); +} + +/** + * i40e_sysfs_ptp_pins_write - sysfs interface for setting PTP pins in HW + * @kobj: sysfs node + * @attr: sysfs node attributes + * @buf: string representing PTP pins + * @count: length of a 'buf' string + * + * Return number of bytes written on success or negative value on failure. + **/ +static ssize_t i40e_sysfs_ptp_pins_write(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev; + struct i40e_pf *pf; + + if(__get_pf_pdev(kobj, &pdev)) + return -EPERM; + + pf = pci_get_drvdata(pdev); + + return i40e_ptp_set_pins_str(pf, buf, count); +} + +/** + * i40e_ptp_pins_sysfs_init - initialize sysfs for PTP pins + * @pf: board private structure + * + * Initialize sysfs for handling PTP timestamping pins in HW. + **/ +static void i40e_ptp_pins_sysfs_init(struct i40e_pf *pf) +{ + if (pf->hw.pf_id != 0 || !i40e_is_ptp_pin_dev(&pf->hw)) + return; + + pf->ptp_kobj = kobject_create_and_add("ptp_pins", &pf->pdev->dev.kobj); + if(!pf->ptp_kobj) { + dev_info(&pf->pdev->dev, "Failed to create ptp_pins kobject\n"); + return; + } + + if (sysfs_create_file(pf->ptp_kobj, &ptp_pins_attribute.attr)) { + dev_info(&pf->pdev->dev, "Failed to create PTP pins kobject\n"); + kobject_put(pf->ptp_kobj); + return; + } +} +#endif /* HAVE_PTP_1588_CLOCK_PINS */ + /** * i40e_ptp_init - Initialize the 1588 support after device probe or reset * @pf: Board private structure @@ -732,10 +1658,14 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf) * This function sets device up for 1588 support. The first time it is run, it * will create a PHC clock device. It does not create a clock device if one * already exists. It also reconfigures the device after a reset. + * + * The first time a clock is created, i40e_ptp_create_clock will set + * pf->ptp_prev_hw_time to the current system time. During resets, it is + * expected that this timespec will be set to the last known PTP clock time, + * in order to preserve the clock time as close as possible across a reset. **/ void i40e_ptp_init(struct i40e_pf *pf) { - struct net_device *netdev = pf->vsi[pf->lan_vsi]->netdev; struct i40e_hw *hw = &pf->hw; u32 pf_id; long err; @@ -747,9 +1677,7 @@ void i40e_ptp_init(struct i40e_pf *pf) I40E_PRTTSYN_CTL0_PF_ID_SHIFT; if (hw->pf_id != pf_id) { pf->flags &= ~I40E_FLAG_PTP; - dev_info(&pf->pdev->dev, "%s: PTP not supported on %s\n", - __func__, - netdev->name); + dev_info(&pf->pdev->dev, "PTP not supported on this device\n"); return; } @@ -760,10 +1688,9 @@ void i40e_ptp_init(struct i40e_pf *pf) err = i40e_ptp_create_clock(pf); if (err) { pf->ptp_clock = NULL; - dev_err(&pf->pdev->dev, "%s: ptp_clock_register failed\n", - __func__); - } else if (pf->ptp_clock) { - struct timespec64 ts; + dev_err(&pf->pdev->dev, + "PTP clock register failed: %ld\n", err); + } else { u32 regval; if (pf->hw.debug_mask & I40E_DEBUG_LAN) @@ -784,10 +1711,15 @@ void i40e_ptp_init(struct i40e_pf *pf) /* reset timestamping mode */ i40e_ptp_set_timestamp_mode(pf, &pf->tstamp_config); - /* Set the clock value. */ - ts = ktime_to_timespec64(ktime_get_real()); - i40e_ptp_settime(&pf->ptp_caps, &ts); + /* Restore the clock time based on last known value */ + i40e_ptp_restore_hw_time(pf); } + +#ifndef HAVE_PTP_1588_CLOCK_PINS + i40e_ptp_pins_sysfs_init(pf); +#endif /* HAVE_PTP_1588_CLOCK_PINS */ + + i40e_ptp_set_1pps_signal_hw(pf); } /** @@ -799,20 +1731,42 @@ void i40e_ptp_init(struct i40e_pf *pf) **/ void i40e_ptp_stop(struct i40e_pf *pf) { + struct i40e_hw *hw = &pf->hw; + u32 regval; + pf->flags &= ~I40E_FLAG_PTP; pf->ptp_tx = false; pf->ptp_rx = false; if (pf->ptp_tx_skb) { - dev_kfree_skb_any(pf->ptp_tx_skb); + struct sk_buff *skb = pf->ptp_tx_skb; + pf->ptp_tx_skb = NULL; clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state); + dev_kfree_skb_any(skb); } if (pf->ptp_clock) { ptp_clock_unregister(pf->ptp_clock); pf->ptp_clock = NULL; - dev_info(&pf->pdev->dev, "%s: removed PHC on %s\n", __func__, + dev_info(&pf->pdev->dev, "removed PHC from %s\n", pf->vsi[pf->lan_vsi]->netdev->name); } + + /* Set GPIO4 as an input */ + wr32(hw, I40E_GLGEN_GPIO_CTL(I40E_SDP3_2), 0x0); + wr32(hw, I40E_GLGEN_GPIO_CTL(I40E_SDP3_3), 0x0); + wr32(hw, I40E_GLGEN_GPIO_CTL(I40E_GPIO_4), 0x0); + + regval = rd32(hw, I40E_PRTTSYN_AUX_0(0)); + regval &= ~I40E_PRTTSYN_AUX_0_PTPFLAG_MASK; + wr32(hw, I40E_PRTTSYN_AUX_0(0), regval); + + /* Disable interrupts */ + regval = rd32(hw, I40E_PRTTSYN_CTL0); + regval &= I40E_PRTTSYN_CTL0_FFFB_MASK; + wr32(hw, I40E_PRTTSYN_CTL0, regval); + + i40e_ptp_free_pins(pf); } +#endif /* HAVE_PTP_1588_CLOCK */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_register.h b/drivers/net/ethernet/intel/i40e/i40e_register.h index 86ca27f72..955611c14 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_register.h +++ b/drivers/net/ethernet/intel/i40e/i40e_register.h @@ -1,51 +1,28 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_REGISTER_H_ #define _I40E_REGISTER_H_ -#define I40E_GL_ARQBAH 0x000801C0 /* Reset: EMPR */ +#define I40E_GL_ARQBAH 0x000801C0 /* Reset: EMPR */ #define I40E_GL_ARQBAH_ARQBAH_SHIFT 0 -#define I40E_GL_ARQBAH_ARQBAH_MASK I40E_MASK(0xFFFFFFFF, I40E_GL_ARQBAH_ARQBAH_SHIFT) -#define I40E_GL_ARQBAL 0x000800C0 /* Reset: EMPR */ +#define I40E_GL_ARQBAH_ARQBAH_MASK I40E_MASK(0xFFFFFFFF, I40E_GL_ARQBAH_ARQBAH_SHIFT) +#define I40E_GL_ARQBAL 0x000800C0 /* Reset: EMPR */ #define I40E_GL_ARQBAL_ARQBAL_SHIFT 0 -#define I40E_GL_ARQBAL_ARQBAL_MASK I40E_MASK(0xFFFFFFFF, I40E_GL_ARQBAL_ARQBAL_SHIFT) -#define I40E_GL_ARQH 0x000803C0 /* Reset: EMPR */ +#define I40E_GL_ARQBAL_ARQBAL_MASK I40E_MASK(0xFFFFFFFF, I40E_GL_ARQBAL_ARQBAL_SHIFT) +#define I40E_GL_ARQH 0x000803C0 /* Reset: EMPR */ #define I40E_GL_ARQH_ARQH_SHIFT 0 -#define I40E_GL_ARQH_ARQH_MASK I40E_MASK(0x3FF, I40E_GL_ARQH_ARQH_SHIFT) -#define I40E_GL_ARQT 0x000804C0 /* Reset: EMPR */ +#define I40E_GL_ARQH_ARQH_MASK I40E_MASK(0x3FF, I40E_GL_ARQH_ARQH_SHIFT) +#define I40E_GL_ARQT 0x000804C0 /* Reset: EMPR */ #define I40E_GL_ARQT_ARQT_SHIFT 0 -#define I40E_GL_ARQT_ARQT_MASK I40E_MASK(0x3FF, I40E_GL_ARQT_ARQT_SHIFT) -#define I40E_GL_ATQBAH 0x00080140 /* Reset: EMPR */ +#define I40E_GL_ARQT_ARQT_MASK I40E_MASK(0x3FF, I40E_GL_ARQT_ARQT_SHIFT) +#define I40E_GL_ATQBAH 0x00080140 /* Reset: EMPR */ #define I40E_GL_ATQBAH_ATQBAH_SHIFT 0 -#define I40E_GL_ATQBAH_ATQBAH_MASK I40E_MASK(0xFFFFFFFF, I40E_GL_ATQBAH_ATQBAH_SHIFT) -#define I40E_GL_ATQBAL 0x00080040 /* Reset: EMPR */ +#define I40E_GL_ATQBAH_ATQBAH_MASK I40E_MASK(0xFFFFFFFF, I40E_GL_ATQBAH_ATQBAH_SHIFT) +#define I40E_GL_ATQBAL 0x00080040 /* Reset: EMPR */ #define I40E_GL_ATQBAL_ATQBAL_SHIFT 0 -#define I40E_GL_ATQBAL_ATQBAL_MASK I40E_MASK(0xFFFFFFFF, I40E_GL_ATQBAL_ATQBAL_SHIFT) -#define I40E_GL_ATQH 0x00080340 /* Reset: EMPR */ +#define I40E_GL_ATQBAL_ATQBAL_MASK I40E_MASK(0xFFFFFFFF, I40E_GL_ATQBAL_ATQBAL_SHIFT) +#define I40E_GL_ATQH 0x00080340 /* Reset: EMPR */ #define I40E_GL_ATQH_ATQH_SHIFT 0 #define I40E_GL_ATQH_ATQH_MASK I40E_MASK(0x3FF, I40E_GL_ATQH_ATQH_SHIFT) #define I40E_GL_ATQLEN 0x00080240 /* Reset: EMPR */ @@ -81,7 +58,7 @@ #define I40E_PF_ARQLEN_ARQCRIT_SHIFT 30 #define I40E_PF_ARQLEN_ARQCRIT_MASK I40E_MASK(0x1, I40E_PF_ARQLEN_ARQCRIT_SHIFT) #define I40E_PF_ARQLEN_ARQENABLE_SHIFT 31 -#define I40E_PF_ARQLEN_ARQENABLE_MASK I40E_MASK(0x1, I40E_PF_ARQLEN_ARQENABLE_SHIFT) +#define I40E_PF_ARQLEN_ARQENABLE_MASK I40E_MASK(0x1u, I40E_PF_ARQLEN_ARQENABLE_SHIFT) #define I40E_PF_ARQT 0x00080480 /* Reset: EMPR */ #define I40E_PF_ARQT_ARQT_SHIFT 0 #define I40E_PF_ARQT_ARQT_MASK I40E_MASK(0x3FF, I40E_PF_ARQT_ARQT_SHIFT) @@ -104,7 +81,7 @@ #define I40E_PF_ATQLEN_ATQCRIT_SHIFT 30 #define I40E_PF_ATQLEN_ATQCRIT_MASK I40E_MASK(0x1, I40E_PF_ATQLEN_ATQCRIT_SHIFT) #define I40E_PF_ATQLEN_ATQENABLE_SHIFT 31 -#define I40E_PF_ATQLEN_ATQENABLE_MASK I40E_MASK(0x1, I40E_PF_ATQLEN_ATQENABLE_SHIFT) +#define I40E_PF_ATQLEN_ATQENABLE_MASK I40E_MASK(0x1u, I40E_PF_ATQLEN_ATQENABLE_SHIFT) #define I40E_PF_ATQT 0x00080400 /* Reset: EMPR */ #define I40E_PF_ATQT_ATQT_SHIFT 0 #define I40E_PF_ATQT_ATQT_MASK I40E_MASK(0x3FF, I40E_PF_ATQT_ATQT_SHIFT) @@ -131,7 +108,7 @@ #define I40E_VF_ARQLEN_ARQCRIT_SHIFT 30 #define I40E_VF_ARQLEN_ARQCRIT_MASK I40E_MASK(0x1, I40E_VF_ARQLEN_ARQCRIT_SHIFT) #define I40E_VF_ARQLEN_ARQENABLE_SHIFT 31 -#define I40E_VF_ARQLEN_ARQENABLE_MASK I40E_MASK(0x1, I40E_VF_ARQLEN_ARQENABLE_SHIFT) +#define I40E_VF_ARQLEN_ARQENABLE_MASK I40E_MASK(0x1u, I40E_VF_ARQLEN_ARQENABLE_SHIFT) #define I40E_VF_ARQT(_VF) (0x00082C00 + ((_VF) * 4)) /* _i=0...127 */ /* Reset: EMPR */ #define I40E_VF_ARQT_MAX_INDEX 127 #define I40E_VF_ARQT_ARQT_SHIFT 0 @@ -159,7 +136,7 @@ #define I40E_VF_ATQLEN_ATQCRIT_SHIFT 30 #define I40E_VF_ATQLEN_ATQCRIT_MASK I40E_MASK(0x1, I40E_VF_ATQLEN_ATQCRIT_SHIFT) #define I40E_VF_ATQLEN_ATQENABLE_SHIFT 31 -#define I40E_VF_ATQLEN_ATQENABLE_MASK I40E_MASK(0x1, I40E_VF_ATQLEN_ATQENABLE_SHIFT) +#define I40E_VF_ATQLEN_ATQENABLE_MASK I40E_MASK(0x1u, I40E_VF_ATQLEN_ATQENABLE_SHIFT) #define I40E_VF_ATQT(_VF) (0x00082800 + ((_VF) * 4)) /* _i=0...127 */ /* Reset: EMPR */ #define I40E_VF_ATQT_MAX_INDEX 127 #define I40E_VF_ATQT_ATQT_SHIFT 0 @@ -282,7 +259,7 @@ #define I40E_PRTDCB_RETSTCC_UPINTC_MODE_SHIFT 30 #define I40E_PRTDCB_RETSTCC_UPINTC_MODE_MASK I40E_MASK(0x1, I40E_PRTDCB_RETSTCC_UPINTC_MODE_SHIFT) #define I40E_PRTDCB_RETSTCC_ETSTC_SHIFT 31 -#define I40E_PRTDCB_RETSTCC_ETSTC_MASK I40E_MASK(0x1, I40E_PRTDCB_RETSTCC_ETSTC_SHIFT) +#define I40E_PRTDCB_RETSTCC_ETSTC_MASK I40E_MASK(0x1u, I40E_PRTDCB_RETSTCC_ETSTC_SHIFT) #define I40E_PRTDCB_RPPMC 0x001223A0 /* Reset: CORER */ #define I40E_PRTDCB_RPPMC_LANRPPM_SHIFT 0 #define I40E_PRTDCB_RPPMC_LANRPPM_MASK I40E_MASK(0xFF, I40E_PRTDCB_RPPMC_LANRPPM_SHIFT) @@ -386,6 +363,14 @@ #define I40E_GL_FWSTS_FWRI_MASK I40E_MASK(0x1, I40E_GL_FWSTS_FWRI_SHIFT) #define I40E_GL_FWSTS_FWS1B_SHIFT 16 #define I40E_GL_FWSTS_FWS1B_MASK I40E_MASK(0xFF, I40E_GL_FWSTS_FWS1B_SHIFT) +#define I40E_GL_FWSTS_FWS1B_EMPR_0 I40E_MASK(0x20, I40E_GL_FWSTS_FWS1B_SHIFT) +#define I40E_GL_FWSTS_FWS1B_EMPR_10 I40E_MASK(0x2A, I40E_GL_FWSTS_FWS1B_SHIFT) +#define I40E_XL710_GL_FWSTS_FWS1B_REC_MOD_CORER_MASK I40E_MASK(0x30, I40E_GL_FWSTS_FWS1B_SHIFT) +#define I40E_XL710_GL_FWSTS_FWS1B_REC_MOD_GLOBR_MASK I40E_MASK(0x31, I40E_GL_FWSTS_FWS1B_SHIFT) +#define I40E_XL710_GL_FWSTS_FWS1B_REC_MOD_TRANSITION_MASK I40E_MASK(0x32, I40E_GL_FWSTS_FWS1B_SHIFT) +#define I40E_XL710_GL_FWSTS_FWS1B_REC_MOD_NVM_MASK I40E_MASK(0x33, I40E_GL_FWSTS_FWS1B_SHIFT) +#define I40E_X722_GL_FWSTS_FWS1B_REC_MOD_CORER_MASK I40E_MASK(0xB, I40E_GL_FWSTS_FWS1B_SHIFT) +#define I40E_X722_GL_FWSTS_FWS1B_REC_MOD_GLOBR_MASK I40E_MASK(0xC, I40E_GL_FWSTS_FWS1B_SHIFT) #define I40E_GLGEN_CLKSTAT 0x000B8184 /* Reset: POR */ #define I40E_GLGEN_CLKSTAT_CLKMODE_SHIFT 0 #define I40E_GLGEN_CLKSTAT_CLKMODE_MASK I40E_MASK(0x1, I40E_GLGEN_CLKSTAT_CLKMODE_SHIFT) @@ -526,7 +511,7 @@ #define I40E_GLGEN_MSCA_MDICMD_SHIFT 30 #define I40E_GLGEN_MSCA_MDICMD_MASK I40E_MASK(0x1, I40E_GLGEN_MSCA_MDICMD_SHIFT) #define I40E_GLGEN_MSCA_MDIINPROGEN_SHIFT 31 -#define I40E_GLGEN_MSCA_MDIINPROGEN_MASK I40E_MASK(0x1, I40E_GLGEN_MSCA_MDIINPROGEN_SHIFT) +#define I40E_GLGEN_MSCA_MDIINPROGEN_MASK I40E_MASK(0x1u, I40E_GLGEN_MSCA_MDIINPROGEN_SHIFT) #define I40E_GLGEN_MSRWD(_i) (0x0008819C + ((_i) * 4)) /* _i=0...3 */ /* Reset: POR */ #define I40E_GLGEN_MSRWD_MAX_INDEX 3 #define I40E_GLGEN_MSRWD_MDIWRDATA_SHIFT 0 @@ -1265,14 +1250,14 @@ #define I40E_GLLAN_TXPRE_QDIS_SET_QDIS_SHIFT 30 #define I40E_GLLAN_TXPRE_QDIS_SET_QDIS_MASK I40E_MASK(0x1, I40E_GLLAN_TXPRE_QDIS_SET_QDIS_SHIFT) #define I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_SHIFT 31 -#define I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_MASK I40E_MASK(0x1, I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_SHIFT) +#define I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_MASK I40E_MASK(0x1u, I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_SHIFT) #define I40E_PFLAN_QALLOC 0x001C0400 /* Reset: CORER */ #define I40E_PFLAN_QALLOC_FIRSTQ_SHIFT 0 #define I40E_PFLAN_QALLOC_FIRSTQ_MASK I40E_MASK(0x7FF, I40E_PFLAN_QALLOC_FIRSTQ_SHIFT) #define I40E_PFLAN_QALLOC_LASTQ_SHIFT 16 #define I40E_PFLAN_QALLOC_LASTQ_MASK I40E_MASK(0x7FF, I40E_PFLAN_QALLOC_LASTQ_SHIFT) #define I40E_PFLAN_QALLOC_VALID_SHIFT 31 -#define I40E_PFLAN_QALLOC_VALID_MASK I40E_MASK(0x1, I40E_PFLAN_QALLOC_VALID_SHIFT) +#define I40E_PFLAN_QALLOC_VALID_MASK I40E_MASK(0x1u, I40E_PFLAN_QALLOC_VALID_SHIFT) #define I40E_QRX_ENA(_Q) (0x00120000 + ((_Q) * 4)) /* _i=0...1535 */ /* Reset: PFR */ #define I40E_QRX_ENA_MAX_INDEX 1535 #define I40E_QRX_ENA_QENA_REQ_SHIFT 0 @@ -1681,7 +1666,7 @@ #define I40E_GLNVM_SRCTL_START_SHIFT 30 #define I40E_GLNVM_SRCTL_START_MASK I40E_MASK(0x1, I40E_GLNVM_SRCTL_START_SHIFT) #define I40E_GLNVM_SRCTL_DONE_SHIFT 31 -#define I40E_GLNVM_SRCTL_DONE_MASK I40E_MASK(0x1, I40E_GLNVM_SRCTL_DONE_SHIFT) +#define I40E_GLNVM_SRCTL_DONE_MASK I40E_MASK(0x1u, I40E_GLNVM_SRCTL_DONE_SHIFT) #define I40E_GLNVM_SRDATA 0x000B6114 /* Reset: POR */ #define I40E_GLNVM_SRDATA_WRDATA_SHIFT 0 #define I40E_GLNVM_SRDATA_WRDATA_MASK I40E_MASK(0xFFFF, I40E_GLNVM_SRDATA_WRDATA_SHIFT) @@ -2794,7 +2779,7 @@ #define I40E_GLV_RUPP_MAX_INDEX 383 #define I40E_GLV_RUPP_RUPP_SHIFT 0 #define I40E_GLV_RUPP_RUPP_MASK I40E_MASK(0xFFFFFFFF, I40E_GLV_RUPP_RUPP_SHIFT) -#define I40E_GLV_TEPC(_VSI) (0x00344000 + ((_VSI) * 4)) /* _i=0...383 */ /* Reset: CORER */ +#define I40E_GLV_TEPC(_i) (0x00344000 + ((_i) * 8)) /* _i=0...383 */ /* Reset: CORER */ #define I40E_GLV_TEPC_MAX_INDEX 383 #define I40E_GLV_TEPC_TEPC_SHIFT 0 #define I40E_GLV_TEPC_TEPC_MASK I40E_MASK(0xFFFFFFFF, I40E_GLV_TEPC_TEPC_SHIFT) @@ -2914,6 +2899,9 @@ #define I40E_PRTTSYN_AUX_0_PULSEW_MASK I40E_MASK(0xF, I40E_PRTTSYN_AUX_0_PULSEW_SHIFT) #define I40E_PRTTSYN_AUX_0_EVNTLVL_SHIFT 16 #define I40E_PRTTSYN_AUX_0_EVNTLVL_MASK I40E_MASK(0x3, I40E_PRTTSYN_AUX_0_EVNTLVL_SHIFT) +#define I40E_PRTTSYN_AUX_0_PTPFLAG_SHIFT 17 +#define I40E_PRTTSYN_AUX_0_PTPFLAG_MASK I40E_MASK(0x1, I40E_PRTTSYN_AUX_0_PTPFLAG_SHIFT) +#define I40E_PRTTSYN_AUX_0_PTP_OUT_SYNC_CLK_IO 0xF #define I40E_PRTTSYN_AUX_1(_i) (0x001E42E0 + ((_i) * 32)) /* _i=0...1 */ /* Reset: GLOBR */ #define I40E_PRTTSYN_AUX_1_MAX_INDEX 1 #define I40E_PRTTSYN_AUX_1_INSTNT_SHIFT 0 @@ -3048,7 +3036,7 @@ #define I40E_PF_VT_PFALLOC_LASTVF_SHIFT 8 #define I40E_PF_VT_PFALLOC_LASTVF_MASK I40E_MASK(0xFF, I40E_PF_VT_PFALLOC_LASTVF_SHIFT) #define I40E_PF_VT_PFALLOC_VALID_SHIFT 31 -#define I40E_PF_VT_PFALLOC_VALID_MASK I40E_MASK(0x1, I40E_PF_VT_PFALLOC_VALID_SHIFT) +#define I40E_PF_VT_PFALLOC_VALID_MASK I40E_MASK(0x1u, I40E_PF_VT_PFALLOC_VALID_SHIFT) #define I40E_VP_MDET_RX(_VF) (0x0012A000 + ((_VF) * 4)) /* _i=0...127 */ /* Reset: CORER */ #define I40E_VP_MDET_RX_MAX_INDEX 127 #define I40E_VP_MDET_RX_VALID_SHIFT 0 @@ -3184,7 +3172,7 @@ #define I40E_VF_ARQLEN1_ARQCRIT_SHIFT 30 #define I40E_VF_ARQLEN1_ARQCRIT_MASK I40E_MASK(0x1, I40E_VF_ARQLEN1_ARQCRIT_SHIFT) #define I40E_VF_ARQLEN1_ARQENABLE_SHIFT 31 -#define I40E_VF_ARQLEN1_ARQENABLE_MASK I40E_MASK(0x1, I40E_VF_ARQLEN1_ARQENABLE_SHIFT) +#define I40E_VF_ARQLEN1_ARQENABLE_MASK I40E_MASK(0x1u, I40E_VF_ARQLEN1_ARQENABLE_SHIFT) #define I40E_VF_ARQT1 0x00007000 /* Reset: EMPR */ #define I40E_VF_ARQT1_ARQT_SHIFT 0 #define I40E_VF_ARQT1_ARQT_MASK I40E_MASK(0x3FF, I40E_VF_ARQT1_ARQT_SHIFT) @@ -3207,7 +3195,7 @@ #define I40E_VF_ATQLEN1_ATQCRIT_SHIFT 30 #define I40E_VF_ATQLEN1_ATQCRIT_MASK I40E_MASK(0x1, I40E_VF_ATQLEN1_ATQCRIT_SHIFT) #define I40E_VF_ATQLEN1_ATQENABLE_SHIFT 31 -#define I40E_VF_ATQLEN1_ATQENABLE_MASK I40E_MASK(0x1, I40E_VF_ATQLEN1_ATQENABLE_SHIFT) +#define I40E_VF_ATQLEN1_ATQENABLE_MASK I40E_MASK(0x1u, I40E_VF_ATQLEN1_ATQENABLE_SHIFT) #define I40E_VF_ATQT1 0x00008400 /* Reset: EMPR */ #define I40E_VF_ATQT1_ATQT_SHIFT 0 #define I40E_VF_ATQT1_ATQT_MASK I40E_MASK(0x3FF, I40E_VF_ATQT1_ATQT_SHIFT) @@ -5290,6 +5278,87 @@ #define I40E_GLGEN_STAT_HALT 0x00390000 /* Reset: CORER */ #define I40E_GLGEN_STAT_HALT_HALT_CELLS_SHIFT 0 #define I40E_GLGEN_STAT_HALT_HALT_CELLS_MASK I40E_MASK(0x3FFFFFFF, I40E_GLGEN_STAT_HALT_HALT_CELLS_SHIFT) +/* Flow Director */ +#define I40E_REG_INSET_L2_DMAC_SHIFT 60 +#define I40E_REG_INSET_L2_DMAC_MASK I40E_MASK(0xEULL, I40E_REG_INSET_L2_DMAC_SHIFT) +#define I40E_REG_INSET_L2_SMAC_SHIFT 56 +#define I40E_REG_INSET_L2_SMAC_MASK I40E_MASK(0x1CULL, I40E_REG_INSET_L2_SMAC_SHIFT) +#define I40E_REG_INSET_L2_OUTER_VLAN_SHIFT 26 +#define I40E_REG_INSET_L2_OUTER_VLAN_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_L2_OUTER_VLAN_SHIFT) +#define I40E_REG_INSET_L2_INNER_VLAN_SHIFT 55 +#define I40E_REG_INSET_L2_INNER_VLAN_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_L2_INNER_VLAN_SHIFT) +#define I40E_REG_INSET_TUNNEL_VLAN_SHIFT 56 +#define I40E_REG_INSET_TUNNEL_VLAN_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_TUNNEL_VLAN_SHIFT) +#define I40E_REG_INSET_L3_SRC_IP4_SHIFT 47 +#define I40E_REG_INSET_L3_SRC_IP4_MASK I40E_MASK(0x3ULL, I40E_REG_INSET_L3_SRC_IP4_SHIFT) +#define I40E_REG_INSET_L3_DST_IP4_SHIFT 35 +#define I40E_REG_INSET_L3_DST_IP4_MASK I40E_MASK(0x3ULL, I40E_REG_INSET_L3_DST_IP4_SHIFT) +#define I40E_X722_REG_INSET_L3_SRC_IP4_SHIFT 49 +#define I40E_X722_REG_INSET_L3_SRC_IP4_MASK I40E_MASK(0x3ULL, I40E_X722_REG_INSET_L3_SRC_IP4_SHIFT) +#define I40E_X722_REG_INSET_L3_DST_IP4_SHIFT 41 +#define I40E_X722_REG_INSET_L3_DST_IP4_MASK I40E_MASK(0x3ULL, I40E_X722_REG_INSET_L3_DST_IP4_SHIFT) +#define I40E_X722_REG_INSET_L3_IP4_PROTO_SHIFT 52 +#define I40E_X722_REG_INSET_L3_IP4_PROTO_MASK I40E_MASK(0x1ULL, I40E_X722_REG_INSET_L3_IP4_PROTO_SHIFT) +#define I40E_X722_REG_INSET_L3_IP4_TTL_SHIFT 52 +#define I40E_X722_REG_INSET_L3_IP4_TTL_MASK I40E_MASK(0x1ULL, I40E_X722_REG_INSET_L3_IP4_TTL_SHIFT) +#define I40E_REG_INSET_L3_IP4_TOS_SHIFT 54 +#define I40E_REG_INSET_L3_IP4_TOS_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_L3_IP4_TOS_SHIFT) +#define I40E_REG_INSET_L3_IP4_PROTO_SHIFT 50 +#define I40E_REG_INSET_L3_IP4_PROTO_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_L3_IP4_PROTO_SHIFT) +#define I40E_REG_INSET_L3_IP4_TTL_SHIFT 50 +#define I40E_REG_INSET_L3_IP4_TTL_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_L3_IP4_TTL_SHIFT) +#define I40E_REG_INSET_L3_SRC_IP6_SHIFT 43 +#define I40E_REG_INSET_L3_SRC_IP6_MASK I40E_MASK(0xFFULL, I40E_REG_INSET_L3_SRC_IP6_SHIFT) +#define I40E_REG_INSET_L3_DST_IP6_SHIFT 35 +#define I40E_REG_INSET_L3_DST_IP6_MASK I40E_MASK(0xFFULL, I40E_REG_INSET_L3_DST_IP6_SHIFT) +#define I40E_REG_INSET_L3_IP6_TC_SHIFT 54 +#define I40E_REG_INSET_L3_IP6_TC_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_L3_IP6_TC_SHIFT) +#define I40E_REG_INSET_L3_IP6_NEXT_HDR_SHIFT 51 +#define I40E_REG_INSET_L3_IP6_NEXT_HDR_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_L3_IP6_NEXT_HDR_SHIFT) +#define I40E_REG_INSET_L3_IP6_HOP_LIMIT_SHIFT 51 +#define I40E_REG_INSET_L3_IP6_HOP_LIMIT_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_L3_IP6_HOP_LIMIT_SHIFT) +#define I40E_REG_INSET_L4_SRC_PORT_SHIFT 34 +#define I40E_REG_INSET_L4_SRC_PORT_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_L4_SRC_PORT_SHIFT) +#define I40E_REG_INSET_L4_DST_PORT_SHIFT 33 +#define I40E_REG_INSET_L4_DST_PORT_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_L4_DST_PORT_SHIFT) +#define I40E_REG_INSET_L4_SCTP_VERIFICATION_TAG_SHIFT 31 +#define I40E_REG_INSET_L4_SCTP_VERIFICATION_TAG_MASK I40E_MASK(0x3ULL, I40E_REG_INSET_L4_SCTP_VERIFICATION_TAG_SHIFT) +#define I40E_REG_INSET_TUNNEL_L2_INNER_DST_MAC_SHIFT 22 +#define I40E_REG_INSET_TUNNEL_L2_INNER_DST_MAC_MASK I40E_MASK(0x7ULL, I40E_REG_INSET_TUNNEL_L2_INNER_DST_MAC_SHIFT) +#define I40E_REG_INSET_TUNNEL_L2_INNER_SRC_MAC_SHIFT 11 +#define I40E_REG_INSET_TUNNEL_L2_INNER_SRC_MAC_MASK I40E_MASK(0x7ULL, I40E_REG_INSET_TUNNEL_L2_INNER_SRC_MAC_SHIFT) +#define I40E_REG_INSET_TUNNEL_L4_UDP_DST_PORT_SHIFT 21 +#define I40E_REG_INSET_TUNNEL_L4_UDP_DST_PORT_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_TUNNEL_L4_UDP_DST_PORT_SHIFT) +#define I40E_REG_INSET_TUNNEL_ID_SHIFT 18 +#define I40E_REG_INSET_TUNNEL_ID_MASK I40E_MASK(0x3ULL, I40E_REG_INSET_TUNNEL_ID_SHIFT) +#define I40E_REG_INSET_LAST_ETHER_TYPE_SHIFT 14 +#define I40E_REG_INSET_LAST_ETHER_TYPE_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_LAST_ETHER_TYPE_SHIFT) +#define I40E_REG_INSET_TUNNEL_L3_SRC_IP4_SHIFT 8 +#define I40E_REG_INSET_TUNNEL_L3_SRC_IP4_MASK I40E_MASK(0x3ULL, I40E_REG_INSET_TUNNEL_L3_SRC_IP4_SHIFT) +#define I40E_REG_INSET_TUNNEL_L3_DST_IP4_SHIFT 6 +#define I40E_REG_INSET_TUNNEL_L3_DST_IP4_MASK I40E_MASK(0x3ULL, I40E_REG_INSET_TUNNEL_L3_DST_IP4_SHIFT) +#define I40E_REG_INSET_TUNNEL_L3_DST_IP6_SHIFT 6 +#define I40E_REG_INSET_TUNNEL_L3_DST_IP6_MASK I40E_MASK(0xFFULL, I40E_REG_INSET_TUNNEL_L3_DST_IP6_SHIFT) +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD1_SHIFT 13 +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD1_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_FLEX_PAYLOAD_WORD1_SHIFT) +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD2_SHIFT 12 +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD2_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_FLEX_PAYLOAD_WORD2_SHIFT) +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD3_SHIFT 11 +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD3_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_FLEX_PAYLOAD_WORD3_SHIFT) +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD4_SHIFT 10 +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD4_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_FLEX_PAYLOAD_WORD4_SHIFT) +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD5_SHIFT 9 +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD5_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_FLEX_PAYLOAD_WORD5_SHIFT) +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD6_SHIFT 8 +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD6_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_FLEX_PAYLOAD_WORD6_SHIFT) +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD7_SHIFT 7 +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD7_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_FLEX_PAYLOAD_WORD7_SHIFT) +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD8_SHIFT 6 +#define I40E_REG_INSET_FLEX_PAYLOAD_WORD8_MASK I40E_MASK(0x1ULL, I40E_REG_INSET_FLEX_PAYLOAD_WORD8_SHIFT) +#define I40E_REG_INSET_FLEX_PAYLOAD_WORDS_SHIFT 6 +#define I40E_REG_INSET_FLEX_PAYLOAD_WORDS_MASK I40E_MASK(0xFFULL, I40E_REG_INSET_FLEX_PAYLOAD_WORDS_SHIFT) +#define I40E_REG_INSET_MASK_DEFAULT 0x0000000000000000ULL + #define I40E_VFINT_DYN_CTL01_WB_ON_ITR_SHIFT 30 #define I40E_VFINT_DYN_CTL01_WB_ON_ITR_MASK I40E_MASK(0x1, I40E_VFINT_DYN_CTL01_WB_ON_ITR_SHIFT) #define I40E_VFINT_DYN_CTLN1_WB_ON_ITR_SHIFT 30 @@ -5350,4 +5419,5 @@ #define I40E_VFPE_WQEALLOC1_PEQPID_MASK I40E_MASK(0x3FFFF, I40E_VFPE_WQEALLOC1_PEQPID_SHIFT) #define I40E_VFPE_WQEALLOC1_WQE_DESC_INDEX_SHIFT 20 #define I40E_VFPE_WQEALLOC1_WQE_DESC_INDEX_MASK I40E_MASK(0xFFF, I40E_VFPE_WQEALLOC1_WQE_DESC_INDEX_SHIFT) + #endif /* _I40E_REGISTER_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_status.h b/drivers/net/ethernet/intel/i40e/i40e_status.h index 5f9cac55a..ab12f1311 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_status.h +++ b/drivers/net/ethernet/intel/i40e/i40e_status.h @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_STATUS_H_ #define _I40E_STATUS_H_ @@ -95,6 +72,7 @@ enum i40e_status_code { I40E_ERR_NOT_READY = -63, I40E_NOT_SUPPORTED = -64, I40E_ERR_FIRMWARE_API_VERSION = -65, + I40E_ERR_ADMIN_QUEUE_CRITICAL_ERROR = -66, }; #endif /* _I40E_STATUS_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_trace.h b/drivers/net/ethernet/intel/i40e/i40e_trace.h index d3e55f54a..27e47ccf3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_trace.h +++ b/drivers/net/ethernet/intel/i40e/i40e_trace.h @@ -1,29 +1,23 @@ -/******************************************************************************* - * - * Intel(R) 40-10 Gigabit Ethernet Connection Network Driver - * Copyright(c) 2013 - 2017 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ -/* Modeled on trace-events-sample.h */ +#ifndef CONFIG_TRACEPOINTS +#if !defined(_I40E_TRACE_H_) +#define _I40E_TRACE_H_ +/* If the Linux kernel tracepoints are not available then the i40e_trace* + * macros become nops. + */ -/* The trace subsystem name for i40e will be "i40e". +#define i40e_trace(trace_name, args...) +#define i40e_trace_enabled(trace_name) (0) +#endif /* !defined(_I40E_TRACE_H_) */ +#else /* CONFIG_TRACEPOINTS */ +/* + * Modeled on trace-events-sample.h + */ + +/* + * The trace subsystem name for i40e will be "i40e". * * This file is named i40e_trace.h. * @@ -34,7 +28,8 @@ #undef TRACE_SYSTEM #define TRACE_SYSTEM i40e -/* See trace-events-sample.h for a detailed description of why this +/* + * See trace-events-sample.h for a detailed description of why this * guard clause is different from most normal include files. */ #if !defined(_I40E_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) @@ -69,7 +64,8 @@ #define i40e_trace_enabled(trace_name) I40E_TRACE_NAME(trace_name##_enabled)() -/* Events common to PF and VF. Corresponding versions will be defined +/* + * Events common to PF and VF. Corresponding versions will be defined * for both, named trace_i40e_* and trace_i40evf_*. The i40e_trace() * macro above will select the right trace point name for the driver * being built from shared code. @@ -85,7 +81,8 @@ DECLARE_EVENT_CLASS( TP_ARGS(ring, desc, buf), - /* The convention here is to make the first fields in the + /* + * The convention here is to make the first fields in the * TP_STRUCT match the TP_PROTO exactly. This enables the use * of the args struct generated by the tplist tool (from the * bcc-tools package) to be used for those fields. To access @@ -132,7 +129,7 @@ DECLARE_EVENT_CLASS( i40e_rx_template, TP_PROTO(struct i40e_ring *ring, - union i40e_32byte_rx_desc *desc, + union i40e_rx_desc *desc, struct sk_buff *skb), TP_ARGS(ring, desc, skb), @@ -160,7 +157,7 @@ DECLARE_EVENT_CLASS( DEFINE_EVENT( i40e_rx_template, i40e_clean_rx_irq, TP_PROTO(struct i40e_ring *ring, - union i40e_32byte_rx_desc *desc, + union i40e_rx_desc *desc, struct sk_buff *skb), TP_ARGS(ring, desc, skb)); @@ -168,7 +165,7 @@ DEFINE_EVENT( DEFINE_EVENT( i40e_rx_template, i40e_clean_rx_irq_rx, TP_PROTO(struct i40e_ring *ring, - union i40e_32byte_rx_desc *desc, + union i40e_rx_desc *desc, struct sk_buff *skb), TP_ARGS(ring, desc, skb)); @@ -213,7 +210,9 @@ DEFINE_EVENT( TP_ARGS(skb, ring)); -/* Events unique to the PF. */ +/* + * Events unique to the PF. + */ #endif /* _I40E_TRACE_H_ */ /* This must be outside ifdef _I40E_TRACE_H */ @@ -227,3 +226,4 @@ DEFINE_EVENT( #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE i40e_trace #include +#endif /* CONFIG_TRACEPOINTS */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 3c07ff171..96bc531ac 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1,32 +1,10 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2016 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #include -#include -#include +#ifdef HAVE_XDP_SUPPORT +#include +#endif #include "i40e.h" #include "i40e_trace.h" #include "i40e_prototype.h" @@ -67,9 +45,6 @@ static void i40e_fdir(struct i40e_ring *tx_ring, flex_ptype = I40E_TXD_FLTR_QW0_QINDEX_MASK & (fdata->q_index << I40E_TXD_FLTR_QW0_QINDEX_SHIFT); - flex_ptype |= I40E_TXD_FLTR_QW0_FLEXOFF_MASK & - (fdata->flex_off << I40E_TXD_FLTR_QW0_FLEXOFF_SHIFT); - flex_ptype |= I40E_TXD_FLTR_QW0_PCTYPE_MASK & (fdata->pctype << I40E_TXD_FLTR_QW0_PCTYPE_SHIFT); @@ -152,6 +127,7 @@ static int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, /* grab the next descriptor */ i = tx_ring->next_to_use; first = &tx_ring->tx_bi[i]; + i40e_fdir(tx_ring, fdir_data, add); /* Now program a dummy descriptor */ @@ -192,7 +168,6 @@ dma_fail: } #define IP_HEADER_OFFSET 14 -#define I40E_UDPIP_DUMMY_PACKET_LEN 42 /** * i40e_add_del_fdir_udpv4 - Add/Remove UDPv4 filters * @vsi: pointer to the targeted VSI @@ -264,7 +239,6 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi, return 0; } -#define I40E_TCPIP_DUMMY_PACKET_LEN 54 /** * i40e_add_del_fdir_tcpv4 - Add/Remove TCPv4 filters * @vsi: pointer to the targeted VSI @@ -285,7 +259,7 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, /* Dummy packet */ static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0, 0x45, 0, 0, 0x28, 0, 0, 0x40, 0, 0x40, 0x6, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x50, 0x11, 0x0, 0x72, 0, 0, 0, 0}; raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, GFP_KERNEL); @@ -334,7 +308,7 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && I40E_DEBUG_FD & pf->hw.debug_mask) dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n"); - pf->flags |= I40E_FLAG_FD_ATR_AUTO_DISABLED; + set_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state); } else { pf->fd_tcp4_filter_cnt--; } @@ -342,7 +316,6 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, return 0; } -#define I40E_SCTPIP_DUMMY_PACKET_LEN 46 /** * i40e_add_del_fdir_sctpv4 - Add/Remove SCTPv4 Flow Director filters for * a specific flow spec @@ -416,7 +389,6 @@ static int i40e_add_del_fdir_sctpv4(struct i40e_vsi *vsi, return 0; } -#define I40E_IP_DUMMY_PACKET_LEN 34 /** * i40e_add_del_fdir_ipv4 - Add/Remove IPv4 Flow Director filters for * a specific flow spec @@ -465,9 +437,6 @@ static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi, dev_info(&pf->pdev->dev, "PCTYPE:%d, Filter command send failed for fd_id:%d (ret = %d)\n", fd_data->pctype, fd_data->fd_id, ret); - /* The packet buffer wasn't added to the ring so we - * need to free it now. - */ kfree(raw_packet); return -EOPNOTSUPP; } else if (I40E_DEBUG_FD & pf->hw.debug_mask) { @@ -493,7 +462,7 @@ static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi, /** * i40e_add_del_fdir - Build raw packets to add/del fdir filter * @vsi: pointer to the targeted VSI - * @cmd: command to get or set RX flow classification rules + * @input: filter to add or delete * @add: true adds a filter, false removes it * **/ @@ -503,7 +472,7 @@ int i40e_add_del_fdir(struct i40e_vsi *vsi, struct i40e_pf *pf = vsi->back; int ret; - switch (input->flow_type & ~FLOW_EXT) { + switch (input->flow_type) { case TCP_V4_FLOW: ret = i40e_add_del_fdir_tcpv4(vsi, input, add); break; @@ -592,8 +561,14 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, pf->fd_atr_cnt = i40e_get_current_atr_cnt(pf); if ((rx_desc->wb.qword0.hi_dword.fd_id == 0) && - pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED) { - pf->flags |= I40E_FLAG_FD_ATR_AUTO_DISABLED; + test_bit(__I40E_FD_SB_AUTO_DISABLED, pf->state)) { + /* These set_bit() calls aren't atomic with the + * test_bit() here, but worse case we potentially + * disable ATR and queue a flush right after SB + * support is re-enabled. That shouldn't cause an + * issue in practice + */ + set_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state); set_bit(__I40E_FD_FLUSH_REQUESTED, pf->state); } @@ -606,11 +581,10 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, */ if (fcnt_prog >= (fcnt_avail - I40E_FDIR_BUFFER_FULL_MARGIN)) { if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) && - !(pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED)) { - pf->flags |= I40E_FLAG_FD_SB_AUTO_DISABLED; + !test_and_set_bit(__I40E_FD_SB_AUTO_DISABLED, + pf->state)) if (I40E_DEBUG_FD & pf->hw.debug_mask) dev_warn(&pdev->dev, "FD filter space full, new ntuple rules will not be added\n"); - } } } else if (error == BIT(I40E_RX_PROG_STATUS_DESC_NO_FD_ENTRY_SHIFT)) { if (I40E_DEBUG_FD & pf->hw.debug_mask) @@ -630,8 +604,14 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring, if (tx_buffer->skb) { if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB) kfree(tx_buffer->raw_buf); +#ifdef HAVE_XDP_SUPPORT else if (ring_is_xdp(ring)) +#ifdef HAVE_XDP_FRAME_STRUCT + xdp_return_frame(tx_buffer->xdpf); +#else page_frag_free(tx_buffer->raw_buf); +#endif +#endif else dev_kfree_skb_any(tx_buffer->skb); if (dma_unmap_len(tx_buffer, len)) @@ -706,17 +686,23 @@ void i40e_free_tx_resources(struct i40e_ring *tx_ring) /** * i40e_get_tx_pending - how many tx descriptors not processed - * @tx_ring: the ring of descriptors + * @ring: the ring of descriptors + * @in_sw: use SW variables * * Since there is no access to the ring head register * in XL710, we need to use our local copies **/ -u32 i40e_get_tx_pending(struct i40e_ring *ring) +u32 i40e_get_tx_pending(struct i40e_ring *ring, bool in_sw) { u32 head, tail; - head = i40e_get_head(ring); - tail = readl(ring->tail); + if (!in_sw) { + head = i40e_get_head(ring); + tail = readl(ring->tail); + } else { + head = ring->next_to_clean; + tail = ring->next_to_use; + } if (head != tail) return (head < tail) ? @@ -725,6 +711,59 @@ u32 i40e_get_tx_pending(struct i40e_ring *ring) return 0; } +/** + * i40e_detect_recover_hung - Function to detect and recover hung_queues + * @vsi: pointer to vsi struct with tx queues + * + * VSI has netdev and netdev has TX queues. This function is to check each of + * those TX queues if they are hung, trigger recovery by issuing SW interrupt. + **/ +void i40e_detect_recover_hung(struct i40e_vsi *vsi) +{ + struct i40e_ring *tx_ring = NULL; + struct net_device *netdev; + unsigned int i; + int packets; + + if (!vsi) + return; + + if (test_bit(__I40E_VSI_DOWN, vsi->state)) + return; + + netdev = vsi->netdev; + if (!netdev) + return; + + if (!netif_carrier_ok(netdev)) + return; + + for (i = 0; i < vsi->num_queue_pairs; i++) { + tx_ring = vsi->tx_rings[i]; + if (tx_ring && tx_ring->desc) { + /* If packet counter has not changed the queue is + * likely stalled, so force an interrupt for this + * queue. + * + * prev_pkt_ctr would be negative if there was no + * pending work. + */ + packets = tx_ring->stats.packets & INT_MAX; + if (tx_ring->tx_stats.prev_pkt_ctr == packets) { + i40e_force_wb(vsi, tx_ring->q_vector); + continue; + } + + /* Memory barrier between read of packet count and call + * to i40e_get_tx_pending() + */ + smp_rmb(); + tx_ring->tx_stats.prev_pkt_ctr = + i40e_get_tx_pending(tx_ring, true) ? packets : -1; + } + } +} + #define WB_STRIDE 4 /** @@ -738,7 +777,7 @@ u32 i40e_get_tx_pending(struct i40e_ring *ring) static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, struct i40e_ring *tx_ring, int napi_budget) { - u16 i = tx_ring->next_to_clean; + int i = tx_ring->next_to_clean; struct i40e_tx_buffer *tx_buf; struct i40e_tx_desc *tx_head; struct i40e_tx_desc *tx_desc; @@ -759,7 +798,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, break; /* prevent any other reads prior to eop_desc */ - smp_rmb(); + read_barrier_depends(); i40e_trace(clean_tx_irq, tx_ring, tx_desc, tx_buf); /* we have caught up to head, no work left to do */ @@ -774,9 +813,15 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, total_packets += tx_buf->gso_segs; /* free the skb/XDP data */ +#ifdef HAVE_XDP_SUPPORT if (ring_is_xdp(tx_ring)) +#ifdef HAVE_XDP_FRAME_STRUCT + xdp_return_frame(tx_buf->xdpf); +#else page_frag_free(tx_buf->raw_buf); +#endif else +#endif napi_consume_skb(tx_buf->skb, napi_budget); /* unmap skb header data */ @@ -844,7 +889,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, * them to be written back in case we stay in NAPI. * In this mode on X722 we do not enable Interrupt. */ - unsigned int j = i40e_get_tx_pending(tx_ring); + unsigned int j = i40e_get_tx_pending(tx_ring, false); if (budget && ((j / WB_STRIDE) == 0) && (j > 0) && @@ -902,8 +947,8 @@ static void i40e_enable_wb_on_itr(struct i40e_vsi *vsi, I40E_PFINT_DYN_CTLN_ITR_INDX_MASK; /* set noitr */ wr32(&vsi->back->hw, - I40E_PFINT_DYN_CTLN(q_vector->v_idx + vsi->base_vector - 1), - val); + I40E_PFINT_DYN_CTLN(q_vector->reg_idx), + val); } else { val = I40E_PFINT_DYN_CTL0_WB_ON_ITR_MASK | I40E_PFINT_DYN_CTL0_ITR_INDX_MASK; /* set noitr */ @@ -929,8 +974,7 @@ void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) /* allow 00 to be written to the index */ wr32(&vsi->back->hw, - I40E_PFINT_DYN_CTLN(q_vector->v_idx + - vsi->base_vector - 1), val); + I40E_PFINT_DYN_CTLN(q_vector->reg_idx), val); } else { u32 val = I40E_PFINT_DYN_CTL0_INTENA_MASK | I40E_PFINT_DYN_CTL0_ITR_INDX_MASK | /* set noitr */ @@ -942,101 +986,244 @@ void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) } } +static inline bool i40e_container_is_rx(struct i40e_q_vector *q_vector, + struct i40e_ring_container *rc) +{ + return &q_vector->rx == rc; +} + +static inline unsigned int i40e_itr_divisor(struct i40e_q_vector *q_vector) +{ + unsigned int divisor; + + switch (q_vector->vsi->back->hw.phy.link_info.link_speed) { + case I40E_LINK_SPEED_40GB: + divisor = I40E_ITR_ADAPTIVE_MIN_INC * 1024; + break; + case I40E_LINK_SPEED_25GB: + case I40E_LINK_SPEED_20GB: + divisor = I40E_ITR_ADAPTIVE_MIN_INC * 512; + break; + default: + case I40E_LINK_SPEED_10GB: + divisor = I40E_ITR_ADAPTIVE_MIN_INC * 256; + break; + case I40E_LINK_SPEED_1GB: + case I40E_LINK_SPEED_100MB: + divisor = I40E_ITR_ADAPTIVE_MIN_INC * 32; + break; + } + + return divisor; +} + /** - * i40e_set_new_dynamic_itr - Find new ITR level + * i40e_update_itr - update the dynamic ITR value based on statistics + * @q_vector: structure containing interrupt and ring information * @rc: structure containing ring performance data * - * Returns true if ITR changed, false if not - * - * Stores a new ITR value based on packets and byte counts during - * the last interrupt. The advantage of per interrupt computation - * is faster updates and more accurate ITR for the current traffic - * pattern. Constants in this function were computed based on - * theoretical maximum wire speed and thresholds were set based on - * testing data as well as attempting to minimize response time + * Stores a new ITR value based on packets and byte + * counts during the last interrupt. The advantage of per interrupt + * computation is faster updates and more accurate ITR for the current + * traffic pattern. Constants in this function were computed + * based on theoretical maximum wire speed and thresholds were set based + * on testing data as well as attempting to minimize response time * while increasing bulk throughput. **/ -static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) +static void i40e_update_itr(struct i40e_q_vector *q_vector, + struct i40e_ring_container *rc) { - enum i40e_latency_range new_latency_range = rc->latency_range; - u32 new_itr = rc->itr; - int bytes_per_int; - unsigned int usecs, estimated_usecs; + unsigned int avg_wire_size, packets, bytes, itr; + unsigned long next_update = jiffies; - if (rc->total_packets == 0 || !rc->itr) - return false; + /* If we don't have any rings just leave ourselves set for maximum + * possible latency so we take ourselves out of the equation. + */ + if (!rc->ring || !ITR_IS_DYNAMIC(rc->ring->itr_setting)) + return; - usecs = (rc->itr << 1) * ITR_COUNTDOWN_START; - bytes_per_int = rc->total_bytes / usecs; + /* For Rx we want to push the delay up and default to low latency. + * for Tx we want to pull the delay down and default to high latency. + */ + itr = i40e_container_is_rx(q_vector, rc) ? + I40E_ITR_ADAPTIVE_MIN_USECS | I40E_ITR_ADAPTIVE_LATENCY : + I40E_ITR_ADAPTIVE_MAX_USECS | I40E_ITR_ADAPTIVE_LATENCY; + + /* If we didn't update within up to 1 - 2 jiffies we can assume + * that either packets are coming in so slow there hasn't been + * any work, or that there is so much work that NAPI is dealing + * with interrupt moderation and we don't need to do anything. + */ + if (time_after(next_update, rc->next_update)) + goto clear_counts; + + /* If itr_countdown is set it means we programmed an ITR within + * the last 4 interrupt cycles. This has a side effect of us + * potentially firing an early interrupt. In order to work around + * this we need to throw out any data received for a few + * interrupts following the update. + */ + if (q_vector->itr_countdown) { + itr = rc->target_itr; + goto clear_counts; + } - /* The calculations in this algorithm depend on interrupts actually - * firing at the ITR rate. This may not happen if the packet rate is - * really low, or if we've been napi polling. Check to make sure - * that's not the case before we continue. + packets = rc->total_packets; + bytes = rc->total_bytes; + + if (i40e_container_is_rx(q_vector, rc)) { + /* If Rx there are 1 to 4 packets and bytes are less than + * 9000 assume insufficient data to use bulk rate limiting + * approach unless Tx is already in bulk rate limiting. We + * are likely latency driven. + */ + if (packets && packets < 4 && bytes < 9000 && + (q_vector->tx.target_itr & I40E_ITR_ADAPTIVE_LATENCY)) { + itr = I40E_ITR_ADAPTIVE_LATENCY; + goto adjust_by_size; + } + } else if (packets < 4) { + /* If we have Tx and Rx ITR maxed and Tx ITR is running in + * bulk mode and we are receiving 4 or fewer packets just + * reset the ITR_ADAPTIVE_LATENCY bit for latency mode so + * that the Rx can relax. + */ + if (rc->target_itr == I40E_ITR_ADAPTIVE_MAX_USECS && + (q_vector->rx.target_itr & I40E_ITR_MASK) == + I40E_ITR_ADAPTIVE_MAX_USECS) + goto clear_counts; + } else if (packets > 32) { + /* If we have processed over 32 packets in a single interrupt + * for Tx assume we need to switch over to "bulk" mode. + */ + rc->target_itr &= ~I40E_ITR_ADAPTIVE_LATENCY; + } + + /* We have no packets to actually measure against. This means + * either one of the other queues on this vector is active or + * we are a Tx queue doing TSO with too high of an interrupt rate. + * + * Between 4 and 56 we can assume that our current interrupt delay + * is only slightly too low. As such we should increase it by a small + * fixed amount. */ - estimated_usecs = jiffies_to_usecs(jiffies - rc->last_itr_update); - if (estimated_usecs > usecs) { - new_latency_range = I40E_LOW_LATENCY; - goto reset_latency; + if (packets < 56) { + itr = rc->target_itr + I40E_ITR_ADAPTIVE_MIN_INC; + if ((itr & I40E_ITR_MASK) > I40E_ITR_ADAPTIVE_MAX_USECS) { + itr &= I40E_ITR_ADAPTIVE_LATENCY; + itr += I40E_ITR_ADAPTIVE_MAX_USECS; + } + goto clear_counts; + } + + if (packets <= 256) { + itr = min(q_vector->tx.current_itr, q_vector->rx.current_itr); + itr &= I40E_ITR_MASK; + + /* Between 56 and 112 is our "goldilocks" zone where we are + * working out "just right". Just report that our current + * ITR is good for us. + */ + if (packets <= 112) + goto clear_counts; + + /* If packet count is 128 or greater we are likely looking + * at a slight overrun of the delay we want. Try halving + * our delay to see if that will cut the number of packets + * in half per interrupt. + */ + itr /= 2; + itr &= I40E_ITR_MASK; + if (itr < I40E_ITR_ADAPTIVE_MIN_USECS) + itr = I40E_ITR_ADAPTIVE_MIN_USECS; + + goto clear_counts; } - /* simple throttlerate management - * 0-10MB/s lowest (50000 ints/s) - * 10-20MB/s low (20000 ints/s) - * 20-1249MB/s bulk (18000 ints/s) + /* The paths below assume we are dealing with a bulk ITR since + * number of packets is greater than 256. We are just going to have + * to compute a value and try to bring the count under control, + * though for smaller packet sizes there isn't much we can do as + * NAPI polling will likely be kicking in sooner rather than later. + */ + itr = I40E_ITR_ADAPTIVE_BULK; + +adjust_by_size: + /* If packet counts are 256 or greater we can assume we have a gross + * overestimation of what the rate should be. Instead of trying to fine + * tune it just use the formula below to try and dial in an exact value + * give the current packet size of the frame. + */ + avg_wire_size = bytes / packets; + + /* The following is a crude approximation of: + * wmem_default / (size + overhead) = desired_pkts_per_int + * rate / bits_per_byte / (size + ethernet overhead) = pkt_rate + * (desired_pkt_rate / pkt_rate) * usecs_per_sec = ITR value * - * The math works out because the divisor is in 10^(-6) which - * turns the bytes/us input value into MB/s values, but - * make sure to use usecs, as the register values written - * are in 2 usec increments in the ITR registers, and make sure - * to use the smoothed values that the countdown timer gives us. + * Assuming wmem_default is 212992 and overhead is 640 bytes per + * packet, (256 skb, 64 headroom, 320 shared info), we can reduce the + * formula down to + * + * (170 * (size + 24)) / (size + 640) = ITR + * + * We first do some math on the packet size and then finally bitshift + * by 8 after rounding up. We also have to account for PCIe link speed + * difference as ITR scales based on this. */ - switch (new_latency_range) { - case I40E_LOWEST_LATENCY: - if (bytes_per_int > 10) - new_latency_range = I40E_LOW_LATENCY; - break; - case I40E_LOW_LATENCY: - if (bytes_per_int > 20) - new_latency_range = I40E_BULK_LATENCY; - else if (bytes_per_int <= 10) - new_latency_range = I40E_LOWEST_LATENCY; - break; - case I40E_BULK_LATENCY: - default: - if (bytes_per_int <= 20) - new_latency_range = I40E_LOW_LATENCY; - break; + if (avg_wire_size <= 60) { + /* Start at 250k ints/sec */ + avg_wire_size = 4096; + } else if (avg_wire_size <= 380) { + /* 250K ints/sec to 60K ints/sec */ + avg_wire_size *= 40; + avg_wire_size += 1696; + } else if (avg_wire_size <= 1084) { + /* 60K ints/sec to 36K ints/sec */ + avg_wire_size *= 15; + avg_wire_size += 11452; + } else if (avg_wire_size <= 1980) { + /* 36K ints/sec to 30K ints/sec */ + avg_wire_size *= 5; + avg_wire_size += 22420; + } else { + /* plateau at a limit of 30K ints/sec */ + avg_wire_size = 32256; } -reset_latency: - rc->latency_range = new_latency_range; + /* If we are in low latency mode halve our delay which doubles the + * rate to somewhere between 100K to 16K ints/sec + */ + if (itr & I40E_ITR_ADAPTIVE_LATENCY) + avg_wire_size /= 2; - switch (new_latency_range) { - case I40E_LOWEST_LATENCY: - new_itr = I40E_ITR_50K; - break; - case I40E_LOW_LATENCY: - new_itr = I40E_ITR_20K; - break; - case I40E_BULK_LATENCY: - new_itr = I40E_ITR_18K; - break; - default: - break; + /* Resultant value is 256 times larger than it needs to be. This + * gives us room to adjust the value as needed to either increase + * or decrease the value based on link speeds of 10G, 2.5G, 1G, etc. + * + * Use addition as we have already recorded the new latency flag + * for the ITR value. + */ + itr += DIV_ROUND_UP(avg_wire_size, i40e_itr_divisor(q_vector)) * + I40E_ITR_ADAPTIVE_MIN_INC; + + if ((itr & I40E_ITR_MASK) > I40E_ITR_ADAPTIVE_MAX_USECS) { + itr &= I40E_ITR_ADAPTIVE_LATENCY; + itr += I40E_ITR_ADAPTIVE_MAX_USECS; } +clear_counts: + /* write back value */ + rc->target_itr = itr; + + /* next update should occur within next jiffy */ + rc->next_update = next_update + 1; + rc->total_bytes = 0; rc->total_packets = 0; - rc->last_itr_update = jiffies; - - if (new_itr != rc->itr) { - rc->itr = new_itr; - return true; - } - return false; } +#ifndef CONFIG_I40E_DISABLE_PACKET_SPLIT /** * i40e_reuse_rx_page - page flip buffer and store it back on the ring * @rx_ring: rx descriptor ring to store buffers on @@ -1063,6 +1250,33 @@ static void i40e_reuse_rx_page(struct i40e_ring *rx_ring, new_buff->pagecnt_bias = old_buff->pagecnt_bias; } +#endif /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ +#ifdef CONFIG_I40E_DISABLE_PACKET_SPLIT +/** + * i40e_reuse_rx_skb - Recycle unused skb and store it back on the ring + * @rx_ring: rx descriptor ring to store buffers on + * @old_buff: donor buffer to have skb reused + * + * Synchronizes skb for reuse by the adapter + **/ +static void i40e_reuse_rx_skb(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *old_buff) +{ + struct i40e_rx_buffer *new_buff; + u16 nta = rx_ring->next_to_alloc; + + new_buff = &rx_ring->rx_bi[nta]; + + /* update, and store next to alloc */ + nta++; + rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0; + + /* transfer page from old buffer to new buffer */ + new_buff->dma = old_buff->dma; + new_buff->skb = old_buff->skb; +} + +#endif /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ /** * i40e_rx_is_programming_status - check for programming status descriptor * @qw: qword representing status_error_len in CPU ordering @@ -1108,12 +1322,20 @@ static void i40e_clean_programming_status(struct i40e_ring *rx_ring, prefetch(I40E_RX_DESC(rx_ring, ntc)); +#ifdef CONFIG_I40E_DISABLE_PACKET_SPLIT + /* place unused page back on the ring */ + i40e_reuse_rx_skb(rx_ring, rx_buffer); + + /* clear contents of buffer_info */ + rx_buffer->skb = NULL; +#else /* place unused page back on the ring */ i40e_reuse_rx_page(rx_ring, rx_buffer); rx_ring->rx_stats.page_reuse_count++; /* clear contents of buffer_info */ rx_buffer->page = NULL; +#endif id = (qw & I40E_RX_PROG_STATUS_DESC_QW1_PROGID_MASK) >> I40E_RX_PROG_STATUS_DESC_QW1_PROGID_SHIFT; @@ -1143,8 +1365,6 @@ int i40e_setup_tx_descriptors(struct i40e_ring *tx_ring) if (!tx_ring->tx_bi) goto err; - u64_stats_init(&tx_ring->syncp); - /* round up to nearest 4K */ tx_ring->size = tx_ring->count * sizeof(struct i40e_tx_desc); /* add u32 for head writeback, align after this takes care of @@ -1162,6 +1382,7 @@ int i40e_setup_tx_descriptors(struct i40e_ring *tx_ring) tx_ring->next_to_use = 0; tx_ring->next_to_clean = 0; + tx_ring->tx_stats.prev_pkt_ctr = -1; return 0; err: @@ -1192,6 +1413,15 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring) for (i = 0; i < rx_ring->count; i++) { struct i40e_rx_buffer *rx_bi = &rx_ring->rx_bi[i]; +#ifdef CONFIG_I40E_DISABLE_PACKET_SPLIT + if (!rx_bi->skb) + continue; + + dma_unmap_single(rx_ring->dev, rx_bi->dma, + rx_ring->rx_buf_len, DMA_FROM_DEVICE); + dev_kfree_skb(rx_bi->skb); + rx_bi->skb = NULL; +#else /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ if (!rx_bi->page) continue; @@ -1214,6 +1444,7 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring) rx_bi->page = NULL; rx_bi->page_offset = 0; +#endif /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ } bi_size = sizeof(struct i40e_rx_buffer) * rx_ring->count; @@ -1236,6 +1467,10 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring) void i40e_free_rx_resources(struct i40e_ring *rx_ring) { i40e_clean_rx_ring(rx_ring); +#ifdef HAVE_XDP_BUFF_RXQ + if (rx_ring->vsi->type == I40E_VSI_MAIN) + xdp_rxq_info_unreg(&rx_ring->xdp_rxq); +#endif rx_ring->xdp_prog = NULL; kfree(rx_ring->rx_bi); rx_ring->rx_bi = NULL; @@ -1256,6 +1491,7 @@ void i40e_free_rx_resources(struct i40e_ring *rx_ring) int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring) { struct device *dev = rx_ring->dev; + int err = -ENOMEM; int bi_size; /* warn if we are about to overwrite the pointer */ @@ -1264,11 +1500,13 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring) rx_ring->rx_bi = kzalloc(bi_size, GFP_KERNEL); if (!rx_ring->rx_bi) goto err; +#ifdef HAVE_NDO_GET_STATS64 u64_stats_init(&rx_ring->syncp); +#endif /* HAVE_NDO_GET_STATS64 */ /* Round up to nearest 4K */ - rx_ring->size = rx_ring->count * sizeof(union i40e_32byte_rx_desc); + rx_ring->size = rx_ring->count * sizeof(union i40e_rx_desc); rx_ring->size = ALIGN(rx_ring->size, 4096); rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size, &rx_ring->dma, GFP_KERNEL); @@ -1282,14 +1520,22 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring) rx_ring->next_to_alloc = 0; rx_ring->next_to_clean = 0; rx_ring->next_to_use = 0; +#ifdef HAVE_XDP_BUFF_RXQ + /* XDP RX-queue info only needed for RX rings exposed to XDP */ + if (rx_ring->vsi->type == I40E_VSI_MAIN) { + err = xdp_rxq_info_reg(&rx_ring->xdp_rxq, rx_ring->netdev, + rx_ring->queue_index); + if (err < 0) + goto err; + } rx_ring->xdp_prog = rx_ring->vsi->xdp_prog; - +#endif return 0; err: kfree(rx_ring->rx_bi); rx_ring->rx_bi = NULL; - return -ENOMEM; + return err; } /** @@ -1313,6 +1559,46 @@ static inline void i40e_release_rx_desc(struct i40e_ring *rx_ring, u32 val) writel(val, rx_ring->tail); } +#ifdef CONFIG_I40E_DISABLE_PACKET_SPLIT +static bool i40e_alloc_mapped_skb(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *bi) +{ + struct sk_buff *skb = bi->skb; + dma_addr_t dma; + + if (unlikely(skb)) + return true; + + if (likely(!skb)) { + skb = __napi_alloc_skb(&rx_ring->q_vector->napi, + rx_ring->rx_buf_len, + GFP_ATOMIC | __GFP_NOWARN); + if (unlikely(!skb)) { + rx_ring->rx_stats.alloc_buff_failed++; + return false; + } + } + + dma = dma_map_single(rx_ring->dev, skb->data, + rx_ring->rx_buf_len, DMA_FROM_DEVICE); + + /* + * if mapping failed free memory back to system since + * there isn't much point in holding memory we can't use + */ + if (dma_mapping_error(rx_ring->dev, dma)) { + dev_kfree_skb_any(skb); + rx_ring->rx_stats.alloc_buff_failed++; + return false; + } + + bi->skb = skb; + bi->dma = dma; + + return true; +} + +#else /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ /** * i40e_rx_offset - Return expected offset into page to access data * @rx_ring: Ring we are requesting offset of @@ -1376,6 +1662,7 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, return true; } +#endif /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ /** * i40e_receive_skb - Send a completed packet up the stack * @rx_ring: rx ring in play @@ -1386,12 +1673,32 @@ static void i40e_receive_skb(struct i40e_ring *rx_ring, struct sk_buff *skb, u16 vlan_tag) { struct i40e_q_vector *q_vector = rx_ring->q_vector; +#ifdef HAVE_VLAN_RX_REGISTER + struct i40e_vsi *vsi = rx_ring->vsi; +#endif +#ifdef HAVE_VLAN_RX_REGISTER + if (vlan_tag & VLAN_VID_MASK) { + if (!vsi->vlgrp) + dev_kfree_skb_any(skb); + else + vlan_gro_receive(&q_vector->napi, vsi->vlgrp, + vlan_tag, skb); + } else { + napi_gro_receive(&q_vector->napi, skb); + } +#else /* HAVE_VLAN_RX_REGISTER */ +#ifdef NETIF_F_HW_VLAN_CTAG_RX if ((rx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && (vlan_tag & VLAN_VID_MASK)) +#else + if ((rx_ring->netdev->features & NETIF_F_HW_VLAN_RX) && + (vlan_tag & VLAN_VID_MASK)) +#endif /* NETIF_F_HW_VLAN_CTAG_RX */ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); napi_gro_receive(&q_vector->napi, skb); +#endif /* HAVE_VLAN_RX_REGISTER */ } /** @@ -1415,6 +1722,12 @@ bool i40e_alloc_rx_buffers(struct i40e_ring *rx_ring, u16 cleaned_count) bi = &rx_ring->rx_bi[ntu]; do { +#ifdef CONFIG_I40E_DISABLE_PACKET_SPLIT + if (!i40e_alloc_mapped_skb(rx_ring, bi)) + goto no_buffers; + + rx_desc->read.pkt_addr = cpu_to_le64(bi->dma); +#else if (!i40e_alloc_mapped_page(rx_ring, bi)) goto no_buffers; @@ -1428,6 +1741,7 @@ bool i40e_alloc_rx_buffers(struct i40e_ring *rx_ring, u16 cleaned_count) * because each write-back erases this info. */ rx_desc->read.pkt_addr = cpu_to_le64(bi->dma + bi->page_offset); +#endif /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ rx_desc++; bi++; @@ -1459,6 +1773,58 @@ no_buffers: return true; } +#define I40E_XDP_PASS 0 +#define I40E_XDP_CONSUMED BIT(0) +#define I40E_XDP_TX BIT(1) +#define I40E_XDP_REDIR BIT(2) + +static inline void i40e_xdp_ring_update_tail(struct i40e_ring *xdp_ring) +{ + /* Force memory writes to complete before letting h/w + * know there are new descriptors to fetch. + */ + wmb(); + writel_relaxed(xdp_ring->next_to_use, xdp_ring->tail); +} + +#ifdef I40E_ADD_PROBES +static void i40e_rx_extra_counters(struct i40e_vsi *vsi, u32 rx_error, + const struct i40e_rx_ptype_decoded decoded) +{ + bool ipv4; + + ipv4 = (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP) && + (decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4); + + if (ipv4 && + (rx_error & (BIT(I40E_RX_DESC_ERROR_IPE_SHIFT) | + BIT(I40E_RX_DESC_ERROR_EIPE_SHIFT)))) + vsi->back->rx_ip4_cso_err++; + + if (rx_error & BIT(I40E_RX_DESC_ERROR_L4E_SHIFT)) { + if (decoded.inner_prot == I40E_RX_PTYPE_INNER_PROT_TCP) + vsi->back->rx_tcp_cso_err++; + else if (decoded.inner_prot == I40E_RX_PTYPE_INNER_PROT_UDP) + vsi->back->rx_udp_cso_err++; + else if (decoded.inner_prot == I40E_RX_PTYPE_INNER_PROT_SCTP) + vsi->back->rx_sctp_cso_err++; + } + + if ((decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP) && + (decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4)) + vsi->back->rx_ip4_cso++; + if (decoded.inner_prot == I40E_RX_PTYPE_INNER_PROT_TCP) + vsi->back->rx_tcp_cso++; + else if (decoded.inner_prot == I40E_RX_PTYPE_INNER_PROT_UDP) + vsi->back->rx_udp_cso++; + else if (decoded.inner_prot == I40E_RX_PTYPE_INNER_PROT_SCTP) + vsi->back->rx_sctp_cso++; +} + +#endif /* I40E_ADD_PROBES */ +#if defined(HAVE_VXLAN_RX_OFFLOAD) || defined(HAVE_GENEVE_RX_OFFLOAD) || defined(HAVE_UDP_ENC_RX_OFFLOAD) +#define I40E_TUNNEL_SUPPORT +#endif /** * i40e_rx_checksum - Indicate in skb if hw indicated a good cksum * @vsi: the VSI we care about @@ -1488,8 +1854,13 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, skb_checksum_none_assert(skb); /* Rx csum enabled and ip headers found? */ +#ifdef HAVE_NDO_SET_FEATURES if (!(vsi->netdev->features & NETIF_F_RXCSUM)) return; +#else + if (!(vsi->back->flags & I40E_FLAG_RX_CSUM_ENABLED)) + return; +#endif /* did the hardware decode the packet and checksum? */ if (!(rx_status & BIT(I40E_RX_DESC_STATUS_L3L4P_SHIFT))) @@ -1498,12 +1869,19 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, /* both known and outer_ip must be set for the below code to work */ if (!(decoded.known && decoded.outer_ip)) return; +#ifdef I40E_ADD_PROBES + vsi->back->hw_csum_rx_outer++; +#endif ipv4 = (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP) && (decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4); ipv6 = (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP) && (decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6); +#ifdef I40E_ADD_PROBES + i40e_rx_extra_counters(vsi, rx_error, decoded); + +#endif /* I40E_ADD_PROBES */ if (ipv4 && (rx_error & (BIT(I40E_RX_DESC_ERROR_IPE_SHIFT) | BIT(I40E_RX_DESC_ERROR_EIPE_SHIFT)))) @@ -1526,12 +1904,18 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT)) return; +#ifdef I40E_TUNNEL_SUPPORT /* If there is an outer header present that might contain a checksum * we need to bump the checksum level by 1 to reflect the fact that * we are indicating we validated the inner checksum. */ if (decoded.tunnel_type >= I40E_RX_PTYPE_TUNNEL_IP_GRENAT) +#ifdef HAVE_SKBUFF_CSUM_LEVEL skb->csum_level = 1; +#else + skb->encapsulation = 1; +#endif +#endif /* I40E_TUNNEL_SUPPORT */ /* Only report checksum unnecessary for TCP, UDP, or SCTP */ switch (decoded.inner_prot) { @@ -1539,11 +1923,10 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, case I40E_RX_PTYPE_INNER_PROT_UDP: case I40E_RX_PTYPE_INNER_PROT_SCTP: skb->ip_summed = CHECKSUM_UNNECESSARY; - /* fall though */ + /* fall through */ default: break; } - return; checksum_fail: @@ -1556,7 +1939,7 @@ checksum_fail: * * Returns a hash type to be used by skb_set_hash **/ -static inline int i40e_ptype_to_htype(u8 ptype) +static inline enum pkt_hash_types i40e_ptype_to_htype(u8 ptype) { struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(ptype); @@ -1577,12 +1960,15 @@ static inline int i40e_ptype_to_htype(u8 ptype) * i40e_rx_hash - set the hash value in the skb * @ring: descriptor ring * @rx_desc: specific descriptor + * @skb: skb currently being received and modified + * @rx_ptype: Rx packet type **/ static inline void i40e_rx_hash(struct i40e_ring *ring, union i40e_rx_desc *rx_desc, struct sk_buff *skb, u8 rx_ptype) { +#ifdef NETIF_F_RXHASH u32 hash; const __le64 rss_mask = cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH << @@ -1595,6 +1981,7 @@ static inline void i40e_rx_hash(struct i40e_ring *ring, hash = le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss); skb_set_hash(skb, hash, i40e_ptype_to_htype(rx_ptype)); } +#endif /* NETIF_F_RXHASH */ } /** @@ -1613,6 +2000,7 @@ void i40e_process_skb_fields(struct i40e_ring *rx_ring, union i40e_rx_desc *rx_desc, struct sk_buff *skb, u8 rx_ptype) { +#ifdef HAVE_PTP_1588_CLOCK u64 qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); u32 rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >> I40E_RXD_QW1_STATUS_SHIFT; @@ -1622,6 +2010,7 @@ void i40e_process_skb_fields(struct i40e_ring *rx_ring, if (unlikely(tsynvalid)) i40e_ptp_rx_hwtstamp(rx_ring->vsi->back, skb, tsyn); +#endif /* HAVE_PTP_1588_CLOCK */ i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); @@ -1649,7 +2038,6 @@ void i40e_process_skb_fields(struct i40e_ring *rx_ring, **/ static bool i40e_cleanup_headers(struct i40e_ring *rx_ring, struct sk_buff *skb, union i40e_rx_desc *rx_desc) - { /* XDP packets use error pointer so abort at this point */ if (IS_ERR(skb)) @@ -1673,6 +2061,51 @@ static bool i40e_cleanup_headers(struct i40e_ring *rx_ring, struct sk_buff *skb, return false; } +#ifdef CONFIG_I40E_DISABLE_PACKET_SPLIT +/** + * i40e_get_rx_buffer - Fetch Rx buffer and synchronize data for use + * @rx_ring: rx descriptor ring to transact packets on + * @size: size of buffer to add to skb + * + * This function will pull an Rx buffer from the ring and synchronize it + * for use by the CPU. + * + * ONE-BUFF version + */ +static struct i40e_rx_buffer *i40e_get_rx_buffer(struct i40e_ring *rx_ring, + const unsigned int size) +{ + struct i40e_rx_buffer *rx_buffer; + + rx_buffer = &rx_ring->rx_bi[rx_ring->next_to_clean]; + + /* we are reusing so sync this buffer for CPU use */ + dma_unmap_single(rx_ring->dev, rx_buffer->dma, + rx_ring->rx_buf_len, DMA_FROM_DEVICE); + + prefetch(rx_buffer->skb->data); + + return rx_buffer; +} + +/** + * i40e_put_rx_buffer - Clean up used buffer and either recycle or free + * @rx_ring: rx descriptor ring to transact packets on + * @rx_buffer: rx buffer to pull data from + * + * This function will clean up the contents of the rx_buffer. It will + * either recycle the bufer or unmap it and free the associated resources. + * + * ONE-BUFF version + */ +static void i40e_put_rx_buffer(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *rx_buffer) +{ + /* clear contents of buffer_info */ + rx_buffer->skb = NULL; +} + +#else /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ /** * i40e_page_is_reusable - check if any reuse is possible * @page: page struct to check @@ -1737,10 +2170,17 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer) * the pagecnt_bias and page count so that we fully restock the * number of references the driver holds. */ +#ifdef HAVE_PAGE_COUNT_BULK_UPDATE if (unlikely(!pagecnt_bias)) { page_ref_add(page, USHRT_MAX); rx_buffer->pagecnt_bias = USHRT_MAX; } +#else + if (likely(!pagecnt_bias)) { + get_page(page); + rx_buffer->pagecnt_bias = 1; + } +#endif return true; } @@ -1765,7 +2205,7 @@ static void i40e_add_rx_frag(struct i40e_ring *rx_ring, #if (PAGE_SIZE < 8192) unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; #else - unsigned int truesize = SKB_DATA_ALIGN(size + i40e_rx_offset(rx_ring)); + unsigned int truesize = SKB_DATA_ALIGN(size + i40e_rx_offset(rx_ring)); #endif skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, @@ -1822,11 +2262,13 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, struct i40e_rx_buffer *rx_buffer, struct xdp_buff *xdp) { - unsigned int size = xdp->data_end - xdp->data; + unsigned int size = (u8 *)xdp->data_end - (u8 *)xdp->data; + #if (PAGE_SIZE < 8192) unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; #else - unsigned int truesize = SKB_DATA_ALIGN(size); + unsigned int truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(I40E_SKB_PAD + size); #endif unsigned int headlen; struct sk_buff *skb; @@ -1834,7 +2276,7 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, /* prefetch first cache line of first page */ prefetch(xdp->data); #if L1_CACHE_BYTES < 128 - prefetch(xdp->data + L1_CACHE_BYTES); + prefetch((void *)((u8 *)xdp->data + L1_CACHE_BYTES)); #endif /* allocate a skb to store the frags */ @@ -1847,7 +2289,8 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, /* Determine available headroom for copy */ headlen = size; if (headlen > I40E_RX_HDR_SIZE) - headlen = eth_get_headlen(xdp->data, I40E_RX_HDR_SIZE); + headlen = eth_get_headlen(skb->dev, xdp->data, + I40E_RX_HDR_SIZE); /* align pull length to size of long to optimize memcpy performance */ memcpy(__skb_put(skb, headlen), xdp->data, @@ -1874,10 +2317,11 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, return skb; } +#ifdef HAVE_SWIOTLB_SKIP_CPU_SYNC /** * i40e_build_skb - Build skb around an existing buffer - * @rx_ring: Rx descriptor ring to transact packets on - * @rx_buffer: Rx buffer to pull data from + * @rx_ring: rx descriptor ring to transact packets on + * @rx_buffer: rx buffer to pull data from * @xdp: xdp_buff pointing to the data * * This function builds an skb around an existing Rx buffer, taking care @@ -1887,12 +2331,14 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, struct i40e_rx_buffer *rx_buffer, struct xdp_buff *xdp) { - unsigned int size = xdp->data_end - xdp->data; + unsigned int size = (u8 *)xdp->data_end - (u8 *)xdp->data; + #if (PAGE_SIZE < 8192) unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; #else unsigned int truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + - SKB_DATA_ALIGN(I40E_SKB_PAD + size); + SKB_DATA_ALIGN(xdp->data_end - + xdp->data_hard_start); #endif struct sk_buff *skb; @@ -1907,7 +2353,7 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, return NULL; /* update pointers within the skb to store the data */ - skb_reserve(skb, I40E_SKB_PAD); + skb_reserve(skb, xdp->data - xdp->data_hard_start); __skb_put(skb, size); /* buffer is used by skb, update page_offset */ @@ -1920,13 +2366,14 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, return skb; } +#endif /* HAVE_SWIOTLB_SKIP_CPU_SYNC */ /** * i40e_put_rx_buffer - Clean up used buffer and either recycle or free * @rx_ring: rx descriptor ring to transact packets on * @rx_buffer: rx buffer to pull data from * * This function will clean up the contents of the rx_buffer. It will - * either recycle the bufer or unmap it and free the associated resources. + * either recycle the buffer or unmap it and free the associated resources. */ static void i40e_put_rx_buffer(struct i40e_ring *rx_ring, struct i40e_rx_buffer *rx_buffer) @@ -1948,6 +2395,7 @@ static void i40e_put_rx_buffer(struct i40e_ring *rx_ring, rx_buffer->page = NULL; } +#endif /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ /** * i40e_is_non_eop - process handling of non-EOP buffers * @rx_ring: Rx ring being processed @@ -1981,12 +2429,26 @@ static bool i40e_is_non_eop(struct i40e_ring *rx_ring, return true; } -#define I40E_XDP_PASS 0 -#define I40E_XDP_CONSUMED 1 -#define I40E_XDP_TX 2 +#ifdef HAVE_XDP_SUPPORT +#ifdef HAVE_XDP_FRAME_STRUCT +static int i40e_xmit_xdp_ring(struct xdp_frame *xdp, + struct i40e_ring *xdp_ring); +static int i40e_xmit_xdp_tx_ring(struct xdp_buff *xdp, + struct i40e_ring *xdp_ring) +{ + struct xdp_frame *xdpf = convert_to_xdp_frame(xdp); + + if (unlikely(!xdpf)) + return I40E_XDP_CONSUMED; + + return i40e_xmit_xdp_ring(xdpf, xdp_ring); +} +#else static int i40e_xmit_xdp_ring(struct xdp_buff *xdp, struct i40e_ring *xdp_ring); +#endif +#endif /** * i40e_run_xdp - run an XDP program @@ -1997,9 +2459,11 @@ static struct sk_buff *i40e_run_xdp(struct i40e_ring *rx_ring, struct xdp_buff *xdp) { int result = I40E_XDP_PASS; +#ifdef HAVE_XDP_SUPPORT struct i40e_ring *xdp_ring; struct bpf_prog *xdp_prog; u32 act; + int err; rcu_read_lock(); xdp_prog = READ_ONCE(rx_ring->xdp_prog); @@ -2007,26 +2471,46 @@ static struct sk_buff *i40e_run_xdp(struct i40e_ring *rx_ring, if (!xdp_prog) goto xdp_out; + prefetchw(xdp->data_hard_start); /* xdp_frame write */ + act = bpf_prog_run_xdp(xdp_prog, xdp); switch (act) { case XDP_PASS: + rx_ring->xdp_stats.xdp_pass++; break; case XDP_TX: xdp_ring = rx_ring->vsi->xdp_rings[rx_ring->queue_index]; +#ifdef HAVE_XDP_FRAME_STRUCT + result = i40e_xmit_xdp_tx_ring(xdp, xdp_ring); +#else result = i40e_xmit_xdp_ring(xdp, xdp_ring); +#endif + rx_ring->xdp_stats.xdp_tx++; + break; + case XDP_REDIRECT: + err = xdp_do_redirect(rx_ring->netdev, xdp, xdp_prog); + result = !err ? I40E_XDP_REDIR : I40E_XDP_CONSUMED; + if (!err) + rx_ring->xdp_stats.xdp_redirect++; + else + rx_ring->xdp_stats.xdp_redirect_fail++; break; default: bpf_warn_invalid_xdp_action(act); + /* fallthrough -- abort and drop */ case XDP_ABORTED: trace_xdp_exception(rx_ring->netdev, xdp_prog, act); + rx_ring->xdp_stats.xdp_unknown++; /* fallthrough -- handle aborts by dropping packet */ case XDP_DROP: result = I40E_XDP_CONSUMED; + rx_ring->xdp_stats.xdp_drop++; break; } xdp_out: rcu_read_unlock(); - return ERR_PTR(-result); +#endif /* HAVE_XDP_SUPPORT */ + return (struct sk_buff *)ERR_PTR(-result); } /** @@ -2067,12 +2551,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) unsigned int total_rx_bytes = 0, total_rx_packets = 0; struct sk_buff *skb = rx_ring->skb; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); - bool failure = false, xdp_xmit = false; + unsigned int xdp_xmit = 0; + bool failure = false; + struct xdp_buff xdp; + +#ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; +#endif while (likely(total_rx_packets < (unsigned int)budget)) { struct i40e_rx_buffer *rx_buffer; union i40e_rx_desc *rx_desc; - struct xdp_buff xdp; unsigned int size; u16 vlan_tag; u8 rx_ptype; @@ -2114,19 +2603,27 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) rx_buffer = i40e_get_rx_buffer(rx_ring, size); /* retrieve a buffer from the ring */ +#ifdef CONFIG_I40E_DISABLE_PACKET_SPLIT + /* we are leaking memory if an skb is already present */ + WARN_ON(skb); + skb = rx_buffer->skb; + __skb_put(skb, size); +#else if (!skb) { xdp.data = page_address(rx_buffer->page) + rx_buffer->page_offset; - xdp.data_hard_start = xdp.data - - i40e_rx_offset(rx_ring); - xdp.data_end = xdp.data + size; + xdp.data_hard_start = (void *)((u8 *)xdp.data - + i40e_rx_offset(rx_ring)); + xdp.data_end = (void *)((u8 *)xdp.data + size); skb = i40e_run_xdp(rx_ring, &xdp); } if (IS_ERR(skb)) { - if (PTR_ERR(skb) == -I40E_XDP_TX) { - xdp_xmit = true; + unsigned int xdp_res = -PTR_ERR(skb); + + if (xdp_res & (I40E_XDP_TX | I40E_XDP_REDIR)) { + xdp_xmit |= xdp_res; i40e_rx_buffer_flip(rx_ring, rx_buffer, size); } else { rx_buffer->pagecnt_bias++; @@ -2135,8 +2632,10 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) total_rx_packets++; } else if (skb) { i40e_add_rx_frag(rx_ring, rx_buffer, skb, size); +#ifdef HAVE_SWIOTLB_SKIP_CPU_SYNC } else if (ring_uses_build_skb(rx_ring)) { skb = i40e_build_skb(rx_ring, rx_buffer, &xdp); +#endif } else { skb = i40e_construct_skb(rx_ring, rx_buffer, &xdp); } @@ -2147,6 +2646,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) rx_buffer->pagecnt_bias++; break; } +#endif /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ i40e_put_rx_buffer(rx_ring, rx_buffer); cleaned_count++; @@ -2180,19 +2680,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) total_rx_packets++; } - if (xdp_xmit) { - struct i40e_ring *xdp_ring; + if (xdp_xmit & I40E_XDP_REDIR) + xdp_do_flush_map(); - xdp_ring = rx_ring->vsi->xdp_rings[rx_ring->queue_index]; + if (xdp_xmit & I40E_XDP_TX) { + struct i40e_ring *xdp_ring = + rx_ring->vsi->xdp_rings[rx_ring->queue_index]; - /* Force memory writes to complete before letting h/w - * know there are new descriptors to fetch. - */ - wmb(); - - writel(xdp_ring->next_to_use, xdp_ring->tail); + i40e_xdp_ring_update_tail(xdp_ring); } - rx_ring->skb = skb; u64_stats_update_begin(&rx_ring->syncp); @@ -2206,31 +2702,45 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) return failure ? budget : (int)total_rx_packets; } -static u32 i40e_buildreg_itr(const int type, const u16 itr) +static inline u32 i40e_buildreg_itr(const int type, u16 itr) { u32 val; + /* We don't bother with setting the CLEARPBA bit as the data sheet + * points out doing so is "meaningless since it was already + * auto-cleared". The auto-clearing happens when the interrupt is + * asserted. + * + * Hardware errata 28 for also indicates that writing to a + * xxINT_DYN_CTLx CSR with INTENA_MSK (bit 31) set to 0 will clear + * an event in the PBA anyway so we need to rely on the automask + * to hold pending events for us until the interrupt is re-enabled + * + * The itr value is reported in microseconds, and the register + * value is recorded in 2 microsecond units. For this reason we + * only need to shift by the interval shift - 1 instead of the + * full value. + */ + itr &= I40E_ITR_MASK; + val = I40E_PFINT_DYN_CTLN_INTENA_MASK | - /* Don't clear PBA because that can cause lost interrupts that - * came in while we were cleaning/polling - */ (type << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT) | - (itr << I40E_PFINT_DYN_CTLN_INTERVAL_SHIFT); + (itr << (I40E_PFINT_DYN_CTLN_INTERVAL_SHIFT - 1)); return val; } /* a small macro to shorten up some long lines */ #define INTREG I40E_PFINT_DYN_CTLN -static inline int get_rx_itr(struct i40e_vsi *vsi, int idx) -{ - return vsi->rx_rings[idx]->rx_itr_setting; -} -static inline int get_tx_itr(struct i40e_vsi *vsi, int idx) -{ - return vsi->tx_rings[idx]->tx_itr_setting; -} +/* The act of updating the ITR will cause it to immediately trigger. In order + * to prevent this from throwing off adaptive update statistics we defer the + * update so that it can only happen so often. So after either Tx or Rx are + * updated we make the adaptive scheme wait until either the ITR completely + * expires via the next_update expiration or we have been through at least + * 3 interrupts. + */ +#define ITR_COUNTDOWN_START 3 /** * i40e_update_enable_itr - Update itr and re-enable MSIX interrupt @@ -2242,79 +2752,57 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) { struct i40e_hw *hw = &vsi->back->hw; - bool rx = false, tx = false; - u32 rxval, txval; - int vector; - int idx = q_vector->v_idx; - int rx_itr_setting, tx_itr_setting; + u32 intval; /* If we don't have MSIX, then we only need to re-enable icr0 */ if (!(vsi->back->flags & I40E_FLAG_MSIX_ENABLED)) { - i40e_irq_dynamic_enable_icr0(vsi->back, false); + i40e_irq_dynamic_enable_icr0(vsi->back); return; } - vector = (q_vector->v_idx + vsi->base_vector); - - /* avoid dynamic calculation if in countdown mode OR if - * all dynamic is disabled - */ - rxval = txval = i40e_buildreg_itr(I40E_ITR_NONE, 0); - - rx_itr_setting = get_rx_itr(vsi, idx); - tx_itr_setting = get_tx_itr(vsi, idx); - - if (q_vector->itr_countdown > 0 || - (!ITR_IS_DYNAMIC(rx_itr_setting) && - !ITR_IS_DYNAMIC(tx_itr_setting))) { - goto enable_int; - } - - if (ITR_IS_DYNAMIC(rx_itr_setting)) { - rx = i40e_set_new_dynamic_itr(&q_vector->rx); - rxval = i40e_buildreg_itr(I40E_RX_ITR, q_vector->rx.itr); - } - - if (ITR_IS_DYNAMIC(tx_itr_setting)) { - tx = i40e_set_new_dynamic_itr(&q_vector->tx); - txval = i40e_buildreg_itr(I40E_TX_ITR, q_vector->tx.itr); - } - - if (rx || tx) { - /* get the higher of the two ITR adjustments and - * use the same value for both ITR registers - * when in adaptive mode (Rx and/or Tx) - */ - u16 itr = max(q_vector->tx.itr, q_vector->rx.itr); + /* These will do nothing if dynamic updates are not enabled */ + i40e_update_itr(q_vector, &q_vector->tx); + i40e_update_itr(q_vector, &q_vector->rx); - q_vector->tx.itr = q_vector->rx.itr = itr; - txval = i40e_buildreg_itr(I40E_TX_ITR, itr); - tx = true; - rxval = i40e_buildreg_itr(I40E_RX_ITR, itr); - rx = true; - } - - /* only need to enable the interrupt once, but need - * to possibly update both ITR values + /* This block of logic allows us to get away with only updating + * one ITR value with each interrupt. The idea is to perform a + * pseudo-lazy update with the following criteria. + * + * 1. Rx is given higher priority than Tx if both are in same state + * 2. If we must reduce an ITR that is given highest priority. + * 3. We then give priority to increasing ITR based on amount. */ - if (rx) { - /* set the INTENA_MSK_MASK so that this first write - * won't actually enable the interrupt, instead just - * updating the ITR (it's bit 31 PF and VF) + if (q_vector->rx.target_itr < q_vector->rx.current_itr) { + /* Rx ITR needs to be reduced, this is highest priority */ + intval = i40e_buildreg_itr(I40E_RX_ITR, + q_vector->rx.target_itr); + q_vector->rx.current_itr = q_vector->rx.target_itr; + q_vector->itr_countdown = ITR_COUNTDOWN_START; + } else if ((q_vector->tx.target_itr < q_vector->tx.current_itr) || + ((q_vector->rx.target_itr - q_vector->rx.current_itr) < + (q_vector->tx.target_itr - q_vector->tx.current_itr))) { + /* Tx ITR needs to be reduced, this is second priority + * Tx ITR needs to be increased more than Rx, fourth priority */ - rxval |= BIT(31); - /* don't check _DOWN because interrupt isn't being enabled */ - wr32(hw, INTREG(vector - 1), rxval); + intval = i40e_buildreg_itr(I40E_TX_ITR, + q_vector->tx.target_itr); + q_vector->tx.current_itr = q_vector->tx.target_itr; + q_vector->itr_countdown = ITR_COUNTDOWN_START; + } else if (q_vector->rx.current_itr != q_vector->rx.target_itr) { + /* Rx ITR needs to be increased, third priority */ + intval = i40e_buildreg_itr(I40E_RX_ITR, + q_vector->rx.target_itr); + q_vector->rx.current_itr = q_vector->rx.target_itr; + q_vector->itr_countdown = ITR_COUNTDOWN_START; + } else { + /* No ITR update, lowest priority */ + intval = i40e_buildreg_itr(I40E_ITR_NONE, 0); + if (q_vector->itr_countdown) + q_vector->itr_countdown--; } -enable_int: if (!test_bit(__I40E_VSI_DOWN, vsi->state)) - wr32(hw, INTREG(vector - 1), txval); - - if (q_vector->itr_countdown) - q_vector->itr_countdown--; - else - q_vector->itr_countdown = ITR_COUNTDOWN_START; + wr32(hw, INTREG(q_vector->reg_idx), intval); } /** @@ -2332,6 +2820,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) container_of(napi, struct i40e_q_vector, napi); struct i40e_vsi *vsi = q_vector->vsi; struct i40e_ring *ring; + u64 flags = vsi->back->flags; bool clean_complete = true; bool arm_wb = false; int budget_per_ring; @@ -2372,8 +2861,15 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) clean_complete = false; } +#ifndef HAVE_NETDEV_NAPI_LIST + /* if netdev is disabled we need to stop polling */ + if (!netif_running(vsi->netdev)) + clean_complete = true; + +#endif /* If work not completed, return budget and polling will return */ if (!clean_complete) { +#ifdef HAVE_IRQ_AFFINITY_NOTIFY int cpu_id = smp_processor_id(); /* It is possible that the interrupt affinity has changed but, @@ -2393,6 +2889,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) /* Return budget-1 so that polling stops */ return budget - 1; } +#endif /* HAVE_IRQ_AFFINITY_NOTIFY */ tx_only: if (arm_wb) { q_vector->tx.ring[0].tx_stats.tx_force_wb++; @@ -2401,7 +2898,7 @@ tx_only: return budget; } - if (vsi->back->flags & I40E_TXR_FLAGS_WB_ON_ITR) + if (flags & I40E_TXR_FLAGS_WB_ON_ITR) q_vector->arm_wb_state = false; /* Work is done so exit the polling mode and re-enable the interrupt */ @@ -2438,7 +2935,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, if (!(pf->flags & I40E_FLAG_FD_ATR_ENABLED)) return; - if (pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED) + if (test_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state)) return; /* if sampling is disabled do nothing */ @@ -2450,8 +2947,12 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, return; /* snag network header to get L4 type and address */ - hdr.network = (tx_flags & I40E_TX_FLAGS_UDP_TUNNEL) ? +#ifdef HAVE_SKB_INNER_NETWORK_HEADER + hdr.network = (tx_flags & I40E_TX_FLAGS_TUNNEL) ? skb_inner_network_header(skb) : skb_network_header(skb); +#else + hdr.network = skb_network_header(skb); +#endif /* HAVE_SKB_INNER_NETWORK_HEADER */ /* Note: tx_flags gets modified to reflect inner protocols in * tx_enable_csum function if encap is enabled. @@ -2466,8 +2967,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, unsigned int h_offset = inner_hlen; /* this function updates h_offset to the end of the header */ - l4_proto = - ipv6_find_hdr(skb, &h_offset, IPPROTO_TCP, NULL, NULL); + l4_proto = ipv6_find_hdr(skb, &h_offset, IPPROTO_TCP, NULL, NULL); /* hlen will contain our best estimate of the tcp header */ hlen = h_offset - inner_hlen; } @@ -2478,8 +2978,9 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, th = (struct tcphdr *)(hdr.network + hlen); /* Due to lack of space, no more new filters can be programmed */ - if (th->syn && (pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED)) + if (th->syn && test_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state)) return; + if (pf->flags & I40E_FLAG_HW_ATR_EVICT_ENABLED) { /* HW ATR eviction will take care of removing filters on FIN * and RST packets. @@ -2531,7 +3032,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, I40E_TXD_FLTR_QW1_FD_STATUS_SHIFT; dtype_cmd |= I40E_TXD_FLTR_QW1_CNT_ENA_MASK; - if (!(tx_flags & I40E_TX_FLAGS_UDP_TUNNEL)) + if (!(tx_flags & I40E_TX_FLAGS_TUNNEL)) dtype_cmd |= ((u32)I40E_FD_ATR_STAT_IDX(pf->hw.pf_id) << I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) & @@ -2570,8 +3071,13 @@ static inline int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, __be16 protocol = skb->protocol; u32 tx_flags = 0; +#ifdef NETIF_F_HW_VLAN_CTAG_RX if (protocol == htons(ETH_P_8021Q) && !(tx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_TX)) { +#else + if (protocol == htons(ETH_P_8021Q) && + !(tx_ring->netdev->features & NETIF_F_HW_VLAN_TX)) { +#endif /* When HW VLAN acceleration is turned off by the user the * stack sets the protocol to 8021q so that the driver * can take any steps required to support the SW only @@ -2629,6 +3135,14 @@ out: return 0; } +#ifndef HAVE_ENCAP_TSO_OFFLOAD +#define inner_ip_hdr(skb) 0 +#define inner_tcp_hdr(skb) 0 +#define inner_ipv6_hdr(skb) 0 +#define inner_tcp_hdrlen(skb) 0 +#define inner_tcp_hdrlen(skb) 0 +#define skb_inner_transport_header(skb) ((skb)->data) +#endif /* HAVE_ENCAP_TSO_OFFLOAD */ /** * i40e_tso - set up the tso context descriptor * @first: pointer to first Tx buffer for xmit @@ -2677,14 +3191,32 @@ static int i40e_tso(struct i40e_tx_buffer *first, u8 *hdr_len, ip.v6->payload_len = 0; } +#ifdef HAVE_ENCAP_TSO_OFFLOAD if (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE | +#ifdef NETIF_F_GSO_PARTIAL SKB_GSO_GRE_CSUM | +#endif +#ifdef NETIF_F_GSO_IPXIP4 SKB_GSO_IPXIP4 | +#ifdef NETIF_F_GSO_IPXIP6 SKB_GSO_IPXIP6 | +#endif +#else +#ifdef NETIF_F_GSO_IPIP + SKB_GSO_IPIP | +#endif +#ifdef NETIF_F_GSO_SIT + SKB_GSO_SIT | +#endif +#endif SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM)) { +#ifndef NETIF_F_GSO_PARTIAL + if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM) { +#else if (!(skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) && (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM)) { +#endif l4.udp->len = 0; /* determine offset of outer transport header */ @@ -2709,6 +3241,7 @@ static int i40e_tso(struct i40e_tx_buffer *first, u8 *hdr_len, } } +#endif /* HAVE_ENCAP_TSO_OFFLOAD */ /* determine offset of inner transport header */ l4_offset = l4.hdr - skb->data; @@ -2723,7 +3256,14 @@ static int i40e_tso(struct i40e_tx_buffer *first, u8 *hdr_len, gso_size = skb_shinfo(skb)->gso_size; gso_segs = skb_shinfo(skb)->gso_segs; - /* update GSO size and bytecount with header size */ +#ifndef HAVE_NDO_FEATURES_CHECK + /* too small a TSO segment size causes problems */ + if (gso_size < 64) { + gso_size = 64; + gso_segs = DIV_ROUND_UP(skb->len - *hdr_len, 64); + } +#endif + /* update gso size and bytecount with header size */ first->gso_segs = gso_segs; first->bytecount += (first->gso_segs - 1) * *hdr_len; @@ -2737,6 +3277,7 @@ static int i40e_tso(struct i40e_tx_buffer *first, u8 *hdr_len, return 1; } +#ifdef HAVE_PTP_1588_CLOCK /** * i40e_tsyn - set up the tsyn context descriptor * @tx_ring: ptr to the ring to send @@ -2751,7 +3292,11 @@ static int i40e_tsyn(struct i40e_ring *tx_ring, struct sk_buff *skb, { struct i40e_pf *pf; +#ifdef SKB_SHARED_TX_IS_UNION + if (likely(!(skb_tx(skb)->hardware))) +#else if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))) +#endif return 0; /* Tx timestamps cannot be sampled when doing TSO */ @@ -2767,7 +3312,11 @@ static int i40e_tsyn(struct i40e_ring *tx_ring, struct sk_buff *skb, if (pf->ptp_tx && !test_and_set_bit_lock(__I40E_PTP_TX_IN_PROGRESS, pf->state)) { +#ifdef SKB_SHARED_TX_IS_UNION + skb_tx(skb)->in_progress = 1; +#else skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; +#endif pf->ptp_tx_start = jiffies; pf->ptp_tx_skb = skb_get(skb); } else { @@ -2781,6 +3330,7 @@ static int i40e_tsyn(struct i40e_ring *tx_ring, struct sk_buff *skb, return 1; } +#endif /* HAVE_PTP_1588_CLOCK */ /** * i40e_tx_enable_csum - Enable Tx checksum offloads * @skb: send buffer @@ -2819,6 +3369,7 @@ static int i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, /* compute outer L2 header size */ offset = ((ip.hdr - skb->data) / 2) << I40E_TX_DESC_LENGTH_MACLEN_SHIFT; +#ifdef HAVE_ENCAP_CSUM_OFFLOAD if (skb->encapsulation) { u32 tunnel = 0; /* define outer network header type */ @@ -2842,17 +3393,29 @@ static int i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, switch (l4_proto) { case IPPROTO_UDP: tunnel |= I40E_TXD_CTX_UDP_TUNNELING; - *tx_flags |= I40E_TX_FLAGS_UDP_TUNNEL; + *tx_flags |= I40E_TX_FLAGS_TUNNEL; break; +#ifdef HAVE_GRE_ENCAP_OFFLOAD case IPPROTO_GRE: tunnel |= I40E_TXD_CTX_GRE_TUNNELING; - *tx_flags |= I40E_TX_FLAGS_UDP_TUNNEL; + *tx_flags |= I40E_TX_FLAGS_TUNNEL; + /* There was a long-standing issue in GRE where GSO + * was not setting the outer transport header unless + * a GRE checksum was requested. This was fixed in + * the 4.6 version of the kernel. In the 4.7 kernel + * support for GRE over IPv6 was added to GSO. So we + * can assume this workaround for all IPv4 headers + * without impacting later versions of the GRE. + */ + if (ip.v4->version == 4) + l4.hdr = ip.hdr + (ip.v4->ihl * 4); break; case IPPROTO_IPIP: case IPPROTO_IPV6: - *tx_flags |= I40E_TX_FLAGS_UDP_TUNNEL; + *tx_flags |= I40E_TX_FLAGS_TUNNEL; l4.hdr = skb_inner_network_header(skb); break; +#endif default: if (*tx_flags & I40E_TX_FLAGS_TSO) return -1; @@ -2861,6 +3424,11 @@ static int i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, return 0; } +#ifdef I40E_ADD_PROBES + if (*tx_flags & I40E_TX_FLAGS_IPV4) + if (*tx_flags & I40E_TX_FLAGS_TSO) + tx_ring->vsi->back->tx_ip4_cso++; +#endif /* compute outer L3 header size */ tunnel |= ((l4.hdr - ip.hdr) / 4) << I40E_TXD_CTX_QW0_EXT_IPLEN_SHIFT; @@ -2874,7 +3442,9 @@ static int i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, /* indicate if we need to offload outer UDP header */ if ((*tx_flags & I40E_TX_FLAGS_TSO) && +#ifdef NETIF_F_GSO_PARTIAL !(skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) && +#endif (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM)) tunnel |= I40E_TXD_CTX_QW0_L4T_CS_MASK; @@ -2892,16 +3462,22 @@ static int i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, if (ip.v6->version == 6) *tx_flags |= I40E_TX_FLAGS_IPV6; } +#endif /* HAVE_ENCAP_CSUM_OFFLOAD */ /* Enable IP checksum offloads */ if (*tx_flags & I40E_TX_FLAGS_IPV4) { l4_proto = ip.v4->protocol; +#ifdef I40E_ADD_PROBES + if (*tx_flags & I40E_TX_FLAGS_TSO) + tx_ring->vsi->back->tx_ip4_cso++; +#endif /* the stack computes the IP header already, the only time we * need the hardware to recompute it is in the case of TSO. */ cmd |= (*tx_flags & I40E_TX_FLAGS_TSO) ? I40E_TX_DESC_CMD_IIPT_IPV4_CSUM : I40E_TX_DESC_CMD_IIPT_IPV4; +#ifdef NETIF_F_IPV6_CSUM } else if (*tx_flags & I40E_TX_FLAGS_IPV6) { cmd |= I40E_TX_DESC_CMD_IIPT_IPV6; @@ -2910,6 +3486,7 @@ static int i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, if (l4.hdr != exthdr) ipv6_skip_exthdr(skb, exthdr - skb->data, &l4_proto, &frag_off); +#endif } /* compute inner L3 header size */ @@ -2921,18 +3498,29 @@ static int i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, /* enable checksum offloads */ cmd |= I40E_TX_DESC_CMD_L4T_EOFT_TCP; offset |= l4.tcp->doff << I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT; +#ifdef I40E_ADD_PROBES + tx_ring->vsi->back->tx_tcp_cso++; +#endif break; case IPPROTO_SCTP: /* enable SCTP checksum offload */ +#ifdef HAVE_SCTP cmd |= I40E_TX_DESC_CMD_L4T_EOFT_SCTP; offset |= (sizeof(struct sctphdr) >> 2) << I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT; +#ifdef I40E_ADD_PROBES + tx_ring->vsi->back->tx_sctp_cso++; +#endif +#endif /* HAVE_SCTP */ break; case IPPROTO_UDP: /* enable UDP checksum offload */ cmd |= I40E_TX_DESC_CMD_L4T_EOFT_UDP; offset |= (sizeof(struct udphdr) >> 2) << I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT; +#ifdef I40E_ADD_PROBES + tx_ring->vsi->back->tx_udp_cso++; +#endif break; default: if (*tx_flags & I40E_TX_FLAGS_TSO) @@ -3016,7 +3604,7 @@ int __i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size) **/ bool __i40e_chk_linearize(struct sk_buff *skb) { - const struct skb_frag_struct *frag, *stale; + const skb_frag_t *frag, *stale; int nr_frags, sum; /* no need to check if number of frags is less than 7 */ @@ -3048,10 +3636,30 @@ bool __i40e_chk_linearize(struct sk_buff *skb) /* Walk through fragments adding latest fragment, testing it, and * then removing stale fragments from the sum. */ - stale = &skb_shinfo(skb)->frags[0]; - for (;;) { + for (stale = &skb_shinfo(skb)->frags[0];; stale++) { + int stale_size = skb_frag_size(stale); + sum += skb_frag_size(frag++); + /* The stale fragment may present us with a smaller + * descriptor than the actual fragment size. To account + * for that we need to remove all the data on the front and + * figure out what the remainder would be in the last + * descriptor associated with the fragment. + */ + if (stale_size > I40E_MAX_DATA_PER_TXD) { + int align_pad = -(skb_frag_off(stale)) & + (I40E_MAX_READ_REQ_SIZE - 1); + + sum -= align_pad; + stale_size -= align_pad; + + do { + sum -= I40E_MAX_DATA_PER_TXD_ALIGNED; + stale_size -= I40E_MAX_DATA_PER_TXD_ALIGNED; + } while (stale_size > I40E_MAX_DATA_PER_TXD); + } + /* if sum is negative we failed to make sufficient progress */ if (sum < 0) return true; @@ -3059,7 +3667,7 @@ bool __i40e_chk_linearize(struct sk_buff *skb) if (!nr_frags--) break; - sum -= skb_frag_size(stale++); + sum -= stale_size; } return false; @@ -3075,7 +3683,7 @@ bool __i40e_chk_linearize(struct sk_buff *skb) * @td_cmd: the command field in the descriptor * @td_offset: offset for checksum or crc * - * Returns 0 on success, -1 on failure to DMA + * Returns 0 on success, negative error code on DMA failure. **/ static inline int i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, struct i40e_tx_buffer *first, u32 tx_flags, @@ -3083,7 +3691,7 @@ static inline int i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, { unsigned int data_len = skb->data_len; unsigned int size = skb_headlen(skb); - struct skb_frag_struct *frag; + skb_frag_t *frag; struct i40e_tx_buffer *tx_bi; struct i40e_tx_desc *tx_desc; u16 i = tx_ring->next_to_use; @@ -3097,6 +3705,11 @@ static inline int i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, I40E_TX_FLAGS_VLAN_SHIFT; } +#ifdef I40E_ADD_PROBES + if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) + tx_ring->vsi->back->tcp_segs += first->gso_segs; + +#endif first->tx_flags = tx_flags; dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE); @@ -3176,38 +3789,12 @@ static inline int i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, /* write last descriptor with EOP bit */ td_cmd |= I40E_TX_DESC_CMD_EOP; - /* We can OR these values together as they both are checked against - * 4 below and at this point desc_count will be used as a boolean value - * after this if/else block. + /* We OR these values together to check both against 4 (WB_STRIDE) + * below. This is safe since we don't re-use desc_count afterwards. */ desc_count |= ++tx_ring->packet_stride; - /* Algorithm to optimize tail and RS bit setting: - * if queue is stopped - * mark RS bit - * reset packet counter - * else if xmit_more is supported and is true - * advance packet counter to 4 - * reset desc_count to 0 - * - * if desc_count >= 4 - * mark RS bit - * reset packet counter - * if desc_count > 0 - * update tail - * - * Note: If there are less than 4 descriptors - * pending and interrupts were disabled the service task will - * trigger a force WB. - */ - if (netif_xmit_stopped(txring_txq(tx_ring))) { - goto do_rs; - } else if (skb->xmit_more) { - /* set stride to arm on next packet and reset desc_count */ - tx_ring->packet_stride = WB_STRIDE; - desc_count = 0; - } else if (desc_count >= WB_STRIDE) { -do_rs: + if (desc_count >= WB_STRIDE) { /* write last descriptor with RS bit set */ td_cmd |= I40E_TX_DESC_CMD_RS; tx_ring->packet_stride = 0; @@ -3216,6 +3803,11 @@ do_rs: tx_desc->cmd_type_offset_bsz = build_ctob(td_cmd, td_offset, size, td_tag); + /* timestamp the skb as late as possible, just prior to notifying + * the MAC that it should transmit this packet + */ + skb_tx_timestamp(skb); + /* Force memory writes to complete before letting h/w know there * are new descriptors to fetch. * @@ -3228,14 +3820,37 @@ do_rs: first->next_to_watch = tx_desc; /* notify HW of packet */ - if (desc_count) { +#ifdef HAVE_SKB_XMIT_MORE + if (netif_xmit_stopped(txring_txq(tx_ring)) || !netdev_xmit_more()) { writel(i, tx_ring->tail); - /* we need this if more than one processor can write to our tail - * at a time, it synchronizes IO on IA64/Altix systems +#ifndef SPIN_UNLOCK_IMPLIES_MMIOWB + /* We need this mmiowb on IA64/Altix systems where wmb() isn't + * guaranteed to synchronize I/O. + * + * Note that mmiowb() only provides a guarantee about ordering + * when in conjunction with a spin_unlock(). This barrier is + * used to guarantee the I/O ordering with respect to a spin + * lock in the networking core code. */ mmiowb(); +#endif } +#else + writel(i, tx_ring->tail); + +#ifndef SPIN_UNLOCK_IMPLIES_MMIOWB + /* We need this mmiowb on IA64/Altix systems where wmb() isn't + * guaranteed to synchronize I/O. + * + * Note that mmiowb() only provides a guarantee about ordering when in + * conjunction with a spin_unlock(). This barrier is used to guarantee + * the I/O ordering with respect to a spin lock in the networking core + * code. + */ + mmiowb(); +#endif +#endif /* HAVE_XMIT_MORE */ return 0; @@ -3255,36 +3870,63 @@ dma_error: tx_ring->next_to_use = i; - return -1; + return -EIO; +} + +#if !defined(HAVE_NET_DEVICE_OPS) && defined(HAVE_NETDEV_SELECT_QUEUE) +/** + * i40e_lan_select_queue - Select the right Tx queue for the skb for LAN VSI + * @netdev: network interface device structure + * @skb: send buffer + * + * Returns the index of the selected Tx queue + **/ +u16 i40e_lan_select_queue(struct net_device *netdev, struct sk_buff *skb) +{ + return skb_tx_hash(netdev, skb); } +#endif /* !HAVE_NET_DEVICE_OPS && HAVE_NETDEV_SELECT_QUEUE */ + +#ifdef HAVE_XDP_SUPPORT /** * i40e_xmit_xdp_ring - transmits an XDP buffer to an XDP Tx ring - * @xdp: data to transmit + * @xdp: frame data to transmit * @xdp_ring: XDP Tx ring **/ +#ifdef HAVE_XDP_FRAME_STRUCT +static int i40e_xmit_xdp_ring(struct xdp_frame *xdp, + struct i40e_ring *xdp_ring) +#else static int i40e_xmit_xdp_ring(struct xdp_buff *xdp, struct i40e_ring *xdp_ring) +#endif { - u32 size = xdp->data_end - xdp->data; u16 i = xdp_ring->next_to_use; struct i40e_tx_buffer *tx_bi; struct i40e_tx_desc *tx_desc; dma_addr_t dma; + void *data; + u32 size; + + size = xdp_get_len(xdp); + data = xdp->data; if (!unlikely(I40E_DESC_UNUSED(xdp_ring))) { xdp_ring->tx_stats.tx_busy++; return I40E_XDP_CONSUMED; } - - dma = dma_map_single(xdp_ring->dev, xdp->data, size, DMA_TO_DEVICE); + dma = dma_map_single(xdp_ring->dev, data, size, DMA_TO_DEVICE); if (dma_mapping_error(xdp_ring->dev, dma)) return I40E_XDP_CONSUMED; - tx_bi = &xdp_ring->tx_bi[i]; tx_bi->bytecount = size; tx_bi->gso_segs = 1; - tx_bi->raw_buf = xdp->data; +#ifdef HAVE_XDP_FRAME_STRUCT + tx_bi->xdpf = xdp; +#else + tx_bi->raw_buf = data; +#endif /* record length, and DMA address */ dma_unmap_len_set(tx_bi, len, size); @@ -3304,12 +3946,11 @@ static int i40e_xmit_xdp_ring(struct xdp_buff *xdp, i++; if (i == xdp_ring->count) i = 0; - tx_bi->next_to_watch = tx_desc; xdp_ring->next_to_use = i; - return I40E_XDP_TX; } +#endif /** * i40e_xmit_frame_ring - Sends buffer on Tx ring @@ -3330,7 +3971,9 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, u32 td_cmd = 0; u8 hdr_len = 0; int tso, count; +#ifdef HAVE_PTP_1588_CLOCK int tsyn; +#endif /* HAVE_PTP_1588_CLOCK */ /* prefetch the data, we'll need it later */ prefetch(skb->data); @@ -3390,12 +4033,13 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, if (tso < 0) goto out_drop; +#ifdef HAVE_PTP_1588_CLOCK tsyn = i40e_tsyn(tx_ring, skb, tx_flags, &cd_type_cmd_tso_mss); if (tsyn) tx_flags |= I40E_TX_FLAGS_TSYN; - skb_tx_timestamp(skb); +#endif /* HAVE_PTP_1588_CLOCK */ /* always enable CRC insertion offload */ td_cmd |= I40E_TX_DESC_CMD_ICRC; @@ -3409,16 +4053,25 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, */ i40e_atr(tx_ring, skb, tx_flags); +#ifdef HAVE_PTP_1588_CLOCK if (i40e_tx_map(tx_ring, skb, first, tx_flags, hdr_len, td_cmd, td_offset)) goto cleanup_tx_tstamp; +#else + i40e_tx_map(tx_ring, skb, first, tx_flags, hdr_len, + td_cmd, td_offset); +#endif +#ifndef HAVE_TRANS_START_IN_QUEUE + tx_ring->netdev->trans_start = jiffies; +#endif return NETDEV_TX_OK; out_drop: i40e_trace(xmit_frame_ring_drop, first->skb, tx_ring); dev_kfree_skb_any(first->skb); first->skb = NULL; +#ifdef HAVE_PTP_1588_CLOCK cleanup_tx_tstamp: if (unlikely(tx_flags & I40E_TX_FLAGS_TSYN)) { struct i40e_pf *pf = i40e_netdev_to_pf(tx_ring->netdev); @@ -3427,7 +4080,7 @@ cleanup_tx_tstamp: pf->ptp_tx_skb = NULL; clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state); } - +#endif return NETDEV_TX_OK; } @@ -3452,3 +4105,90 @@ netdev_tx_t i40e_lan_xmit_frame(struct sk_buff *skb, struct net_device *netdev) return i40e_xmit_frame_ring(skb, tx_ring); } + +#ifdef HAVE_XDP_SUPPORT +/** + * i40e_xdp_xmit - Implements ndo_xdp_xmit + * @dev: netdev + * @n: amount of frames + * @frames: XDP frames + * @flags: XDP xmit flags + * + * Returns number of frames successfully sent. Frames that fail are + * free'ed via XDP return API. + * + * For error cases, a negative errno code is returned and no-frames + * are transmitted (caller must handle freeing frames). + **/ +#ifdef HAVE_XDP_FRAME_STRUCT +int i40e_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames, + u32 flags) +#else +int i40e_xdp_xmit(struct net_device *dev, struct xdp_buff *xdp) +#endif +{ + struct i40e_netdev_priv *np = netdev_priv(dev); + unsigned int queue_index = smp_processor_id(); + struct i40e_vsi *vsi = np->vsi; +#ifdef HAVE_XDP_FRAME_STRUCT + struct i40e_ring *xdp_ring; + int drops = 0; + int i; +#endif + int err; + + if (test_bit(__I40E_VSI_DOWN, vsi->state)) + return -ENETDOWN; + + if (!i40e_enabled_xdp_vsi(vsi) || queue_index >= vsi->num_queue_pairs) + return -ENXIO; +#ifdef HAVE_XDP_FRAME_STRUCT + if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) + return -EINVAL; + + xdp_ring = vsi->xdp_rings[queue_index]; + + for (i = 0; i < n; i++) { + struct xdp_frame *xdpf = frames[i]; + + err = i40e_xmit_xdp_ring(xdpf, xdp_ring); + if (err != I40E_XDP_TX) { + xdp_return_frame_rx_napi(xdpf); + drops++; + } + } + + if (unlikely(flags & XDP_XMIT_FLUSH)) + i40e_xdp_ring_update_tail(xdp_ring); + + return n - drops; +#else + err = i40e_xmit_xdp_ring(xdp, vsi->xdp_rings[queue_index]); + + if (err != I40E_XDP_TX) + return -ENOSPC; + + return 0; +#endif +} + +/** + * i40e_xdp_flush - Implements ndo_xdp_flush + * @dev: netdev + **/ +void i40e_xdp_flush(struct net_device *dev) +{ + struct i40e_netdev_priv *np = netdev_priv(dev); + unsigned int queue_index = smp_processor_id(); + struct i40e_vsi *vsi = np->vsi; + + if (test_bit(__I40E_VSI_DOWN, vsi->state)) + return; + + if (!i40e_enabled_xdp_vsi(vsi) || queue_index >= vsi->num_queue_pairs) + return; + + i40e_xdp_ring_update_tail(vsi->xdp_rings[queue_index]); +} +#endif + diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 2f848bc5e..da598721f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -1,57 +1,41 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2016 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_TXRX_H_ #define _I40E_TXRX_H_ /* Interrupt Throttling and Rate Limiting Goodies */ - -#define I40E_MAX_ITR 0x0FF0 /* reg uses 2 usec resolution */ -#define I40E_MIN_ITR 0x0001 /* reg uses 2 usec resolution */ -#define I40E_ITR_100K 0x0005 -#define I40E_ITR_50K 0x000A -#define I40E_ITR_20K 0x0019 -#define I40E_ITR_18K 0x001B -#define I40E_ITR_8K 0x003E -#define I40E_ITR_4K 0x007A -#define I40E_MAX_INTRL 0x3B /* reg uses 4 usec resolution */ -#define I40E_ITR_RX_DEF I40E_ITR_20K -#define I40E_ITR_TX_DEF I40E_ITR_20K -#define I40E_ITR_DYNAMIC 0x8000 /* use top bit as a flag */ -#define I40E_MIN_INT_RATE 250 /* ~= 1000000 / (I40E_MAX_ITR * 2) */ -#define I40E_MAX_INT_RATE 500000 /* == 1000000 / (I40E_MIN_ITR * 2) */ #define I40E_DEFAULT_IRQ_WORK 256 -#define ITR_TO_REG(setting) ((setting & ~I40E_ITR_DYNAMIC) >> 1) -#define ITR_IS_DYNAMIC(setting) (!!(setting & I40E_ITR_DYNAMIC)) -#define ITR_REG_TO_USEC(itr_reg) (itr_reg << 1) + +/* The datasheet for the X710 and XL710 indicate that the maximum value for + * the ITR is 8160usec which is then called out as 0xFF0 with a 2usec + * resolution. 8160 is 0x1FE0 when written out in hex. So instead of storing + * the register value which is divided by 2 lets use the actual values and + * avoid an excessive amount of translation. + */ +#define I40E_ITR_DYNAMIC 0x8000 /* use top bit as a flag */ +#define I40E_ITR_MASK 0x1FFE /* mask for ITR register value */ +#define I40E_MIN_ITR 2 /* reg uses 2 usec resolution */ +#define I40E_ITR_100K 10 /* all values below must be even */ +#define I40E_ITR_50K 20 +#define I40E_ITR_20K 50 +#define I40E_ITR_18K 60 +#define I40E_ITR_8K 122 +#define I40E_MAX_ITR 8160 /* maximum value as per datasheet */ +#define ITR_TO_REG(setting) ((setting) & ~I40E_ITR_DYNAMIC) +#define ITR_REG_ALIGN(setting) __ALIGN_MASK(setting, ~I40E_ITR_MASK) +#define ITR_IS_DYNAMIC(setting) (!!((setting) & I40E_ITR_DYNAMIC)) + +#define I40E_ITR_RX_DEF (I40E_ITR_20K | I40E_ITR_DYNAMIC) +#define I40E_ITR_TX_DEF (I40E_ITR_20K | I40E_ITR_DYNAMIC) + /* 0x40 is the enable bit for interrupt rate limiting, and must be set if * the value of the rate limit is non-zero */ #define INTRL_ENA BIT(6) +#define I40E_MAX_INTRL 0x3B /* reg uses 4 usec resolution */ #define INTRL_REG_TO_USEC(intrl) ((intrl & ~INTRL_ENA) << 2) + /** * i40e_intrl_usec_to_reg - convert interrupt rate limit to register * @intrl: interrupt rate limit to convert @@ -131,10 +115,18 @@ enum i40e_dyn_idx_t { */ #define I40E_RX_HDR_SIZE I40E_RXBUFFER_256 #define I40E_PACKET_HDR_PAD (ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2)) +#ifdef I40E_32BYTE_RX #define i40e_rx_desc i40e_32byte_rx_desc +#else +#define i40e_rx_desc i40e_16byte_rx_desc +#endif +#ifdef HAVE_STRUCT_DMA_ATTRS +#define I40E_RX_DMA_ATTR NULL +#else #define I40E_RX_DMA_ATTR \ (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) +#endif /* Attempt to maximize the headroom available for incoming frames. We * use a 2K buffer for receives and need 1536/1534 to store the data for @@ -206,7 +198,7 @@ static inline bool i40e_test_staterr(union i40e_rx_desc *rx_desc, } /* How many Rx Buffers do we bundle into one write to the hardware ? */ -#define I40E_RX_BUFFER_WRITE 16 /* Must be power of 2 */ +#define I40E_RX_BUFFER_WRITE 32 /* Must be power of 2 */ #define I40E_RX_INCREMENT(r, i) \ do { \ (i)++; \ @@ -275,7 +267,7 @@ static inline unsigned int i40e_txd_use_count(unsigned int size) } /* Tx Descriptors needed, worst case */ -#define DESC_NEEDED (MAX_SKB_FRAGS + 4) +#define DESC_NEEDED (MAX_SKB_FRAGS + 6) #define I40E_MIN_DESC_PENDING 4 #define I40E_TX_FLAGS_HW_VLAN BIT(1) @@ -285,9 +277,11 @@ static inline unsigned int i40e_txd_use_count(unsigned int size) #define I40E_TX_FLAGS_IPV6 BIT(5) #define I40E_TX_FLAGS_FCCRC BIT(6) #define I40E_TX_FLAGS_FSO BIT(7) +#ifdef HAVE_PTP_1588_CLOCK #define I40E_TX_FLAGS_TSYN BIT(8) +#endif /* HAVE_PTP_1588_CLOCK */ #define I40E_TX_FLAGS_FD_SB BIT(9) -#define I40E_TX_FLAGS_UDP_TUNNEL BIT(10) +#define I40E_TX_FLAGS_TUNNEL BIT(10) #define I40E_TX_FLAGS_VLAN_MASK 0xffff0000 #define I40E_TX_FLAGS_VLAN_PRIO_MASK 0xe0000000 #define I40E_TX_FLAGS_VLAN_PRIO_SHIFT 29 @@ -296,6 +290,7 @@ static inline unsigned int i40e_txd_use_count(unsigned int size) struct i40e_tx_buffer { struct i40e_tx_desc *next_to_watch; union { + struct xdp_frame *xdpf; struct sk_buff *skb; void *raw_buf; }; @@ -309,6 +304,9 @@ struct i40e_tx_buffer { struct i40e_rx_buffer { dma_addr_t dma; +#ifdef CONFIG_I40E_DISABLE_PACKET_SPLIT + struct sk_buff *skb; +#else struct page *page; #if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536) __u32 page_offset; @@ -316,6 +314,7 @@ struct i40e_rx_buffer { __u16 page_offset; #endif __u16 pagecnt_bias; +#endif /* CONFIG_I40E_DISABLE_PACKET_SPLIT */ }; struct i40e_queue_stats { @@ -323,12 +322,24 @@ struct i40e_queue_stats { u64 bytes; }; +#ifdef HAVE_XDP_SUPPORT +struct i40e_xdp_stats { + u64 xdp_pass; + u64 xdp_drop; + u64 xdp_tx; + u64 xdp_unknown; + u64 xdp_redirect; + u64 xdp_redirect_fail; +}; +#endif + struct i40e_tx_queue_stats { u64 restart_queue; u64 tx_busy; u64 tx_done_old; u64 tx_linearize; u64 tx_force_wb; + int prev_pkt_ctr; }; struct i40e_rx_queue_stats { @@ -342,6 +353,7 @@ struct i40e_rx_queue_stats { enum i40e_ring_state_t { __I40E_TX_FDIR_INIT_DONE, __I40E_TX_XPS_INIT_DONE, + __I40E_RING_STATE_NBITS /* must be last */ }; /* some useful defines for virtchannel interface, which @@ -366,7 +378,7 @@ struct i40e_ring { struct i40e_tx_buffer *tx_bi; struct i40e_rx_buffer *rx_bi; }; - unsigned long state; + DECLARE_BITMAP(state, __I40E_RING_STATE_NBITS); u16 queue_index; /* Queue number of ring */ u8 dcb_tc; /* Traffic class of ring */ u8 __iomem *tail; @@ -376,8 +388,7 @@ struct i40e_ring { * these values always store the USER setting, and must be converted * before programming to a register. */ - u16 rx_itr_setting; - u16 tx_itr_setting; + u16 itr_setting; u16 count; /* Number of descriptors */ u16 reg_idx; /* HW register index of the ring */ @@ -401,7 +412,12 @@ struct i40e_ring { /* stats structs */ struct i40e_queue_stats stats; +#ifdef HAVE_NDO_GET_STATS64 struct u64_stats_sync syncp; +#endif +#ifdef HAVE_XDP_SUPPORT + struct i40e_xdp_stats xdp_stats; +#endif union { struct i40e_tx_queue_stats tx_stats; struct i40e_rx_queue_stats rx_stats; @@ -423,6 +439,11 @@ struct i40e_ring { * i40e_clean_rx_ring_irq() is called * for this ring. */ + + struct i40e_channel *ch; +#ifdef HAVE_XDP_BUFF_RXQ + struct xdp_rxq_info xdp_rxq; +#endif } ____cacheline_internodealigned_in_smp; static inline bool ring_uses_build_skb(struct i40e_ring *ring) @@ -440,6 +461,13 @@ static inline void clear_ring_build_skb_enabled(struct i40e_ring *ring) ring->flags &= ~I40E_RXR_FLAGS_BUILD_SKB_ENABLED; } +#define I40E_ITR_ADAPTIVE_MIN_INC 0x0002 +#define I40E_ITR_ADAPTIVE_MIN_USECS 0x0002 +#define I40E_ITR_ADAPTIVE_MAX_USECS 0x007e +#define I40E_ITR_ADAPTIVE_LATENCY 0x8000 +#define I40E_ITR_ADAPTIVE_BULK 0x0000 +#define ITR_IS_BULK(x) (!((x) & I40E_ITR_ADAPTIVE_LATENCY)) + static inline bool ring_is_xdp(struct i40e_ring *ring) { return !!(ring->flags & I40E_TXR_FLAGS_XDP); @@ -450,21 +478,14 @@ static inline void set_ring_xdp(struct i40e_ring *ring) ring->flags |= I40E_TXR_FLAGS_XDP; } -enum i40e_latency_range { - I40E_LOWEST_LATENCY = 0, - I40E_LOW_LATENCY = 1, - I40E_BULK_LATENCY = 2, -}; - struct i40e_ring_container { - /* array of pointers to rings */ - struct i40e_ring *ring; + struct i40e_ring *ring; /* pointer to linked list of ring(s) */ + unsigned long next_update; /* jiffies value of next update */ unsigned int total_bytes; /* total bytes processed this int */ unsigned int total_packets; /* total packets processed this int */ - unsigned long last_itr_update; /* jiffies of last ITR update */ u16 count; - enum i40e_latency_range latency_range; - u16 itr; + u16 target_itr; /* target ITR setting for ring(s) */ + u16 current_itr; /* current ITR setting for ring(s) */ }; /* iterator for handling rings in ring container */ @@ -484,6 +505,10 @@ static inline unsigned int i40e_rx_pg_order(struct i40e_ring *ring) bool i40e_alloc_rx_buffers(struct i40e_ring *rxr, u16 cleaned_count); netdev_tx_t i40e_lan_xmit_frame(struct sk_buff *skb, struct net_device *netdev); +#if !defined(HAVE_NET_DEVICE_OPS) && defined(HAVE_NETDEV_SELECT_QUEUE) +extern u16 i40e_lan_select_queue(struct net_device *netdev, + struct sk_buff *skb); +#endif void i40e_clean_tx_ring(struct i40e_ring *tx_ring); void i40e_clean_rx_ring(struct i40e_ring *rx_ring); int i40e_setup_tx_descriptors(struct i40e_ring *tx_ring); @@ -492,9 +517,31 @@ void i40e_free_tx_resources(struct i40e_ring *tx_ring); void i40e_free_rx_resources(struct i40e_ring *rx_ring); int i40e_napi_poll(struct napi_struct *napi, int budget); void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector); -u32 i40e_get_tx_pending(struct i40e_ring *ring); +u32 i40e_get_tx_pending(struct i40e_ring *ring, bool in_sw); +void i40e_detect_recover_hung(struct i40e_vsi *vsi); int __i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size); bool __i40e_chk_linearize(struct sk_buff *skb); +#ifdef HAVE_XDP_FRAME_STRUCT +int i40e_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames, + u32 flags); +#else +int i40e_xdp_xmit(struct net_device *dev, struct xdp_buff *xdp); +#endif +void i40e_xdp_flush(struct net_device *dev); + +#ifdef HAVE_XDP_SUPPORT +#ifdef HAVE_XDP_FRAME_STRUCT +static inline u32 xdp_get_len(struct xdp_frame *xdp) +{ + return xdp->len; +} +#else +static inline u32 xdp_get_len(struct xdp_buff *xdp) +{ + return xdp->data_end - xdp->data; +} +#endif +#endif /** * i40e_get_head - Retrieve head from head writeback @@ -521,7 +568,7 @@ static inline u32 i40e_get_head(struct i40e_ring *tx_ring) **/ static inline int i40e_xmit_descriptor_count(struct sk_buff *skb) { - const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[0]; + const skb_frag_t *frag = &skb_shinfo(skb)->frags[0]; unsigned int nr_frags = skb_shinfo(skb)->nr_frags; int count = 0, size = skb_headlen(skb); diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index fd4bbdd88..147d84ec7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2015 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_TYPE_H_ #define _I40E_TYPE_H_ @@ -36,16 +13,19 @@ #include "i40e_devids.h" /* I40E_MASK is a macro used on 32 bit registers */ -#define I40E_MASK(mask, shift) ((u32)(mask) << (shift)) +#define I40E_MASK(mask, shift) (mask << shift) #define I40E_MAX_VSI_QP 16 -#define I40E_MAX_VF_VSI 3 +#define I40E_MAX_VF_VSI 4 #define I40E_MAX_CHAINED_RX_BUFFERS 5 #define I40E_MAX_PF_UDP_OFFLOAD_PORTS 16 /* Max default timeout in ms, */ #define I40E_MAX_NVM_TIMEOUT 18000 +/* Max timeout in ms for the phy to respond */ +#define I40E_MAX_PHY_TIMEOUT 500 + /* Switch from ms to the 1usec global time (this is the GTIME resolution) */ #define I40E_MS_TO_GTIME(time) ((time) * 1000) @@ -53,6 +33,9 @@ struct i40e_hw; typedef void (*I40E_ADMINQ_CALLBACK)(struct i40e_hw *, struct i40e_aq_desc *); +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif /* Data type manipulation macros. */ #define I40E_DESC_UNUSED(R) \ @@ -79,7 +62,9 @@ enum i40e_debug_mask { I40E_DEBUG_DIAG = 0x00000800, I40E_DEBUG_FD = 0x00001000, I40E_DEBUG_PACKAGE = 0x00002000, + I40E_DEBUG_IWARP = 0x00F00000, + I40E_DEBUG_AQ_MESSAGE = 0x01000000, I40E_DEBUG_AQ_DESCRIPTOR = 0x02000000, I40E_DEBUG_AQ_DESC_BUFFER = 0x04000000, @@ -105,16 +90,16 @@ enum i40e_debug_mask { #define I40E_MDIO_CLAUSE45_OPCODE_WRITE_MASK I40E_MASK(1, \ I40E_GLGEN_MSCA_OPCODE_SHIFT) #define I40E_MDIO_CLAUSE45_OPCODE_READ_INC_ADDR_MASK I40E_MASK(2, \ - I40E_GLGEN_MSCA_OPCODE_SHIFT) + I40E_GLGEN_MSCA_OPCODE_SHIFT) #define I40E_MDIO_CLAUSE45_OPCODE_READ_MASK I40E_MASK(3, \ - I40E_GLGEN_MSCA_OPCODE_SHIFT) + I40E_GLGEN_MSCA_OPCODE_SHIFT) -#define I40E_PHY_COM_REG_PAGE 0x1E -#define I40E_PHY_LED_LINK_MODE_MASK 0xF0 -#define I40E_PHY_LED_MANUAL_ON 0x100 -#define I40E_PHY_LED_PROV_REG_1 0xC430 -#define I40E_PHY_LED_MODE_MASK 0xFFFF -#define I40E_PHY_LED_MODE_ORIG 0x80000000 +#define I40E_PHY_COM_REG_PAGE 0x1E +#define I40E_PHY_LED_LINK_MODE_MASK 0xF0 +#define I40E_PHY_LED_MANUAL_ON 0x100 +#define I40E_PHY_LED_PROV_REG_1 0xC430 +#define I40E_PHY_LED_MODE_MASK 0xFFFF +#define I40E_PHY_LED_MODE_ORIG 0x80000000 /* These are structs for managing the hardware information and the operations. * The structures of function pointers are filled out at init time when we @@ -253,7 +238,8 @@ struct i40e_phy_info { #define I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL \ BIT_ULL(I40E_PHY_TYPE_1000BASE_T_OPTICAL) #define I40E_CAP_PHY_TYPE_20GBASE_KR2 BIT_ULL(I40E_PHY_TYPE_20GBASE_KR2) -/* Defining the macro I40E_TYPE_OFFSET to implement a bit shift for some +/* + * Defining the macro I40E_TYPE_OFFSET to implement a bit shift for some * PHY types. There is an unused bit (31) in the I40E_CAP_PHY_TYPE_* bit * fields but no corresponding gap in the i40e_aq_phy_type enumeration. So, * a shift is needed to adjust for this with values larger than 31. The @@ -268,7 +254,26 @@ struct i40e_phy_info { I40E_PHY_TYPE_OFFSET) #define I40E_CAP_PHY_TYPE_25GBASE_LR BIT_ULL(I40E_PHY_TYPE_25GBASE_LR + \ I40E_PHY_TYPE_OFFSET) +#define I40E_CAP_PHY_TYPE_25GBASE_AOC BIT_ULL(I40E_PHY_TYPE_25GBASE_AOC + \ + I40E_PHY_TYPE_OFFSET) +#define I40E_CAP_PHY_TYPE_25GBASE_ACC BIT_ULL(I40E_PHY_TYPE_25GBASE_ACC + \ + I40E_PHY_TYPE_OFFSET) +/* Offset for 2.5G/5G PHY Types value to bit number conversion */ +#define I40E_PHY_TYPE_OFFSET2 (-10) +#define I40E_CAP_PHY_TYPE_2_5GBASE_T BIT_ULL(I40E_PHY_TYPE_2_5GBASE_T + \ + I40E_PHY_TYPE_OFFSET2) +#define I40E_CAP_PHY_TYPE_5GBASE_T BIT_ULL(I40E_PHY_TYPE_5GBASE_T + \ + I40E_PHY_TYPE_OFFSET2) #define I40E_HW_CAP_MAX_GPIO 30 +enum i40e_acpi_programming_method { + I40E_ACPI_PROGRAMMING_METHOD_HW_FVL = 0, + I40E_ACPI_PROGRAMMING_METHOD_AQC_FPK = 1 +}; + +#define I40E_WOL_SUPPORT_MASK 0x1 +#define I40E_ACPI_PROGRAMMING_METHOD_MASK 0x2 +#define I40E_PROXY_SUPPORT_MASK 0x4 + /* Capabilities of a PF or a VF or the whole device */ struct i40e_hw_capabilities { u32 switch_mode; @@ -276,6 +281,16 @@ struct i40e_hw_capabilities { #define I40E_NVM_IMAGE_TYPE_CLOUD 0x2 #define I40E_NVM_IMAGE_TYPE_UDP_CLOUD 0x3 + /* Cloud filter modes: + * Mode1: Filter on L4 port only + * Mode2: Filter for non-tunneled traffic + * Mode3: Filter for tunnel traffic + */ +#define I40E_CLOUD_FILTER_MODE1 0x6 +#define I40E_CLOUD_FILTER_MODE2 0x7 +#define I40E_CLOUD_FILTER_MODE3 0x8 +#define I40E_SWITCH_MODE_MASK 0xF + u32 management_mode; u32 mng_protocols_over_mctp; #define I40E_MNG_PROTOCOL_PLDM 0x2 @@ -336,6 +351,9 @@ struct i40e_hw_capabilities { u32 enabled_tcmap; u32 maxtc; u64 wr_csr_prot; + bool apm_wol_support; + enum i40e_acpi_programming_method acpi_prog_method; + bool proxy_support; }; struct i40e_mac_info { @@ -385,6 +403,8 @@ enum i40e_nvmupd_cmd { I40E_NVMUPD_STATUS, I40E_NVMUPD_EXEC_AQ, I40E_NVMUPD_GET_AQ_RESULT, + I40E_NVMUPD_GET_AQ_EVENT, + I40E_NVMUPD_FEATURES, }; enum i40e_nvmupd_state { @@ -404,18 +424,28 @@ enum i40e_nvmupd_state { #define I40E_NVM_MOD_PNT_MASK 0xFF -#define I40E_NVM_TRANS_SHIFT 8 -#define I40E_NVM_TRANS_MASK (0xf << I40E_NVM_TRANS_SHIFT) -#define I40E_NVM_CON 0x0 -#define I40E_NVM_SNT 0x1 -#define I40E_NVM_LCB 0x2 -#define I40E_NVM_SA (I40E_NVM_SNT | I40E_NVM_LCB) -#define I40E_NVM_ERA 0x4 -#define I40E_NVM_CSUM 0x8 -#define I40E_NVM_EXEC 0xf +#define I40E_NVM_TRANS_SHIFT 8 +#define I40E_NVM_TRANS_MASK (0xf << I40E_NVM_TRANS_SHIFT) +#define I40E_NVM_PRESERVATION_FLAGS_SHIFT 12 +#define I40E_NVM_PRESERVATION_FLAGS_MASK \ + (0x3 << I40E_NVM_PRESERVATION_FLAGS_SHIFT) +#define I40E_NVM_PRESERVATION_FLAGS_SELECTED 0x01 +#define I40E_NVM_PRESERVATION_FLAGS_ALL 0x02 +#define I40E_NVM_CON 0x0 +#define I40E_NVM_SNT 0x1 +#define I40E_NVM_LCB 0x2 +#define I40E_NVM_SA (I40E_NVM_SNT | I40E_NVM_LCB) +#define I40E_NVM_ERA 0x4 +#define I40E_NVM_CSUM 0x8 +#define I40E_NVM_AQE 0xe +#define I40E_NVM_EXEC 0xf + +#define I40E_NVM_EXEC_GET_AQ_RESULT 0x0 +#define I40E_NVM_EXEC_FEATURES 0xe +#define I40E_NVM_EXEC_STATUS 0xf #define I40E_NVM_ADAPT_SHIFT 16 -#define I40E_NVM_ADAPT_MASK (0xffff << I40E_NVM_ADAPT_SHIFT) +#define I40E_NVM_ADAPT_MASK (0xffffULL << I40E_NVM_ADAPT_SHIFT) #define I40E_NVMUPD_MAX_DATA 4096 #define I40E_NVMUPD_IFACE_TIMEOUT 2 /* seconds */ @@ -428,6 +458,33 @@ struct i40e_nvm_access { u8 data[1]; }; +/* NVMUpdate features API */ +#define I40E_NVMUPD_FEATURES_API_VER_MAJOR 0 +#define I40E_NVMUPD_FEATURES_API_VER_MINOR 14 +#define I40E_NVMUPD_FEATURES_API_FEATURES_ARRAY_LEN 12 + +#define I40E_NVMUPD_FEATURE_FLAT_NVM_SUPPORT BIT(0) + +struct i40e_nvmupd_features { + u8 major; + u8 minor; + u16 size; + u8 features[I40E_NVMUPD_FEATURES_API_FEATURES_ARRAY_LEN]; +}; + +/* (Q)SFP module access definitions */ +#define I40E_I2C_EEPROM_DEV_ADDR 0xA0 +#define I40E_I2C_EEPROM_DEV_ADDR2 0xA2 +#define I40E_MODULE_TYPE_ADDR 0x00 +#define I40E_MODULE_REVISION_ADDR 0x01 +#define I40E_MODULE_SFF_8472_COMP 0x5E +#define I40E_MODULE_SFF_8472_SWAP 0x5C +#define I40E_MODULE_SFF_ADDR_MODE 0x04 +#define I40E_MODULE_SFF_DIAG_CAPAB 0x40 +#define I40E_MODULE_TYPE_QSFP_PLUS 0x0D +#define I40E_MODULE_TYPE_QSFP28 0x11 +#define I40E_MODULE_QSFP_MAX_LEN 640 + /* PCI bus types */ enum i40e_bus_type { i40e_bus_type_unknown = 0, @@ -582,6 +639,7 @@ struct i40e_hw { /* state of nvm update process */ enum i40e_nvmupd_state nvmupd_state; struct i40e_aq_desc nvm_wb_desc; + struct i40e_aq_desc nvm_aq_event_desc; struct i40e_virt_mem nvm_buff; bool nvm_release_on_done; u16 nvm_wait_opcode; @@ -597,15 +655,34 @@ struct i40e_hw { struct i40e_dcbx_config remote_dcbx_config; /* Peer Cfg */ struct i40e_dcbx_config desired_dcbx_config; /* CEE Desired Cfg */ + /* WoL and proxy support */ + u16 num_wol_proxy_filters; + u16 wol_proxy_vsi_seid; + #define I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE BIT_ULL(0) +#define I40E_HW_FLAG_802_1AD_CAPABLE BIT_ULL(1) +#define I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE BIT_ULL(2) +#define I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK BIT_ULL(3) +#define I40E_HW_FLAG_FW_LLDP_STOPPABLE BIT_ULL(4) +#define I40E_HW_FLAG_FW_LLDP_PERSISTENT BIT_ULL(5) +#define I40E_HW_FLAG_AQ_PHY_ACCESS_EXTENDED BIT_ULL(6) +#define I40E_HW_FLAG_DROP_MODE BIT_ULL(7) u64 flags; + /* Used in set switch config AQ command */ + u16 switch_tag; + u16 first_tag; + u16 second_tag; + + /* NVMUpdate features */ + struct i40e_nvmupd_features nvmupd_features; + /* debug mask */ u32 debug_mask; char err_str[16]; }; -static inline bool i40e_is_vf(struct i40e_hw *hw) +static INLINE bool i40e_is_vf(struct i40e_hw *hw) { return (hw->mac.type == I40E_MAC_VF || hw->mac.type == I40E_MAC_X722_VF); @@ -705,32 +782,28 @@ enum i40e_rx_desc_status_bits { I40E_RX_DESC_STATUS_CRCP_SHIFT = 4, I40E_RX_DESC_STATUS_TSYNINDX_SHIFT = 5, /* 2 BITS */ I40E_RX_DESC_STATUS_TSYNVALID_SHIFT = 7, - /* Note: Bit 8 is reserved in X710 and XL710 */ I40E_RX_DESC_STATUS_EXT_UDP_0_SHIFT = 8, + I40E_RX_DESC_STATUS_UMBCAST_SHIFT = 9, /* 2 BITS */ I40E_RX_DESC_STATUS_FLM_SHIFT = 11, I40E_RX_DESC_STATUS_FLTSTAT_SHIFT = 12, /* 2 BITS */ I40E_RX_DESC_STATUS_LPBK_SHIFT = 14, I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT = 15, - I40E_RX_DESC_STATUS_RESERVED_SHIFT = 16, /* 2 BITS */ - /* Note: For non-tunnel packets INT_UDP_0 is the right status for - * UDP header - */ + I40E_RX_DESC_STATUS_RESERVED2_SHIFT = 16, /* 2 BITS */ I40E_RX_DESC_STATUS_INT_UDP_0_SHIFT = 18, I40E_RX_DESC_STATUS_LAST /* this entry must be last!!! */ }; #define I40E_RXD_QW1_STATUS_SHIFT 0 -#define I40E_RXD_QW1_STATUS_MASK ((BIT(I40E_RX_DESC_STATUS_LAST) - 1) \ - << I40E_RXD_QW1_STATUS_SHIFT) +#define I40E_RXD_QW1_STATUS_MASK ((BIT(I40E_RX_DESC_STATUS_LAST) - 1) << \ + I40E_RXD_QW1_STATUS_SHIFT) #define I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT I40E_RX_DESC_STATUS_TSYNINDX_SHIFT #define I40E_RXD_QW1_STATUS_TSYNINDX_MASK (0x3UL << \ I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT) #define I40E_RXD_QW1_STATUS_TSYNVALID_SHIFT I40E_RX_DESC_STATUS_TSYNVALID_SHIFT -#define I40E_RXD_QW1_STATUS_TSYNVALID_MASK \ - BIT_ULL(I40E_RXD_QW1_STATUS_TSYNVALID_SHIFT) +#define I40E_RXD_QW1_STATUS_TSYNVALID_MASK BIT_ULL(I40E_RXD_QW1_STATUS_TSYNVALID_SHIFT) enum i40e_rx_desc_fltstat_values { I40E_RX_DESC_FLTSTAT_NO_DATA = 0, @@ -793,7 +866,8 @@ enum i40e_rx_l2_ptype { I40E_RX_PTYPE_GRENAT4_MAC_PAY3 = 58, I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4 = 87, I40E_RX_PTYPE_GRENAT6_MAC_PAY3 = 124, - I40E_RX_PTYPE_GRENAT6_MACVLAN_IPV6_ICMP_PAY4 = 153 + I40E_RX_PTYPE_GRENAT6_MACVLAN_IPV6_ICMP_PAY4 = 153, + I40E_RX_PTYPE_PARSER_ABORTED = 255 }; struct i40e_rx_ptype_decoded { @@ -1044,8 +1118,7 @@ enum i40e_tx_ctx_desc_eipt_offload { #define I40E_TXD_CTX_GRE_TUNNELING (0x2ULL << I40E_TXD_CTX_QW0_NATT_SHIFT) #define I40E_TXD_CTX_QW0_EIP_NOINC_SHIFT 11 -#define I40E_TXD_CTX_QW0_EIP_NOINC_MASK \ - BIT_ULL(I40E_TXD_CTX_QW0_EIP_NOINC_SHIFT) +#define I40E_TXD_CTX_QW0_EIP_NOINC_MASK BIT_ULL(I40E_TXD_CTX_QW0_EIP_NOINC_SHIFT) #define I40E_TXD_CTX_EIP_NOINC_IPID_CONST I40E_TXD_CTX_QW0_EIP_NOINC_MASK @@ -1151,10 +1224,6 @@ enum i40e_filter_program_desc_pcmd { I40E_TXD_FLTR_QW1_CMD_SHIFT) #define I40E_TXD_FLTR_QW1_ATR_MASK BIT_ULL(I40E_TXD_FLTR_QW1_ATR_SHIFT) -#define I40E_TXD_FLTR_QW1_ATR_SHIFT (0xEULL + \ - I40E_TXD_FLTR_QW1_CMD_SHIFT) -#define I40E_TXD_FLTR_QW1_ATR_MASK BIT_ULL(I40E_TXD_FLTR_QW1_ATR_SHIFT) - #define I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT 20 #define I40E_TXD_FLTR_QW1_CNTINDEX_MASK (0x1FFUL << \ I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) @@ -1268,11 +1337,14 @@ struct i40e_hw_port_stats { u32 rx_lpi_status; u64 tx_lpi_count; /* etlpic */ u64 rx_lpi_count; /* erlpic */ + u64 tx_lpi_duration; + u64 rx_lpi_duration; }; /* Checksum and Shadow RAM pointers */ #define I40E_SR_NVM_CONTROL_WORD 0x00 -#define I40E_SR_EMP_MODULE_PTR 0x0F +#define I40E_EMP_MODULE_PTR 0x0F +#define I40E_SR_EMP_MODULE_PTR 0x48 #define I40E_SR_PBA_FLAGS 0x15 #define I40E_SR_PBA_BLOCK_PTR 0x16 #define I40E_SR_BOOT_CONFIG_PTR 0x17 @@ -1280,17 +1352,27 @@ struct i40e_hw_port_stats { #define I40E_SR_NVM_DEV_STARTER_VERSION 0x18 #define I40E_SR_NVM_WAKE_ON_LAN 0x19 #define I40E_SR_ALTERNATE_SAN_MAC_ADDRESS_PTR 0x27 +#define I40E_SR_PERMANENT_SAN_MAC_ADDRESS_PTR 0x28 +#define I40E_SR_NVM_MAP_VERSION 0x29 +#define I40E_SR_NVM_IMAGE_VERSION 0x2A +#define I40E_SR_NVM_STRUCTURE_VERSION 0x2B #define I40E_SR_NVM_EETRACK_LO 0x2D #define I40E_SR_NVM_EETRACK_HI 0x2E #define I40E_SR_VPD_PTR 0x2F #define I40E_SR_PCIE_ALT_AUTO_LOAD_PTR 0x3E #define I40E_SR_SW_CHECKSUM_WORD 0x3F +#define I40E_SR_EMP_SR_SETTINGS_PTR 0x48 /* Auxiliary field, mask and shift definition for Shadow RAM and NVM Flash */ #define I40E_SR_VPD_MODULE_MAX_SIZE 1024 #define I40E_SR_PCIE_ALT_MODULE_MAX_SIZE 1024 #define I40E_SR_CONTROL_WORD_1_SHIFT 0x06 #define I40E_SR_CONTROL_WORD_1_MASK (0x03 << I40E_SR_CONTROL_WORD_1_SHIFT) +#define I40E_SR_CONTROL_WORD_1_NVM_BANK_VALID BIT(5) +#define I40E_SR_NVM_MAP_STRUCTURE_TYPE BIT(12) +#define I40E_PTR_TYPE BIT(15) +#define I40E_SR_OCP_CFG_WORD0 0x2B +#define I40E_SR_OCP_ENABLED BIT(15) /* Shadow RAM related */ #define I40E_SR_SECTOR_SIZE_IN_WORDS 0x800 @@ -1405,7 +1487,8 @@ enum i40e_reset_type { }; /* IEEE 802.1AB LLDP Agent Variables from NVM */ -#define I40E_NVM_LLDP_CFG_PTR 0xD +#define I40E_NVM_LLDP_CFG_PTR 0x06 +#define I40E_SR_LLDP_CFG_PTR 0x31 struct i40e_lldp_variables { u16 length; u16 adminstatus; @@ -1465,19 +1548,19 @@ struct i40e_lldp_variables { #define I40E_FLEX_57_SHIFT 6 #define I40E_FLEX_57_MASK (0x1ULL << I40E_FLEX_57_SHIFT) -/* Version format for PPP */ -struct i40e_ppp_version { +/* Version format for Dynamic Device Personalization(DDP) */ +struct i40e_ddp_version { u8 major; u8 minor; u8 update; u8 draft; }; -#define I40E_PPP_NAME_SIZE 32 +#define I40E_DDP_NAME_SIZE 32 /* Package header */ struct i40e_package_header { - struct i40e_ppp_version version; + struct i40e_ddp_version version; u32 segment_count; u32 segment_offset[1]; }; @@ -1489,16 +1572,18 @@ struct i40e_generic_seg_header { #define SEGMENT_TYPE_I40E 0x00000011 #define SEGMENT_TYPE_X722 0x00000012 u32 type; - struct i40e_ppp_version version; + struct i40e_ddp_version version; u32 size; - char name[I40E_PPP_NAME_SIZE]; + char name[I40E_DDP_NAME_SIZE]; }; struct i40e_metadata_segment { struct i40e_generic_seg_header header; - struct i40e_ppp_version version; + struct i40e_ddp_version version; +#define I40E_DDP_TRACKID_RDONLY 0 +#define I40E_DDP_TRACKID_INVALID 0xFFFFFFFF u32 track_id; - char name[I40E_PPP_NAME_SIZE]; + char name[I40E_DDP_NAME_SIZE]; }; struct i40e_device_id_entry { @@ -1508,8 +1593,8 @@ struct i40e_device_id_entry { struct i40e_profile_segment { struct i40e_generic_seg_header header; - struct i40e_ppp_version version; - char name[I40E_PPP_NAME_SIZE]; + struct i40e_ddp_version version; + char name[I40E_DDP_NAME_SIZE]; u32 device_table_count; struct i40e_device_id_entry device_table[1]; }; @@ -1525,22 +1610,49 @@ struct i40e_profile_section_header { struct { #define SECTION_TYPE_INFO 0x00000010 #define SECTION_TYPE_MMIO 0x00000800 +#define SECTION_TYPE_RB_MMIO 0x00001800 #define SECTION_TYPE_AQ 0x00000801 +#define SECTION_TYPE_RB_AQ 0x00001801 #define SECTION_TYPE_NOTE 0x80000000 #define SECTION_TYPE_NAME 0x80000001 +#define SECTION_TYPE_PROTO 0x80000002 +#define SECTION_TYPE_PCTYPE 0x80000003 +#define SECTION_TYPE_PTYPE 0x80000004 u32 type; u32 offset; u32 size; } section; }; +struct i40e_profile_tlv_section_record { + u8 rtype; + u8 type; + u16 len; + u8 data[12]; +}; + +/* Generic AQ section in proflie */ +struct i40e_profile_aq_section { + u16 opcode; + u16 flags; + u8 param[16]; + u16 datalen; + u8 data[1]; +}; + struct i40e_profile_info { u32 track_id; - struct i40e_ppp_version version; + struct i40e_ddp_version version; u8 op; -#define I40E_PPP_ADD_TRACKID 0x01 -#define I40E_PPP_REMOVE_TRACKID 0x02 +#define I40E_DDP_ADD_TRACKID 0x01 +#define I40E_DDP_REMOVE_TRACKID 0x02 u8 reserved[7]; - u8 name[I40E_PPP_NAME_SIZE]; + u8 name[I40E_DDP_NAME_SIZE]; }; + +#define I40E_BCM_PHY_PCS_STATUS1_PAGE 0x3 +#define I40E_BCM_PHY_PCS_STATUS1_REG 0x0001 +#define I40E_BCM_PHY_PCS_STATUS1_RX_LPI BIT(8) +#define I40E_BCM_PHY_PCS_STATUS1_TX_LPI BIT(9) + #endif /* _I40E_TYPE_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 4d1e670f4..6b2966ffe 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2016 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #include "i40e.h" @@ -31,8 +8,8 @@ /** * i40e_vc_vf_broadcast * @pf: pointer to the PF structure - * @opcode: operation code - * @retval: return value + * @v_opcode: operation code + * @v_retval: return value * @msg: pointer to the msg buffer * @msglen: msg length * @@ -62,6 +39,39 @@ static void i40e_vc_vf_broadcast(struct i40e_pf *pf, } } +/** + * i40e_vc_link_speed2mbps - Convert AdminQ link_speed bit represented + * to integer value of Mbps + * @link_speed: the speed to convert + * + * Returns the speed as direct value of Mbps. + **/ +static INLINE u32 +i40e_vc_link_speed2mbps(enum i40e_aq_link_speed link_speed) +{ + switch (link_speed) { + case I40E_LINK_SPEED_100MB: + return SPEED_100; + case I40E_LINK_SPEED_1GB: + return SPEED_1000; + case I40E_LINK_SPEED_2_5GB: + return SPEED_2500; + case I40E_LINK_SPEED_5GB: + return SPEED_5000; + case I40E_LINK_SPEED_10GB: + return SPEED_10000; + case I40E_LINK_SPEED_20GB: + return SPEED_20000; + case I40E_LINK_SPEED_25GB: + return SPEED_25000; + case I40E_LINK_SPEED_40GB: + return SPEED_40000; + case I40E_LINK_SPEED_UNKNOWN: + return SPEED_UNKNOWN; + } + return SPEED_UNKNOWN; +} + /** * i40e_vc_notify_vf_link_state * @vf: pointer to the VF structure @@ -78,18 +88,64 @@ static void i40e_vc_notify_vf_link_state(struct i40e_vf *vf) pfe.event = VIRTCHNL_EVENT_LINK_CHANGE; pfe.severity = PF_EVENT_SEVERITY_INFO; - if (vf->link_forced) { + +#ifdef VIRTCHNL_VF_CAP_ADV_LINK_SPEED + if (vf->driver_caps & VIRTCHNL_VF_CAP_ADV_LINK_SPEED) { + /* Always report link is down if the VF queues aren't enabled */ + if (!vf->queues_enabled) { + pfe.event_data.link_event_adv.link_status = false; + pfe.event_data.link_event_adv.link_speed = 0; +#ifdef HAVE_NDO_SET_VF_LINK_STATE + } else if (vf->link_forced) { + pfe.event_data.link_event_adv.link_status = vf->link_up; + pfe.event_data.link_event_adv.link_speed = vf->link_up ? + i40e_vc_link_speed2mbps(ls->link_speed) : 0; +#endif + } else { + pfe.event_data.link_event_adv.link_status = + ls->link_info & I40E_AQ_LINK_UP; + pfe.event_data.link_event_adv.link_speed = + i40e_vc_link_speed2mbps(ls->link_speed); + } + } else { + /* Always report link is down if the VF queues aren't enabled */ + if (!vf->queues_enabled) { + pfe.event_data.link_event.link_status = false; + pfe.event_data.link_event.link_speed = 0; +#ifdef HAVE_NDO_SET_VF_LINK_STATE + } else if (vf->link_forced) { + pfe.event_data.link_event.link_status = vf->link_up; + pfe.event_data.link_event.link_speed = (vf->link_up ? + i40e_virtchnl_link_speed(ls->link_speed) : 0); +#endif + } else { + pfe.event_data.link_event.link_status = + ls->link_info & I40E_AQ_LINK_UP; + pfe.event_data.link_event.link_speed = + i40e_virtchnl_link_speed(ls->link_speed); + } + } +#else /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + /* Always report link is down if the VF queues aren't enabled */ + if (!vf->queues_enabled) { + pfe.event_data.link_event.link_status = false; + pfe.event_data.link_event.link_speed = 0; +#ifdef HAVE_NDO_SET_VF_LINK_STATE + } else if (vf->link_forced) { pfe.event_data.link_event.link_status = vf->link_up; - pfe.event_data.link_event.link_speed = - (vf->link_up ? I40E_LINK_SPEED_40GB : 0); + pfe.event_data.link_event.link_speed = (vf->link_up ? + i40e_virtchnl_link_speed(ls->link_speed) : 0); +#endif } else { pfe.event_data.link_event.link_status = ls->link_info & I40E_AQ_LINK_UP; pfe.event_data.link_event.link_speed = - (enum virtchnl_link_speed)ls->link_speed; + i40e_virtchnl_link_speed(ls->link_speed); } +#endif /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + i40e_aq_send_msg_to_vf(hw, abs_vf_id, VIRTCHNL_OP_EVENT, - 0, (u8 *)&pfe, sizeof(pfe), NULL); + I40E_SUCCESS, (u8 *)&pfe, sizeof(pfe), NULL); } /** @@ -118,7 +174,7 @@ void i40e_vc_notify_reset(struct i40e_pf *pf) pfe.event = VIRTCHNL_EVENT_RESET_IMPENDING; pfe.severity = PF_EVENT_SEVERITY_CERTAIN_DOOM; - i40e_vc_vf_broadcast(pf, VIRTCHNL_OP_EVENT, 0, + i40e_vc_vf_broadcast(pf, VIRTCHNL_OP_EVENT, I40E_SUCCESS, (u8 *)&pfe, sizeof(struct virtchnl_pf_event)); } @@ -147,22 +203,51 @@ void i40e_vc_notify_vf_reset(struct i40e_vf *vf) pfe.event = VIRTCHNL_EVENT_RESET_IMPENDING; pfe.severity = PF_EVENT_SEVERITY_CERTAIN_DOOM; i40e_aq_send_msg_to_vf(&vf->pf->hw, abs_vf_id, VIRTCHNL_OP_EVENT, - 0, (u8 *)&pfe, + I40E_SUCCESS, (u8 *)&pfe, sizeof(struct virtchnl_pf_event), NULL); } /***********************misc routines*****************************/ /** - * i40e_vc_disable_vf - * @pf: pointer to the PF info + * i40e_vc_reset_vf * @vf: pointer to the VF info + * @notify_vf: notify vf about reset or not * - * Disable the VF through a SW reset + * Reset VF handler. **/ -static inline void i40e_vc_disable_vf(struct i40e_pf *pf, struct i40e_vf *vf) +static inline void i40e_vc_reset_vf(struct i40e_vf *vf, bool notify_vf) { - i40e_vc_notify_vf_reset(vf); - i40e_reset_vf(vf, false); + struct i40e_pf *pf = vf->pf; + int i; + + if (notify_vf) + i40e_vc_notify_vf_reset(vf); + + /* We want to ensure that an actual reset occurs initiated after this + * function was called. However, we do not want to wait forever, so + * we'll give a reasonable time and print a message if we failed to + * ensure a reset. + */ + for (i = 0; i < 20; i++) { + /* If pf is in vfs releasing state reset vf is impossible, + * so leave it. + */ + if (test_bit(__I40E_VFS_RELEASING, pf->state)) + return; + + if (i40e_reset_vf(vf, false)) + return; + usleep_range(10000, 20000); + } + + if (notify_vf) + dev_warn(&vf->pf->pdev->dev, + "Failed to initiate reset for VF %d after 200 milliseconds\n", + vf->vf_id); + else + dev_dbg(&vf->pf->pdev->dev, + "Failed to initiate reset for VF %d after 200 milliseconds\n", + vf->vf_id); } /** @@ -189,7 +274,7 @@ static inline bool i40e_vc_isvalid_vsi_id(struct i40e_vf *vf, u16 vsi_id) * check for the valid queue id **/ static inline bool i40e_vc_isvalid_queue_id(struct i40e_vf *vf, u16 vsi_id, - u8 qid) + u16 qid) { struct i40e_pf *pf = vf->pf; struct i40e_vsi *vsi = i40e_find_vsi_from_id(pf, vsi_id); @@ -204,7 +289,7 @@ static inline bool i40e_vc_isvalid_queue_id(struct i40e_vf *vf, u16 vsi_id, * * check for the valid vector id **/ -static inline bool i40e_vc_isvalid_vector_id(struct i40e_vf *vf, u8 vector_id) +static inline bool i40e_vc_isvalid_vector_id(struct i40e_vf *vf, u32 vector_id) { struct i40e_pf *pf = vf->pf; @@ -242,6 +327,38 @@ static u16 i40e_vc_get_pf_queue_id(struct i40e_vf *vf, u16 vsi_id, return pf_queue_id; } +/** + * i40e_get_real_pf_qid + * @vf: pointer to the VF info + * @vsi_id: vsi id + * @queue_id: queue number + * + * wrapper function to get pf_queue_id handling ADq code as well + **/ +static u16 i40e_get_real_pf_qid(struct i40e_vf *vf, u16 vsi_id, u16 queue_id) +{ + int i; + + if (vf->adq_enabled) { + /* Although VF considers all the queues(can be 1 to 16) as its + * own but they may actually belong to different VSIs(up to 4). + * We need to find which queues belongs to which VSI. + */ + for (i = 0; i < vf->num_tc; i++) { + if (queue_id < vf->ch[i].num_qps) { + vsi_id = vf->ch[i].vsi_id; + break; + } + /* find right queue id which is relative to a + * given VSI. + */ + queue_id -= vf->ch[i].num_qps; + } + } + + return i40e_vc_get_pf_queue_id(vf, vsi_id, queue_id); +} + /** * i40e_config_irq_link_list * @vf: pointer to the VF info @@ -258,7 +375,7 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_id, struct i40e_hw *hw = &pf->hw; u16 vsi_queue_id, pf_queue_id; enum i40e_queue_type qtype; - u16 next_q, vector_id; + u16 next_q, vector_id, size; u32 reg, reg_idx; u16 itr_idx = 0; @@ -288,17 +405,19 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_id, vsi_queue_id + 1)); } - next_q = find_first_bit(&linklistmap, - (I40E_MAX_VSI_QP * - I40E_VIRTCHNL_SUPPORTED_QTYPES)); + size = I40E_MAX_VSI_QP * I40E_VIRTCHNL_SUPPORTED_QTYPES; + next_q = find_first_bit(&linklistmap, size); + if (unlikely(next_q == size)) + goto irq_list_done; + vsi_queue_id = next_q / I40E_VIRTCHNL_SUPPORTED_QTYPES; qtype = next_q % I40E_VIRTCHNL_SUPPORTED_QTYPES; - pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, vsi_queue_id); + pf_queue_id = i40e_get_real_pf_qid(vf, vsi_id, vsi_queue_id); reg = ((qtype << I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_SHIFT) | pf_queue_id); wr32(hw, reg_idx, reg); - while (next_q < (I40E_MAX_VSI_QP * I40E_VIRTCHNL_SUPPORTED_QTYPES)) { + while (next_q < size) { switch (qtype) { case I40E_QUEUE_TYPE_RX: reg_idx = I40E_QINT_RQCTL(pf_queue_id); @@ -312,16 +431,13 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_id, break; } - next_q = find_next_bit(&linklistmap, - (I40E_MAX_VSI_QP * - I40E_VIRTCHNL_SUPPORTED_QTYPES), - next_q + 1); - if (next_q < - (I40E_MAX_VSI_QP * I40E_VIRTCHNL_SUPPORTED_QTYPES)) { + next_q = find_next_bit(&linklistmap, size, next_q + 1); + if (next_q < size) { vsi_queue_id = next_q / I40E_VIRTCHNL_SUPPORTED_QTYPES; qtype = next_q % I40E_VIRTCHNL_SUPPORTED_QTYPES; - pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, - vsi_queue_id); + pf_queue_id = i40e_get_real_pf_qid(vf, + vsi_id, + vsi_queue_id); } else { pf_queue_id = I40E_QUEUE_END_OF_LIST; qtype = 0; @@ -352,136 +468,6 @@ irq_list_done: i40e_flush(hw); } -/** - * i40e_release_iwarp_qvlist - * @vf: pointer to the VF. - * - **/ -static void i40e_release_iwarp_qvlist(struct i40e_vf *vf) -{ - struct i40e_pf *pf = vf->pf; - struct virtchnl_iwarp_qvlist_info *qvlist_info = vf->qvlist_info; - u32 msix_vf; - u32 i; - - if (!vf->qvlist_info) - return; - - msix_vf = pf->hw.func_caps.num_msix_vectors_vf; - for (i = 0; i < qvlist_info->num_vectors; i++) { - struct virtchnl_iwarp_qv_info *qv_info; - u32 next_q_index, next_q_type; - struct i40e_hw *hw = &pf->hw; - u32 v_idx, reg_idx, reg; - - qv_info = &qvlist_info->qv_info[i]; - if (!qv_info) - continue; - v_idx = qv_info->v_idx; - if (qv_info->ceq_idx != I40E_QUEUE_INVALID_IDX) { - /* Figure out the queue after CEQ and make that the - * first queue. - */ - reg_idx = (msix_vf - 1) * vf->vf_id + qv_info->ceq_idx; - reg = rd32(hw, I40E_VPINT_CEQCTL(reg_idx)); - next_q_index = (reg & I40E_VPINT_CEQCTL_NEXTQ_INDX_MASK) - >> I40E_VPINT_CEQCTL_NEXTQ_INDX_SHIFT; - next_q_type = (reg & I40E_VPINT_CEQCTL_NEXTQ_TYPE_MASK) - >> I40E_VPINT_CEQCTL_NEXTQ_TYPE_SHIFT; - - reg_idx = ((msix_vf - 1) * vf->vf_id) + (v_idx - 1); - reg = (next_q_index & - I40E_VPINT_LNKLSTN_FIRSTQ_INDX_MASK) | - (next_q_type << - I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_SHIFT); - - wr32(hw, I40E_VPINT_LNKLSTN(reg_idx), reg); - } - } - kfree(vf->qvlist_info); - vf->qvlist_info = NULL; -} - -/** - * i40e_config_iwarp_qvlist - * @vf: pointer to the VF info - * @qvlist_info: queue and vector list - * - * Return 0 on success or < 0 on error - **/ -static int i40e_config_iwarp_qvlist(struct i40e_vf *vf, - struct virtchnl_iwarp_qvlist_info *qvlist_info) -{ - struct i40e_pf *pf = vf->pf; - struct i40e_hw *hw = &pf->hw; - struct virtchnl_iwarp_qv_info *qv_info; - u32 v_idx, i, reg_idx, reg; - u32 next_q_idx, next_q_type; - u32 msix_vf, size; - - size = sizeof(struct virtchnl_iwarp_qvlist_info) + - (sizeof(struct virtchnl_iwarp_qv_info) * - (qvlist_info->num_vectors - 1)); - vf->qvlist_info = kzalloc(size, GFP_KERNEL); - vf->qvlist_info->num_vectors = qvlist_info->num_vectors; - - msix_vf = pf->hw.func_caps.num_msix_vectors_vf; - for (i = 0; i < qvlist_info->num_vectors; i++) { - qv_info = &qvlist_info->qv_info[i]; - if (!qv_info) - continue; - v_idx = qv_info->v_idx; - - /* Validate vector id belongs to this vf */ - if (!i40e_vc_isvalid_vector_id(vf, v_idx)) - goto err; - - vf->qvlist_info->qv_info[i] = *qv_info; - - reg_idx = ((msix_vf - 1) * vf->vf_id) + (v_idx - 1); - /* We might be sharing the interrupt, so get the first queue - * index and type, push it down the list by adding the new - * queue on top. Also link it with the new queue in CEQCTL. - */ - reg = rd32(hw, I40E_VPINT_LNKLSTN(reg_idx)); - next_q_idx = ((reg & I40E_VPINT_LNKLSTN_FIRSTQ_INDX_MASK) >> - I40E_VPINT_LNKLSTN_FIRSTQ_INDX_SHIFT); - next_q_type = ((reg & I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_MASK) >> - I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_SHIFT); - - if (qv_info->ceq_idx != I40E_QUEUE_INVALID_IDX) { - reg_idx = (msix_vf - 1) * vf->vf_id + qv_info->ceq_idx; - reg = (I40E_VPINT_CEQCTL_CAUSE_ENA_MASK | - (v_idx << I40E_VPINT_CEQCTL_MSIX_INDX_SHIFT) | - (qv_info->itr_idx << I40E_VPINT_CEQCTL_ITR_INDX_SHIFT) | - (next_q_type << I40E_VPINT_CEQCTL_NEXTQ_TYPE_SHIFT) | - (next_q_idx << I40E_VPINT_CEQCTL_NEXTQ_INDX_SHIFT)); - wr32(hw, I40E_VPINT_CEQCTL(reg_idx), reg); - - reg_idx = ((msix_vf - 1) * vf->vf_id) + (v_idx - 1); - reg = (qv_info->ceq_idx & - I40E_VPINT_LNKLSTN_FIRSTQ_INDX_MASK) | - (I40E_QUEUE_TYPE_PE_CEQ << - I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_SHIFT); - wr32(hw, I40E_VPINT_LNKLSTN(reg_idx), reg); - } - - if (qv_info->aeq_idx != I40E_QUEUE_INVALID_IDX) { - reg = (I40E_VPINT_AEQCTL_CAUSE_ENA_MASK | - (v_idx << I40E_VPINT_AEQCTL_MSIX_INDX_SHIFT) | - (qv_info->itr_idx << I40E_VPINT_AEQCTL_ITR_INDX_SHIFT)); - - wr32(hw, I40E_VPINT_AEQCTL(vf->vf_id), reg); - } - } - - return 0; -err: - kfree(vf->qvlist_info); - vf->qvlist_info = NULL; - return -EINVAL; -} - /** * i40e_config_vsi_tx_queue * @vf: pointer to the VF info @@ -599,7 +585,7 @@ static int i40e_config_vsi_rx_queue(struct i40e_vf *vf, u16 vsi_id, } rx_ctx.hbuff = info->hdr_size >> I40E_RXQ_CTX_HBUFF_SHIFT; - /* set split mode 10b */ + /* set splitalways mode 10b */ rx_ctx.dtype = I40E_RX_DTYPE_HEADER_SPLIT; } @@ -621,7 +607,7 @@ static int i40e_config_vsi_rx_queue(struct i40e_vf *vf, u16 vsi_id, rx_ctx.dsize = 1; /* default values */ - rx_ctx.lrxqthresh = 2; + rx_ctx.lrxqthresh = 1; rx_ctx.crcstrip = 1; rx_ctx.prefena = 1; rx_ctx.l2tsel = 1; @@ -651,2644 +637,6516 @@ error_param: } /** - * i40e_alloc_vsi_res - * @vf: pointer to the VF info - * @type: type of VSI to allocate + * i40e_validate_vf + * @pf: the physical function + * @vf_id: VF identifier * - * alloc VF vsi context & resources + * Check that the VF is enabled and the vsi exists. + * + * Returns 0 on success, negative on failure **/ -static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) +static int i40e_validate_vf(struct i40e_pf *pf, int vf_id) { - struct i40e_mac_filter *f = NULL; - struct i40e_pf *pf = vf->pf; struct i40e_vsi *vsi; + struct i40e_vf *vf; int ret = 0; - vsi = i40e_vsi_setup(pf, type, pf->vsi[pf->lan_vsi]->seid, vf->vf_id); - - if (!vsi) { + if (vf_id >= pf->num_alloc_vfs) { dev_err(&pf->pdev->dev, - "add vsi failed for VF %d, aq_err %d\n", - vf->vf_id, pf->hw.aq.asq_last_status); - ret = -ENOENT; - goto error_alloc_vsi_res; + "Invalid VF Identifier %d\n", vf_id); + ret = -EINVAL; + goto err_out; } - if (type == I40E_VSI_SRIOV) { - u64 hena = i40e_pf_get_default_rss_hena(pf); - u8 broadcast[ETH_ALEN]; + vf = &pf->vf[vf_id]; + vsi = i40e_find_vsi_from_id(pf, vf->lan_vsi_id); + if (!vsi) + ret = -EINVAL; +err_out: + return ret; +} - vf->lan_vsi_idx = vsi->idx; - vf->lan_vsi_id = vsi->id; - /* If the port VLAN has been configured and then the - * VF driver was removed then the VSI port VLAN - * configuration was destroyed. Check if there is - * a port VLAN and restore the VSI configuration if - * needed. - */ - if (vf->port_vlan_id) - i40e_vsi_add_pvid(vsi, vf->port_vlan_id); +#ifdef HAVE_NDO_SET_VF_LINK_STATE - spin_lock_bh(&vsi->mac_filter_hash_lock); - if (is_valid_ether_addr(vf->default_lan_addr.addr)) { - f = i40e_add_mac_filter(vsi, - vf->default_lan_addr.addr); - if (!f) - dev_info(&pf->pdev->dev, - "Could not add MAC filter %pM for VF %d\n", - vf->default_lan_addr.addr, vf->vf_id); - } - eth_broadcast_addr(broadcast); - f = i40e_add_mac_filter(vsi, broadcast); - if (!f) - dev_info(&pf->pdev->dev, - "Could not allocate VF broadcast filter\n"); - spin_unlock_bh(&vsi->mac_filter_hash_lock); - wr32(&pf->hw, I40E_VFQF_HENA1(0, vf->vf_id), (u32)hena); - wr32(&pf->hw, I40E_VFQF_HENA1(1, vf->vf_id), (u32)(hena >> 32)); - } +/** + * i40e_configure_vf_loopback + * @vsi: VF VSI to configure + * @vf_id: VF identifier + * @enable: enable or disable + * + * This function configures the VF VSI with the loopback settings + * + * Returns 0 on success, negative on failure + * + **/ +static int i40e_configure_vf_loopback(struct i40e_vsi *vsi, int vf_id, + bool enable) +{ + struct i40e_pf *pf = vsi->back; + struct i40e_vsi_context ctxt; + int ret = 0; - /* program mac filter */ - ret = i40e_sync_vsi_filters(vsi); - if (ret) - dev_err(&pf->pdev->dev, "Unable to program ucast filters\n"); + vsi->info.valid_sections = CPU_TO_LE16(I40E_AQ_VSI_PROP_SWITCH_VALID); + if (enable) + vsi->info.switch_id |= + CPU_TO_LE16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB); + else + vsi->info.switch_id &= + ~CPU_TO_LE16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB); - /* Set VF bandwidth if specified */ - if (vf->tx_rate) { - ret = i40e_aq_config_vsi_bw_limit(&pf->hw, vsi->seid, - vf->tx_rate / 50, 0, NULL); - if (ret) - dev_err(&pf->pdev->dev, "Unable to set tx rate, VF %d, error code %d.\n", - vf->vf_id, ret); + memset(&ctxt, 0, sizeof(ctxt)); + ctxt.seid = vsi->seid; + ctxt.pf_num = vsi->back->hw.pf_id; + ctxt.info = vsi->info; + ret = i40e_aq_update_vsi_params(&pf->hw, &ctxt, NULL); + if (ret) { + dev_err(&pf->pdev->dev, "Error %d configuring loopback for VF %d\n", + ret, vf_id); + ret = -EIO; } - -error_alloc_vsi_res: return ret; } /** - * i40e_enable_vf_mappings - * @vf: pointer to the VF info + * i40e_configure_vf_vlan_stripping + * @vsi: VF VSI to configure + * @vf_id: VF identifier + * @enable: enable or disable * - * enable VF mappings + * This function enables or disables vlan stripping on the VF + * + * Returns 0 on success, negative on failure **/ -static void i40e_enable_vf_mappings(struct i40e_vf *vf) +static int i40e_configure_vf_vlan_stripping(struct i40e_vsi *vsi, int vf_id, + bool enable) { - struct i40e_pf *pf = vf->pf; - struct i40e_hw *hw = &pf->hw; - u32 reg, total_queue_pairs = 0; - int j; - - /* Tell the hardware we're using noncontiguous mapping. HW requires - * that VF queues be mapped using this method, even when they are - * contiguous in real life - */ - i40e_write_rx_ctl(hw, I40E_VSILAN_QBASE(vf->lan_vsi_id), - I40E_VSILAN_QBASE_VSIQTABLE_ENA_MASK); + struct i40e_pf *pf = vsi->back; + struct i40e_vsi_context ctxt; + int ret = 0; + u8 flag; - /* enable VF vplan_qtable mappings */ - reg = I40E_VPLAN_MAPENA_TXRX_ENA_MASK; - wr32(hw, I40E_VPLAN_MAPENA(vf->vf_id), reg); + vsi->info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID); + if (enable) { + /* Don't enable vlan stripping if port vlan is set */ + if (vsi->info.pvid) { + dev_err(&pf->pdev->dev, + "Cannot enable vlan stripping when port VLAN is set\n"); + ret = -EINVAL; + goto err_out; + } + flag = I40E_AQ_VSI_PVLAN_EMOD_STR_BOTH; + } else { + flag = I40E_AQ_VSI_PVLAN_EMOD_NOTHING; + } + vsi->info.port_vlan_flags = I40E_AQ_VSI_PVLAN_MODE_ALL | flag; + ctxt.seid = vsi->seid; + ctxt.info = vsi->info; + ret = i40e_aq_update_vsi_params(&pf->hw, &ctxt, NULL); + if (ret) { + dev_err(&pf->pdev->dev, "Error %d configuring vlan stripping for VF %d\n", + ret, vf_id); + ret = -EIO; + } +err_out: + return ret; +} - /* map PF queues to VF queues */ - for (j = 0; j < pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; j++) { - u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id, j); +/** + * i40e_configure_vf_promisc_mode + * @vf: VF + * @vsi: VF VSI to configure + * @promisc_mode: promisc mode to configure + * + * This function configures the requested promisc mode for a vf + * + * Returns 0 on success, negative on failure + **/ +static int i40e_configure_vf_promisc_mode(struct i40e_vf *vf, + struct i40e_vsi *vsi, + u8 promisc_mode) +{ + struct i40e_pf *pf = vsi->back; + int ret = 0; - reg = (qid & I40E_VPLAN_QTABLE_QINDEX_MASK); - wr32(hw, I40E_VPLAN_QTABLE(total_queue_pairs, vf->vf_id), reg); - total_queue_pairs++; + if (promisc_mode & VFD_PROMISC_MULTICAST) { + ret = i40e_aq_set_vsi_multicast_promiscuous(&pf->hw, vsi->seid, + true, NULL); + if (ret) + goto err; + vf->promisc_mode |= VFD_PROMISC_MULTICAST; + } else { + ret = i40e_aq_set_vsi_multicast_promiscuous(&pf->hw, vsi->seid, + false, NULL); + if (ret) + goto err; + vf->promisc_mode &= ~VFD_PROMISC_MULTICAST; } - - /* map PF queues to VSI */ - for (j = 0; j < 7; j++) { - if (j * 2 >= pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs) { - reg = 0x07FF07FF; /* unused */ - } else { - u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id, - j * 2); - reg = qid; - qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id, - (j * 2) + 1); - reg |= qid << 16; - } - i40e_write_rx_ctl(hw, I40E_VSILAN_QTABLE(j, vf->lan_vsi_id), - reg); + if (promisc_mode & VFD_PROMISC_UNICAST) { + ret = i40e_aq_set_vsi_unicast_promiscuous(&pf->hw, vsi->seid, + true, NULL, true); + if (ret) + goto err; + vf->promisc_mode |= VFD_PROMISC_UNICAST; + } else { + ret = i40e_aq_set_vsi_unicast_promiscuous(&pf->hw, vsi->seid, + false, NULL, true); + if (ret) + goto err; + vf->promisc_mode &= ~VFD_PROMISC_UNICAST; } +err: + if (ret) + dev_err(&pf->pdev->dev, "Error %d configuring promisc mode for VF %d\n", + ret, vf->vf_id); - i40e_flush(hw); + return ret; } /** - * i40e_disable_vf_mappings - * @vf: pointer to the VF info + * i40e_add_ingress_egress_mirror + * @src_vsi: VSI to mirror from + * @mirror_vsi: VSI to mirror to + * @rule_type: rule type to configure + * @rule_id: rule id to store * - * disable VF mappings + * This function adds the requested ingress/egress mirror for a vsi + * + * Returns 0 on success, negative on failure **/ -static void i40e_disable_vf_mappings(struct i40e_vf *vf) +static int i40e_add_ingress_egress_mirror(struct i40e_vsi *src_vsi, + struct i40e_vsi *mirror_vsi, + u16 rule_type, u16 *rule_id) { - struct i40e_pf *pf = vf->pf; - struct i40e_hw *hw = &pf->hw; - int i; - - /* disable qp mappings */ - wr32(hw, I40E_VPLAN_MAPENA(vf->vf_id), 0); - for (i = 0; i < I40E_MAX_VSI_QP; i++) - wr32(hw, I40E_VPLAN_QTABLE(i, vf->vf_id), - I40E_QUEUE_END_OF_LIST); - i40e_flush(hw); -} - -/** - * i40e_free_vf_res - * @vf: pointer to the VF info - * - * free VF resources - **/ -static void i40e_free_vf_res(struct i40e_vf *vf) -{ - struct i40e_pf *pf = vf->pf; - struct i40e_hw *hw = &pf->hw; - u32 reg_idx, reg; - int i, msix_vf; - - /* Start by disabling VF's configuration API to prevent the OS from - * accessing the VF's VSI after it's freed / invalidated. - */ - clear_bit(I40E_VF_STATE_INIT, &vf->vf_states); - - /* free vsi & disconnect it from the parent uplink */ - if (vf->lan_vsi_idx) { - i40e_vsi_release(pf->vsi[vf->lan_vsi_idx]); - vf->lan_vsi_idx = 0; - vf->lan_vsi_id = 0; - vf->num_mac = 0; + u16 dst_seid, rules_used, rules_free, sw_seid; + struct i40e_pf *pf = src_vsi->back; + int ret, num = 0, cnt = 1; + int *vsi_ingress_vlan; + int *vsi_egress_vlan; + __le16 *mr_list; + + mr_list = kcalloc(cnt, sizeof(__le16), GFP_KERNEL); + if (!mr_list) { + ret = -ENOMEM; + goto err_out; } - msix_vf = pf->hw.func_caps.num_msix_vectors_vf; - /* disable interrupts so the VF starts in a known state */ - for (i = 0; i < msix_vf; i++) { - /* format is same for both registers */ - if (0 == i) - reg_idx = I40E_VFINT_DYN_CTL0(vf->vf_id); - else - reg_idx = I40E_VFINT_DYN_CTLN(((msix_vf - 1) * - (vf->vf_id)) - + (i - 1)); - wr32(hw, reg_idx, I40E_VFINT_DYN_CTLN_CLEARPBA_MASK); - i40e_flush(hw); + if (src_vsi->type == I40E_VSI_MAIN) { + vsi_ingress_vlan = &pf->ingress_vlan; + vsi_egress_vlan = &pf->egress_vlan; + } else { + vsi_ingress_vlan = &pf->vf[src_vsi->vf_id].ingress_vlan; + vsi_egress_vlan = &pf->vf[src_vsi->vf_id].egress_vlan; } - /* clear the irq settings */ - for (i = 0; i < msix_vf; i++) { - /* format is same for both registers */ - if (0 == i) - reg_idx = I40E_VPINT_LNKLST0(vf->vf_id); + if (I40E_IS_MIRROR_VLAN_ID_VALID(*vsi_ingress_vlan)) { + if (src_vsi->type == I40E_VSI_MAIN) + dev_err(&pf->pdev->dev, + "PF already has an ingress mirroring configured, only one rule per PF is supported!\n"); else - reg_idx = I40E_VPINT_LNKLSTN(((msix_vf - 1) * - (vf->vf_id)) - + (i - 1)); - reg = (I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_MASK | - I40E_VPINT_LNKLSTN_FIRSTQ_INDX_MASK); - wr32(hw, reg_idx, reg); - i40e_flush(hw); + dev_err(&pf->pdev->dev, + "VF=%d already has an ingress mirroring configured, only one rule per VF is supported!\n", + src_vsi->vf_id); + ret = -EPERM; + goto err_out; + } else if (I40E_IS_MIRROR_VLAN_ID_VALID(*vsi_egress_vlan)) { + if (src_vsi->type == I40E_VSI_MAIN) + dev_err(&pf->pdev->dev, + "PF already has an egress mirroring configured, only one rule per PF is supported!\n"); + else + dev_err(&pf->pdev->dev, + "VF=%d already has an egress mirroring configured, only one rule per VF is supported!\n", + src_vsi->vf_id); + ret = -EPERM; + goto err_out; } - /* reset some of the state variables keeping track of the resources */ - vf->num_queue_pairs = 0; - vf->vf_states = 0; + + sw_seid = src_vsi->uplink_seid; + dst_seid = mirror_vsi->seid; + mr_list[num] = CPU_TO_LE16(src_vsi->seid); + ret = i40e_aq_add_mirrorrule(&pf->hw, sw_seid, + rule_type, dst_seid, + cnt, mr_list, NULL, + rule_id, &rules_used, + &rules_free); + kfree(mr_list); +err_out: + return ret; } /** - * i40e_alloc_vf_res - * @vf: pointer to the VF info + * i40e_del_ingress_egress_mirror + * @src_vsi: the mirrored VSI + * @rule_type: rule type to configure + * @rule_id : rule id to delete * - * allocate VF resources + * This function deletes the ingress/egress mirror on a VSI + * + * Returns 0 on success, negative on failure **/ -static int i40e_alloc_vf_res(struct i40e_vf *vf) +static int i40e_del_ingress_egress_mirror(struct i40e_vsi *src_vsi, + u16 rule_type, u16 rule_id) { - struct i40e_pf *pf = vf->pf; - int total_queue_pairs = 0; + u16 rules_used, rules_free, sw_seid; + struct i40e_pf *pf = src_vsi->back; int ret; - /* allocate hw vsi context & associated resources */ - ret = i40e_alloc_vsi_res(vf, I40E_VSI_SRIOV); - if (ret) - goto error_alloc; - total_queue_pairs += pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; - - if (vf->trusted) - set_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps); - else - clear_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps); - - /* store the total qps number for the runtime - * VF req validation - */ - vf->num_queue_pairs = total_queue_pairs; - - /* VF is now completely initialized */ - set_bit(I40E_VF_STATE_INIT, &vf->vf_states); - -error_alloc: - if (ret) - i40e_free_vf_res(vf); - + sw_seid = src_vsi->uplink_seid; + ret = i40e_aq_delete_mirrorrule(&pf->hw, sw_seid, rule_type, + rule_id, 0, NULL, NULL, + &rules_used, &rules_free); return ret; } -#define VF_DEVICE_STATUS 0xAA -#define VF_TRANS_PENDING_MASK 0x20 /** - * i40e_quiesce_vf_pci - * @vf: pointer to the VF structure + * i40e_restore_ingress_egress_mirror + * @src_vsi: the mirrored VSI + * @mirror: VSI to mirror to + * @rule_type: rule type to configure + * @rule_id : rule id to delete * - * Wait for VF PCI transactions to be cleared after reset. Returns -EIO - * if the transactions never clear. + * This function restores the configured ingress/egress mirrors + * + * Returns 0 on success, negative on failure **/ -static int i40e_quiesce_vf_pci(struct i40e_vf *vf) +int i40e_restore_ingress_egress_mirror(struct i40e_vsi *src_vsi, + int mirror, u16 rule_type, u16 *rule_id) { - struct i40e_pf *pf = vf->pf; - struct i40e_hw *hw = &pf->hw; - int vf_abs_id, i; - u32 reg; + struct i40e_vsi *mirror_vsi; + struct i40e_vf *mirror_vf; + struct i40e_pf *pf; + int ret = 0; - vf_abs_id = vf->vf_id + hw->func_caps.vf_base_id; + pf = src_vsi->back; - wr32(hw, I40E_PF_PCI_CIAA, - VF_DEVICE_STATUS | (vf_abs_id << I40E_PF_PCI_CIAA_VF_NUM_SHIFT)); - for (i = 0; i < 100; i++) { - reg = rd32(hw, I40E_PF_PCI_CIAD); - if ((reg & VF_TRANS_PENDING_MASK) == 0) - return 0; - udelay(1); - } - return -EIO; + /* validate the mirror */ + ret = i40e_validate_vf(pf, mirror); + if (ret) + goto err_out; + mirror_vf = &pf->vf[mirror]; + mirror_vsi = pf->vsi[mirror_vf->lan_vsi_idx]; + ret = i40e_add_ingress_egress_mirror(src_vsi, mirror_vsi, rule_type, + rule_id); + +err_out: + return ret; } /** - * i40e_trigger_vf_reset - * @vf: pointer to the VF structure - * @flr: VFLR was issued or not + * i40e_configure_vf_link + * @vf: VF + * @link: link state to configure * - * Trigger hardware to start a reset for a particular VF. Expects the caller - * to wait the proper amount of time to allow hardware to reset the VF before - * it cleans up and restores VF functionality. + * This function configures the requested link state for a VF + * + * Returns 0 on success, negative on failure **/ -static void i40e_trigger_vf_reset(struct i40e_vf *vf, bool flr) +static int i40e_configure_vf_link(struct i40e_vf *vf, u8 link) { + struct virtchnl_pf_event pfe; + struct i40e_link_status *ls; struct i40e_pf *pf = vf->pf; - struct i40e_hw *hw = &pf->hw; - u32 reg, reg_idx, bit_idx; - - /* warn the VF */ - clear_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states); - - /* Disable VF's configuration API during reset. The flag is re-enabled - * in i40e_alloc_vf_res(), when it's safe again to access VF's VSI. - * It's normally disabled in i40e_free_vf_res(), but it's safer - * to do it earlier to give some time to finish to any VF config - * functions that may still be running at this point. - */ - clear_bit(I40E_VF_STATE_INIT, &vf->vf_states); + struct i40e_hw *hw; + int abs_vf_id; + int ret = 0; - /* In the case of a VFLR, the HW has already reset the VF and we - * just need to clean up, so don't hit the VFRTRIG register. + hw = &pf->hw; + abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id; + pfe.event = VIRTCHNL_EVENT_LINK_CHANGE; + pfe.severity = PF_EVENT_SEVERITY_INFO; + ls = &pf->hw.phy.link_info; + switch (link) { + case VFD_LINKSTATE_AUTO: + vf->link_forced = false; +#ifdef VIRTCHNL_VF_CAP_ADV_LINK_SPEED + pfe.event_data.link_event_adv.link_status = + ls->link_info & I40E_AQ_LINK_UP; + pfe.event_data.link_event_adv.link_speed = + i40e_vc_link_speed2mbps(ls->link_speed); +#else /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + pfe.event_data.link_event.link_status = + ls->link_info & I40E_AQ_LINK_UP; + pfe.event_data.link_event.link_speed = + i40e_virtchnl_link_speed(ls->link_speed); +#endif /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + break; + case VFD_LINKSTATE_ON: + vf->link_forced = true; + vf->link_up = true; +#ifdef VIRTCHNL_VF_CAP_ADV_LINK_SPEED + pfe.event_data.link_event_adv.link_status = true; + pfe.event_data.link_event_adv.link_speed = + i40e_vc_link_speed2mbps(ls->link_speed); +#else /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + pfe.event_data.link_event.link_status = true; + pfe.event_data.link_event.link_speed = + i40e_virtchnl_link_speed(ls->link_speed); +#endif /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + break; + case VFD_LINKSTATE_OFF: + vf->link_forced = true; + vf->link_up = false; +#ifdef VIRTCHNL_VF_CAP_ADV_LINK_SPEED + pfe.event_data.link_event_adv.link_status = false; + pfe.event_data.link_event_adv.link_speed = 0; +#else /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + pfe.event_data.link_event.link_status = false; + pfe.event_data.link_event.link_speed = 0; +#endif /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + break; + default: + ret = -EINVAL; + goto error_out; + } + /* Do not allow change link state when VF is disabled + * Check if requested link state is not VFD_LINKSTATE_OFF, to prevent + * false positive warning in case of reloading the driver */ - if (!flr) { - /* reset VF using VPGEN_VFRTRIG reg */ - reg = rd32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id)); - reg |= I40E_VPGEN_VFRTRIG_VFSWR_MASK; - wr32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id), reg); - i40e_flush(hw); + if (vf->pf_ctrl_disable && link != VFD_LINKSTATE_OFF) { + vf->link_up = false; +#ifdef VIRTCHNL_VF_CAP_ADV_LINK_SPEED + pfe.event_data.link_event_adv.link_status = false; + pfe.event_data.link_event_adv.link_speed = 0; +#else /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + pfe.event_data.link_event.link_status = false; + pfe.event_data.link_event.link_speed = 0; +#endif /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + dev_warn(&pf->pdev->dev, + "Not possible to change VF link state, please enable it first\n"); } - /* clear the VFLR bit in GLGEN_VFLRSTAT */ - reg_idx = (hw->func_caps.vf_base_id + vf->vf_id) / 32; - bit_idx = (hw->func_caps.vf_base_id + vf->vf_id) % 32; - wr32(hw, I40E_GLGEN_VFLRSTAT(reg_idx), BIT(bit_idx)); - i40e_flush(hw); - if (i40e_quiesce_vf_pci(vf)) - dev_err(&pf->pdev->dev, "VF %d PCI transactions stuck\n", - vf->vf_id); + /* Notify the VF of its new link state */ + i40e_aq_send_msg_to_vf(hw, abs_vf_id, VIRTCHNL_OP_EVENT, + I40E_SUCCESS, (u8 *)&pfe, sizeof(pfe), NULL); +error_out: + return ret; } /** - * i40e_cleanup_reset_vf + * i40e_vf_del_vlan_mirror * @vf: pointer to the VF structure + * @vsi: pointer to the VSI structure + * + * Delete configured mirror vlans + * + * Returns 0 on success, negative on failure * - * Cleanup a VF after the hardware reset is finished. Expects the caller to - * have verified whether the reset is finished properly, and ensure the - * minimum amount of wait time has passed. **/ -static void i40e_cleanup_reset_vf(struct i40e_vf *vf) +static int i40e_vf_del_vlan_mirror(struct i40e_vf *vf, struct i40e_vsi *vsi) { + u16 rules_used, rules_free, vid; struct i40e_pf *pf = vf->pf; - struct i40e_hw *hw = &pf->hw; - u32 reg; - - /* free VF resources to begin resetting the VSI state */ - i40e_free_vf_res(vf); + int ret = 0, num = 0, cnt; + __le16 *mr_list; + + cnt = bitmap_weight(vf->mirror_vlans, VLAN_N_VID); + if (cnt) { + mr_list = kcalloc(cnt, sizeof(__le16), GFP_KERNEL); + if (!mr_list) + return -ENOMEM; + + for_each_set_bit(vid, vf->mirror_vlans, VLAN_N_VID) { + mr_list[num] = CPU_TO_LE16(vid); + num++; + } - /* Enable hardware by clearing the reset bit in the VPGEN_VFRTRIG reg. - * By doing this we allow HW to access VF memory at any point. If we - * did it any sooner, HW could access memory while it was being freed - * in i40e_free_vf_res(), causing an IOMMU fault. - * - * On the other hand, this needs to be done ASAP, because the VF driver - * is waiting for this to happen and may report a timeout. It's - * harmless, but it gets logged into Guest OS kernel log, so best avoid - * it. - */ - reg = rd32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id)); - reg &= ~I40E_VPGEN_VFRTRIG_VFSWR_MASK; - wr32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id), reg); + ret = i40e_aq_delete_mirrorrule(&pf->hw, vsi->uplink_seid, + I40E_AQC_MIRROR_RULE_TYPE_VLAN, + vf->vlan_rule_id, cnt, mr_list, + NULL, &rules_used, + &rules_free); - /* reallocate VF resources to finish resetting the VSI state */ - if (!i40e_alloc_vf_res(vf)) { - int abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id; - i40e_enable_vf_mappings(vf); - set_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states); - clear_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); - /* Do not notify the client during VF init */ - if (test_and_clear_bit(I40E_VF_STATE_PRE_ENABLE, - &vf->vf_states)) - i40e_notify_client_of_vf_reset(pf, abs_vf_id); - vf->num_vlan = 0; + vf->vlan_rule_id = 0; + kfree(mr_list); } - /* Tell the VF driver the reset is done. This needs to be done only - * after VF has been fully initialized, because the VF driver may - * request resources immediately after setting this flag. - */ - wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_id), VIRTCHNL_VFR_VFACTIVE); + return ret; } /** - * i40e_reset_vf + * i40e_restore_vfd_config * @vf: pointer to the VF structure - * @flr: VFLR was issued or not + * @vsi: VF VSI to be configured + * + * Restore the VF-d config as per the stored configuration + * + * Returns 0 on success, negative on failure * - * reset the VF **/ -void i40e_reset_vf(struct i40e_vf *vf, bool flr) +static int i40e_restore_vfd_config(struct i40e_vf *vf, struct i40e_vsi *vsi) { struct i40e_pf *pf = vf->pf; - struct i40e_hw *hw = &pf->hw; - bool rsd = false; - u32 reg; - int i; + int ret = 0, cnt = 0; + u16 vid; - /* If VFs have been disabled, there is no need to reset */ - if (test_and_set_bit(__I40E_VF_DISABLE, pf->state)) - return; + /* Restore all VF-d configuration on reset */ + for_each_set_bit(vid, vf->trunk_vlans, VLAN_N_VID) { + ret = i40e_vsi_add_vlan(vsi, vid); + if (ret) + goto err_out; + } + if (!vf->allow_untagged) { + spin_lock_bh(&vsi->mac_filter_hash_lock); + i40e_rm_vlan_all_mac(vsi, 0); + spin_unlock_bh(&vsi->mac_filter_hash_lock); + i40e_service_event_schedule(vsi->back); + } + + cnt = bitmap_weight(vf->mirror_vlans, VLAN_N_VID); + if (cnt) { + u16 rule_type = I40E_AQC_MIRROR_RULE_TYPE_VLAN; + u16 rule_id, rules_used, rules_free; + u16 sw_seid = vsi->uplink_seid; + u16 dst_seid = vsi->seid; + __le16 *mr_list; + int num = 0; + + mr_list = kcalloc(cnt, sizeof(__le16), GFP_KERNEL); + if (!mr_list) + return -ENOMEM; + for_each_set_bit(vid, vf->mirror_vlans, VLAN_N_VID) { + mr_list[num] = CPU_TO_LE16(vid); + num++; + } + ret = i40e_aq_add_mirrorrule(&pf->hw, sw_seid, rule_type, + dst_seid, cnt, mr_list, NULL, + &rule_id, &rules_used, + &rules_free); + if (!ret) + vf->vlan_rule_id = rule_id; + kfree(mr_list); + } - i40e_trigger_vf_reset(vf, flr); + ret = i40e_configure_vf_loopback(vsi, vf->vf_id, vf->loopback); + if (ret) { + vf->loopback = false; + goto err_out; + } - /* poll VPGEN_VFRSTAT reg to make sure - * that reset is complete - */ - for (i = 0; i < 10; i++) { - /* VF reset requires driver to first reset the VF and then - * poll the status register to make sure that the reset - * completed successfully. Due to internal HW FIFO flushes, - * we must wait 10ms before the register will be valid. - */ - usleep_range(10000, 20000); - reg = rd32(hw, I40E_VPGEN_VFRSTAT(vf->vf_id)); - if (reg & I40E_VPGEN_VFRSTAT_VFRD_MASK) { - rsd = true; - break; + if (vf->vlan_stripping) { + ret = i40e_configure_vf_vlan_stripping(vsi, vf->vf_id, true); + if (ret) { + vf->vlan_stripping = false; + goto err_out; } } - if (flr) - usleep_range(10000, 20000); + if (vf->promisc_mode) { + ret = i40e_configure_vf_promisc_mode(vf, vsi, vf->promisc_mode); + if (ret) { + vf->promisc_mode = VFD_PROMISC_OFF; + goto err_out; + } + } - if (!rsd) - dev_err(&pf->pdev->dev, "VF reset check timeout on VF %d\n", - vf->vf_id); - usleep_range(10000, 20000); + if (vf->link_forced) { + u8 link; - /* On initial reset, we don't have any queues to disable */ - if (vf->lan_vsi_idx != 0) - i40e_vsi_stop_rings(pf->vsi[vf->lan_vsi_idx]); + link = (vf->link_up ? VFD_LINKSTATE_ON : VFD_LINKSTATE_OFF); + ret = i40e_configure_vf_link(vf, link); + if (ret) { + vf->link_forced = false; + goto err_out; + } + } - i40e_cleanup_reset_vf(vf); + if (vf->bw_share_applied && vf->bw_share) { + struct i40e_aqc_configure_vsi_tc_bw_data bw_data = {0}; + int i; - i40e_flush(hw); - clear_bit(__I40E_VF_DISABLE, pf->state); -} + bw_data.tc_valid_bits = 1; + bw_data.tc_bw_credits[0] = vf->bw_share; + + ret = i40e_aq_config_vsi_tc_bw(&pf->hw, vsi->seid, &bw_data, NULL); + if (ret) { + dev_info(&pf->pdev->dev, + "AQ command Config VSI BW allocation per TC failed = %d\n", + pf->hw.aq.asq_last_status); + vf->bw_share_applied = false; + goto err_out; + } + + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) + vsi->info.qs_handle[i] = bw_data.qs_handles[i]; + } +err_out: + return ret; +} +#endif /* HAVE_NDO_SET_VF_LINK_STATE */ /** - * i40e_reset_all_vfs - * @pf: pointer to the PF structure - * @flr: VFLR was issued or not + * i40e_alloc_vsi_res + * @vf: pointer to the VF info + * @idx: VSI index, applies only for ADq mode, zero otherwise * - * Reset all allocated VFs in one go. First, tell the hardware to reset each - * VF, then do all the waiting in one chunk, and finally finish restoring each - * VF after the wait. This is useful during PF routines which need to reset - * all VFs, as otherwise it must perform these resets in a serialized fashion. + * alloc VF vsi context & resources **/ -void i40e_reset_all_vfs(struct i40e_pf *pf, bool flr) +static int i40e_alloc_vsi_res(struct i40e_vf *vf, u8 idx) { - struct i40e_hw *hw = &pf->hw; - struct i40e_vf *vf; - int i, v; - u32 reg; - - /* If we don't have any VFs, then there is nothing to reset */ - if (!pf->num_alloc_vfs) - return; + struct i40e_mac_filter *f = NULL; + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi; + u64 max_tx_rate = 0; + int ret = 0; - /* If VFs have been disabled, there is no need to reset */ - if (test_and_set_bit(__I40E_VF_DISABLE, pf->state)) - return; + vsi = i40e_vsi_setup(pf, I40E_VSI_SRIOV, pf->vsi[pf->lan_vsi]->seid, + vf->vf_id); - /* Begin reset on all VFs at once */ - for (v = 0; v < pf->num_alloc_vfs; v++) - i40e_trigger_vf_reset(&pf->vf[v], flr); + if (!vsi) { + dev_err(&pf->pdev->dev, + "add vsi failed for VF %d, aq_err %d\n", + vf->vf_id, pf->hw.aq.asq_last_status); + ret = -ENOENT; + goto error_alloc_vsi_res; + } - /* HW requires some time to make sure it can flush the FIFO for a VF - * when it resets it. Poll the VPGEN_VFRSTAT register for each VF in - * sequence to make sure that it has completed. We'll keep track of - * the VFs using a simple iterator that increments once that VF has - * finished resetting. - */ - for (i = 0, v = 0; i < 10 && v < pf->num_alloc_vfs; i++) { - usleep_range(10000, 20000); + if (!idx) { + u64 hena = i40e_pf_get_default_rss_hena(pf); + bool trunk_conf = false; + u8 broadcast[ETH_ALEN]; + u16 vid; - /* Check each VF in sequence, beginning with the VF to fail - * the previous check. + vf->lan_vsi_idx = vsi->idx; + vf->lan_vsi_id = vsi->id; + /* If the port VLAN has been configured and then the + * VF driver was removed then the VSI port VLAN + * configuration was destroyed. Check if there is + * a port VLAN and restore the VSI configuration if + * needed. */ - while (v < pf->num_alloc_vfs) { - vf = &pf->vf[v]; - reg = rd32(hw, I40E_VPGEN_VFRSTAT(vf->vf_id)); - if (!(reg & I40E_VPGEN_VFRSTAT_VFRD_MASK)) - break; + for_each_set_bit(vid, vf->trunk_vlans, VLAN_N_VID) { + if (vid != vf->port_vlan_id) + trunk_conf = true; + } + if (vf->port_vlan_id && !trunk_conf) + i40e_vsi_add_pvid(vsi, vf->port_vlan_id); - /* If the current VF has finished resetting, move on - * to the next VF in sequence. - */ - v++; + spin_lock_bh(&vsi->mac_filter_hash_lock); + if (is_valid_ether_addr(vf->default_lan_addr.addr)) { + f = i40e_add_mac_filter(vsi, + vf->default_lan_addr.addr); + if (!f) + dev_info(&pf->pdev->dev, + "Could not add MAC filter %pM for VF %d\n", + vf->default_lan_addr.addr, vf->vf_id); } + eth_broadcast_addr(broadcast); + f = i40e_add_mac_filter(vsi, broadcast); + if (!f) + dev_info(&pf->pdev->dev, + "Could not allocate VF broadcast filter\n"); + spin_unlock_bh(&vsi->mac_filter_hash_lock); + wr32(&pf->hw, I40E_VFQF_HENA1(0, vf->vf_id), (u32)hena); + wr32(&pf->hw, I40E_VFQF_HENA1(1, vf->vf_id), (u32)(hena >> 32)); + /* program mac filter only for VF VSI */ + ret = i40e_sync_vsi_filters(vsi); + if (ret) + dev_err(&pf->pdev->dev, "Unable to program ucast filters\n"); } - if (flr) - usleep_range(10000, 20000); - - /* Display a warning if at least one VF didn't manage to reset in - * time, but continue on with the operation. - */ - if (v < pf->num_alloc_vfs) - dev_err(&pf->pdev->dev, "VF reset check timeout on VF %d\n", - pf->vf[v].vf_id); - usleep_range(10000, 20000); - - /* Begin disabling all the rings associated with VFs, but do not wait - * between each VF. - */ - for (v = 0; v < pf->num_alloc_vfs; v++) { - /* On initial reset, we don't have any queues to disable */ - if (pf->vf[v].lan_vsi_idx == 0) - continue; - - i40e_vsi_stop_rings_no_wait(pf->vsi[pf->vf[v].lan_vsi_idx]); + /* storing VSI index and id for ADq and don't apply the mac filter */ + if (vf->adq_enabled) { + vf->ch[idx].vsi_idx = vsi->idx; + vf->ch[idx].vsi_id = vsi->id; } - /* Now that we've notified HW to disable all of the VF rings, wait - * until they finish. - */ - for (v = 0; v < pf->num_alloc_vfs; v++) { - /* On initial reset, we don't have any queues to disable */ - if (pf->vf[v].lan_vsi_idx == 0) - continue; - - i40e_vsi_wait_queues_disabled(pf->vsi[pf->vf[v].lan_vsi_idx]); + /* Set VF bandwidth if specified */ + if (vf->tx_rate) { + max_tx_rate = vf->tx_rate; + } else if (vf->ch[idx].max_tx_rate) { + max_tx_rate = vf->ch[idx].max_tx_rate; } - /* Hw may need up to 50ms to finish disabling the RX queues. We - * minimize the wait by delaying only once for all VFs. - */ - mdelay(50); - - /* Finish the reset on each VF */ - for (v = 0; v < pf->num_alloc_vfs; v++) - i40e_cleanup_reset_vf(&pf->vf[v]); + if (max_tx_rate) { + max_tx_rate = div_u64(max_tx_rate, I40E_BW_CREDIT_DIVISOR); + ret = i40e_aq_config_vsi_bw_limit(&pf->hw, vsi->seid, + max_tx_rate, 0, NULL); + if (ret) + dev_err(&pf->pdev->dev, "Unable to set tx rate, VF %d, error code %d.\n", + vf->vf_id, ret); + } +#ifdef HAVE_NDO_SET_VF_LINK_STATE + ret = i40e_restore_vfd_config(vf, vsi); + if (ret) + dev_err(&pf->pdev->dev, + "Failed to restore VF-d config error %d\n", ret); +#endif /* HAVE_NDO_SET_VF_LINK_STATE */ - i40e_flush(hw); - clear_bit(__I40E_VF_DISABLE, pf->state); +error_alloc_vsi_res: + return ret; } /** - * i40e_free_vfs - * @pf: pointer to the PF structure + * i40e_map_pf_queues_to_vsi + * @vf: pointer to the VF info * - * free VF resources + * PF maps LQPs to a VF by programming VSILAN_QTABLE & VPLAN_QTABLE. This + * function takes care of first part VSILAN_QTABLE, mapping pf queues to VSI. **/ -void i40e_free_vfs(struct i40e_pf *pf) +static void i40e_map_pf_queues_to_vsi(struct i40e_vf *vf) { + struct i40e_pf *pf = vf->pf; struct i40e_hw *hw = &pf->hw; - u32 reg_idx, bit_idx; - int i, tmp, vf_id; - - if (!pf->vf) - return; - while (test_and_set_bit(__I40E_VF_DISABLE, pf->state)) - usleep_range(1000, 2000); + u32 reg, num_tc = 1; /* VF has at least one traffic class */ + u16 vsi_id, qps; + int i, j; - i40e_notify_client_of_vf_enable(pf, 0); + if (vf->adq_enabled) + num_tc = vf->num_tc; - /* Amortize wait time by stopping all VFs at the same time */ - for (i = 0; i < pf->num_alloc_vfs; i++) { - if (test_bit(I40E_VF_STATE_INIT, &pf->vf[i].vf_states)) - continue; + for (i = 0; i < num_tc; i++) { + if (vf->adq_enabled) { + qps = vf->ch[i].num_qps; + vsi_id = vf->ch[i].vsi_id; + } else { + qps = pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; + vsi_id = vf->lan_vsi_id; + } - i40e_vsi_stop_rings_no_wait(pf->vsi[pf->vf[i].lan_vsi_idx]); + for (j = 0; j < 7; j++) { + if (j * 2 >= qps) { + /* end of list */ + reg = 0x07FF07FF; + } else { + u16 qid = i40e_vc_get_pf_queue_id(vf, + vsi_id, + j * 2); + reg = qid; + qid = i40e_vc_get_pf_queue_id(vf, vsi_id, + (j * 2) + 1); + reg |= qid << 16; + } + i40e_write_rx_ctl(hw, + I40E_VSILAN_QTABLE(j, vsi_id), + reg); + } } +} - for (i = 0; i < pf->num_alloc_vfs; i++) { - if (test_bit(I40E_VF_STATE_INIT, &pf->vf[i].vf_states)) - continue; +/** + * i40e_map_pf_to_vf_queues + * @vf: pointer to the VF info + * + * PF maps LQPs to a VF by programming VSILAN_QTABLE & VPLAN_QTABLE. This + * function takes care of the second part VPLAN_QTABLE & completes VF mappings. + **/ +static void i40e_map_pf_to_vf_queues(struct i40e_vf *vf) +{ + struct i40e_pf *pf = vf->pf; + struct i40e_hw *hw = &pf->hw; + u32 reg, total_qps = 0; + u32 qps, num_tc = 1; /* VF has at least one traffic class */ + u16 vsi_id, qid; + int i, j; + + if (vf->adq_enabled) + num_tc = vf->num_tc; + + for (i = 0; i < num_tc; i++) { + if (vf->adq_enabled) { + qps = vf->ch[i].num_qps; + vsi_id = vf->ch[i].vsi_id; + } else { + qps = pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; + vsi_id = vf->lan_vsi_id; + } - i40e_vsi_wait_queues_disabled(pf->vsi[pf->vf[i].lan_vsi_idx]); + for (j = 0; j < qps; j++) { + qid = i40e_vc_get_pf_queue_id(vf, vsi_id, j); + + reg = (qid & I40E_VPLAN_QTABLE_QINDEX_MASK); + wr32(hw, I40E_VPLAN_QTABLE(total_qps, vf->vf_id), + reg); + total_qps++; + } } +} - /* Disable IOV before freeing resources. This lets any VF drivers - * running in the host get themselves cleaned up before we yank - * the carpet out from underneath their feet. +/** + * i40e_enable_vf_mappings + * @vf: pointer to the VF info + * + * enable VF mappings + **/ +static void i40e_enable_vf_mappings(struct i40e_vf *vf) +{ + struct i40e_pf *pf = vf->pf; + struct i40e_hw *hw = &pf->hw; + u32 reg; + + /* Tell the hardware we're using noncontiguous mapping. HW requires + * that VF queues be mapped using this method, even when they are + * contiguous in real life */ - if (!pci_vfs_assigned(pf->pdev)) - pci_disable_sriov(pf->pdev); - else - dev_warn(&pf->pdev->dev, "VFs are assigned - not disabling SR-IOV\n"); + i40e_write_rx_ctl(hw, I40E_VSILAN_QBASE(vf->lan_vsi_id), + I40E_VSILAN_QBASE_VSIQTABLE_ENA_MASK); - /* free up VF resources */ - tmp = pf->num_alloc_vfs; - pf->num_alloc_vfs = 0; - for (i = 0; i < tmp; i++) { - if (test_bit(I40E_VF_STATE_INIT, &pf->vf[i].vf_states)) - i40e_free_vf_res(&pf->vf[i]); - /* disable qp mappings */ - i40e_disable_vf_mappings(&pf->vf[i]); - } + /* enable VF vplan_qtable mappings */ + reg = I40E_VPLAN_MAPENA_TXRX_ENA_MASK; + wr32(hw, I40E_VPLAN_MAPENA(vf->vf_id), reg); - kfree(pf->vf); - pf->vf = NULL; + i40e_map_pf_to_vf_queues(vf); + i40e_map_pf_queues_to_vsi(vf); - /* This check is for when the driver is unloaded while VFs are - * assigned. Setting the number of VFs to 0 through sysfs is caught - * before this function ever gets called. - */ - if (!pci_vfs_assigned(pf->pdev)) { - /* Acknowledge VFLR for all VFS. Without this, VFs will fail to - * work correctly when SR-IOV gets re-enabled. - */ - for (vf_id = 0; vf_id < tmp; vf_id++) { - reg_idx = (hw->func_caps.vf_base_id + vf_id) / 32; - bit_idx = (hw->func_caps.vf_base_id + vf_id) % 32; - wr32(hw, I40E_GLGEN_VFLRSTAT(reg_idx), BIT(bit_idx)); - } - } - clear_bit(__I40E_VF_DISABLE, pf->state); + i40e_flush(hw); } -#ifdef CONFIG_PCI_IOV /** - * i40e_alloc_vfs - * @pf: pointer to the PF structure - * @num_alloc_vfs: number of VFs to allocate + * i40e_disable_vf_mappings + * @vf: pointer to the VF info * - * allocate VF resources + * disable VF mappings **/ -int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs) +static void i40e_disable_vf_mappings(struct i40e_vf *vf) { - struct i40e_vf *vfs; - int i, ret = 0; + struct i40e_pf *pf = vf->pf; + struct i40e_hw *hw = &pf->hw; + int i; - /* Disable interrupt 0 so we don't try to handle the VFLR. */ - i40e_irq_dynamic_disable_icr0(pf); + /* disable qp mappings */ + wr32(hw, I40E_VPLAN_MAPENA(vf->vf_id), 0); + for (i = 0; i < I40E_MAX_VSI_QP; i++) + wr32(hw, I40E_VPLAN_QTABLE(i, vf->vf_id), + I40E_QUEUE_END_OF_LIST); + i40e_flush(hw); +} - /* Check to see if we're just allocating resources for extant VFs */ - if (pci_num_vf(pf->pdev) != num_alloc_vfs) { - ret = pci_enable_sriov(pf->pdev, num_alloc_vfs); - if (ret) { - pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED; - pf->num_alloc_vfs = 0; - goto err_iov; - } - } - /* allocate memory */ - vfs = kcalloc(num_alloc_vfs, sizeof(struct i40e_vf), GFP_KERNEL); - if (!vfs) { - ret = -ENOMEM; - goto err_alloc; - } - pf->vf = vfs; +/** + * i40e_free_vf_res + * @vf: pointer to the VF info + * + * free VF resources + **/ +static void i40e_free_vf_res(struct i40e_vf *vf) +{ + struct i40e_pf *pf = vf->pf; + struct i40e_hw *hw = &pf->hw; + u32 reg_idx, reg; + int i, j, msix_vf; - /* apply default profile */ - for (i = 0; i < num_alloc_vfs; i++) { - vfs[i].pf = pf; - vfs[i].parent_type = I40E_SWITCH_ELEMENT_TYPE_VEB; - vfs[i].vf_id = i; + /* Start by disabling VF's configuration API to prevent the OS from + * accessing the VF's VSI after it's freed / invalidated. + */ + clear_bit(I40E_VF_STATE_INIT, &vf->vf_states); - /* assign default capabilities */ - set_bit(I40E_VIRTCHNL_VF_CAP_L2, &vfs[i].vf_caps); - vfs[i].spoofchk = true; +#ifdef HAVE_NDO_SET_VF_LINK_STATE + /* Release vlan mirror */ + if (vf->lan_vsi_idx) + i40e_vf_del_vlan_mirror(vf, pf->vsi[vf->lan_vsi_idx]); +#endif /* HAVE_NDO_SET_VF_LINK_STATE */ - set_bit(I40E_VF_STATE_PRE_ENABLE, &vfs[i].vf_states); + /* It's possible the VF had requeuested more queues than the default so + * do the accounting here when we're about to free them. + */ + if (vf->num_queue_pairs > I40E_DEFAULT_QUEUES_PER_VF) { + pf->queues_left += + vf->num_queue_pairs - I40E_DEFAULT_QUEUES_PER_VF; + } + /* free vsi & disconnect it from the parent uplink */ + if (vf->lan_vsi_idx) { + i40e_vsi_release(pf->vsi[vf->lan_vsi_idx]); + vf->lan_vsi_idx = 0; + vf->lan_vsi_id = 0; } - pf->num_alloc_vfs = num_alloc_vfs; - /* VF resources get allocated during reset */ - i40e_reset_all_vfs(pf, false); + /* do the accounting and remove additional ADq VSI's */ + if (vf->adq_enabled && vf->ch[0].vsi_idx) { + for (j = 0; j < vf->num_tc; j++) { + /* At this point VSI0 is already released so don't + * release it again and only clear their values in + * structure variables + */ + if (j) + i40e_vsi_release(pf->vsi[vf->ch[j].vsi_idx]); + vf->ch[j].vsi_idx = 0; + vf->ch[j].vsi_id = 0; + } + } + msix_vf = pf->hw.func_caps.num_msix_vectors_vf; - i40e_notify_client_of_vf_enable(pf, num_alloc_vfs); + /* disable interrupts so the VF starts in a known state */ + for (i = 0; i < msix_vf; i++) { + /* format is same for both registers */ + if (0 == i) + reg_idx = I40E_VFINT_DYN_CTL0(vf->vf_id); + else + reg_idx = I40E_VFINT_DYN_CTLN(((msix_vf - 1) * + (vf->vf_id)) + + (i - 1)); + wr32(hw, reg_idx, I40E_VFINT_DYN_CTLN_CLEARPBA_MASK); + i40e_flush(hw); + } -err_alloc: - if (ret) - i40e_free_vfs(pf); -err_iov: - /* Re-enable interrupt 0. */ - i40e_irq_dynamic_enable_icr0(pf, false); - return ret; + /* clear the irq settings */ + for (i = 0; i < msix_vf; i++) { + /* format is same for both registers */ + if (0 == i) + reg_idx = I40E_VPINT_LNKLST0(vf->vf_id); + else + reg_idx = I40E_VPINT_LNKLSTN(((msix_vf - 1) * + (vf->vf_id)) + + (i - 1)); + reg = (I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_MASK | + I40E_VPINT_LNKLSTN_FIRSTQ_INDX_MASK); + wr32(hw, reg_idx, reg); + i40e_flush(hw); + } + /* reset some of the state variables keeping track of the resources */ + vf->num_queue_pairs = 0; + clear_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states); + clear_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states); } -#endif /** - * i40e_pci_sriov_enable - * @pdev: pointer to a pci_dev structure - * @num_vfs: number of VFs to allocate + * i40e_alloc_vf_res + * @vf: pointer to the VF info * - * Enable or change the number of VFs + * allocate VF resources **/ -static int i40e_pci_sriov_enable(struct pci_dev *pdev, int num_vfs) +static int i40e_alloc_vf_res(struct i40e_vf *vf) { -#ifdef CONFIG_PCI_IOV - struct i40e_pf *pf = pci_get_drvdata(pdev); - int pre_existing_vfs = pci_num_vf(pdev); - int err = 0; + struct i40e_pf *pf = vf->pf; + int total_queue_pairs = 0; + int ret, idx; - if (test_bit(__I40E_TESTING, pf->state)) { - dev_warn(&pdev->dev, - "Cannot enable SR-IOV virtual functions while the device is undergoing diagnostic testing\n"); - err = -EPERM; - goto err_out; - } + if (vf->num_req_queues && + vf->num_req_queues <= pf->queues_left + I40E_DEFAULT_QUEUES_PER_VF) + pf->num_vf_qps = vf->num_req_queues; + else + pf->num_vf_qps = I40E_DEFAULT_QUEUES_PER_VF; - if (pre_existing_vfs && pre_existing_vfs != num_vfs) - i40e_free_vfs(pf); - else if (pre_existing_vfs && pre_existing_vfs == num_vfs) - goto out; + /* allocate hw vsi context & associated resources */ + ret = i40e_alloc_vsi_res(vf, 0); + if (ret) + goto error_alloc; + total_queue_pairs += pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; - if (num_vfs > pf->num_req_vfs) { - dev_warn(&pdev->dev, "Unable to enable %d VFs. Limited to %d VFs due to device resource constraints.\n", - num_vfs, pf->num_req_vfs); - err = -EPERM; - goto err_out; + /* allocate additional VSIs based on tc information for ADq */ + if (vf->adq_enabled) { + if (pf->queues_left >= + (I40E_MAX_VF_QUEUES - I40E_DEFAULT_QUEUES_PER_VF)) { + /* TC 0 always belongs to VF VSI */ + for (idx = 1; idx < vf->num_tc; idx++) { + ret = i40e_alloc_vsi_res(vf, idx); + if (ret) + goto error_alloc; + } + /* send correct number of queues */ + total_queue_pairs = I40E_MAX_VF_QUEUES; + } else { + dev_info(&pf->pdev->dev, "VF %d: Not enough queues to allocate, disabling ADq\n", + vf->vf_id); + vf->adq_enabled = false; + } } - dev_info(&pdev->dev, "Allocating %d VFs.\n", num_vfs); - err = i40e_alloc_vfs(pf, num_vfs); - if (err) { - dev_warn(&pdev->dev, "Failed to enable SR-IOV: %d\n", err); - goto err_out; - } + /* We account for each VF to get a default number of queue pairs. If + * the VF has now requested more, we need to account for that to make + * certain we never request more queues than we actually have left in + * HW. + */ + if (total_queue_pairs > I40E_DEFAULT_QUEUES_PER_VF) + pf->queues_left -= + total_queue_pairs - I40E_DEFAULT_QUEUES_PER_VF; -out: - return num_vfs; + if (vf->trusted) + set_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps); + else + clear_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps); -err_out: - return err; -#endif - return 0; + /* store the total qps number for the runtime + * VF req validation + */ + vf->num_queue_pairs = total_queue_pairs; + + /* VF is now completely initialized */ + set_bit(I40E_VF_STATE_INIT, &vf->vf_states); + +error_alloc: + if (ret) + i40e_free_vf_res(vf); + + return ret; } +#define VF_DEVICE_STATUS 0xAA +#define VF_TRANS_PENDING_MASK 0x20 /** - * i40e_pci_sriov_configure - * @pdev: pointer to a pci_dev structure - * @num_vfs: number of VFs to allocate + * i40e_quiesce_vf_pci + * @vf: pointer to the VF structure * - * Enable or change the number of VFs. Called when the user updates the number - * of VFs in sysfs. + * Wait for VF PCI transactions to be cleared after reset. Returns -EIO + * if the transactions never clear. **/ -int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs) +static int i40e_quiesce_vf_pci(struct i40e_vf *vf) { - struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_pf *pf = vf->pf; + struct i40e_hw *hw = &pf->hw; + int vf_abs_id, i; + u32 reg; - if (num_vfs) { - if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) { - pf->flags |= I40E_FLAG_VEB_MODE_ENABLED; - i40e_do_reset_safe(pf, - BIT_ULL(__I40E_PF_RESET_REQUESTED)); - } - return i40e_pci_sriov_enable(pdev, num_vfs); - } + vf_abs_id = vf->vf_id + hw->func_caps.vf_base_id; - if (!pci_vfs_assigned(pf->pdev)) { - i40e_free_vfs(pf); - pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED; - i40e_do_reset_safe(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED)); - } else { - dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n"); - return -EINVAL; + wr32(hw, I40E_PF_PCI_CIAA, + VF_DEVICE_STATUS | (vf_abs_id << I40E_PF_PCI_CIAA_VF_NUM_SHIFT)); + for (i = 0; i < 100; i++) { + reg = rd32(hw, I40E_PF_PCI_CIAD); + if ((reg & VF_TRANS_PENDING_MASK) == 0) + return 0; + udelay(1); } - return 0; + return -EIO; } -/***********************virtual channel routines******************/ +static inline int i40e_getnum_vf_vsi_vlan_filters(struct i40e_vsi *vsi); +static inline void i40e_get_vlan_list_sync(struct i40e_vsi *vsi, int *num_vlans, + s16 **vlan_list); +static inline i40e_status +i40e_set_vsi_promisc(struct i40e_vf *vf, u16 seid, bool multi_enable, + bool unicast_enable, s16 *vl, int num_vlans); /** - * i40e_vc_send_msg_to_vf + * i40e_config_vf_promiscuous_mode * @vf: pointer to the VF info - * @v_opcode: virtual channel opcode - * @v_retval: virtual channel return value - * @msg: pointer to the msg buffer - * @msglen: msg length + * @vsi_id: VSI id + * @allmulti: set MAC L2 layer multicast promiscuous enable/disable + * @alluni: set MAC L2 layer unicast promiscuous enable/disable * - * send msg to VF + * Called from the VF to configure the promiscuous mode of + * VF vsis and from the VF reset path to reset promiscuous mode. **/ -static int i40e_vc_send_msg_to_vf(struct i40e_vf *vf, u32 v_opcode, - u32 v_retval, u8 *msg, u16 msglen) +static i40e_status i40e_config_vf_promiscuous_mode(struct i40e_vf *vf, + u16 vsi_id, + bool allmulti, + bool alluni) { - struct i40e_pf *pf; - struct i40e_hw *hw; - int abs_vf_id; - i40e_status aq_ret; + i40e_status aq_ret = I40E_SUCCESS; + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi; + int num_vlans; + s16 *vl; - /* validate the request */ - if (!vf || vf->vf_id >= vf->pf->num_alloc_vfs) - return -EINVAL; + vsi = i40e_find_vsi_from_id(pf, vsi_id); + if (!i40e_vc_isvalid_vsi_id(vf, vsi_id) || !vsi) + return I40E_ERR_PARAM; - pf = vf->pf; - hw = &pf->hw; - abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id; + if (vf->port_vlan_id) { + aq_ret = i40e_set_vsi_promisc(vf, vsi->seid, allmulti, + alluni, &vf->port_vlan_id, 1); + return aq_ret; + } else if (i40e_getnum_vf_vsi_vlan_filters(vsi)) { + i40e_get_vlan_list_sync(vsi, &num_vlans, &vl); - /* single place to detect unsuccessful return values */ - if (v_retval) { - vf->num_invalid_msgs++; - dev_info(&pf->pdev->dev, "VF %d failed opcode %d, retval: %d\n", - vf->vf_id, v_opcode, v_retval); - if (vf->num_invalid_msgs > - I40E_DEFAULT_NUM_INVALID_MSGS_ALLOWED) { - dev_err(&pf->pdev->dev, - "Number of invalid messages exceeded for VF %d\n", - vf->vf_id); - dev_err(&pf->pdev->dev, "Use PF Control I/F to enable the VF\n"); - set_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); - } - } else { - vf->num_valid_msgs++; - /* reset the invalid counter, if a valid message is received. */ - vf->num_invalid_msgs = 0; - } + if (!vl) + return I40E_ERR_NO_MEMORY; - aq_ret = i40e_aq_send_msg_to_vf(hw, abs_vf_id, v_opcode, v_retval, - msg, msglen, NULL); - if (aq_ret) { - dev_info(&pf->pdev->dev, - "Unable to send the message to VF %d aq_err %d\n", - vf->vf_id, pf->hw.aq.asq_last_status); - return -EIO; + aq_ret = i40e_set_vsi_promisc(vf, vsi->seid, allmulti, alluni, + vl, num_vlans); + kfree(vl); + return aq_ret; } - - return 0; + /* no vlans to set on, set on vsi */ + aq_ret = i40e_set_vsi_promisc(vf, vsi->seid, allmulti, alluni, + NULL, 0); + return aq_ret; } /** - * i40e_vc_send_resp_to_vf - * @vf: pointer to the VF info - * @opcode: operation code - * @retval: return value + * i40e_trigger_vf_reset + * @vf: pointer to the VF structure + * @flr: VFLR was issued or not * - * send resp msg to VF + * Trigger hardware to start a reset for a particular VF. Expects the caller + * to wait the proper amount of time to allow hardware to reset the VF before + * it cleans up and restores VF functionality. **/ -static int i40e_vc_send_resp_to_vf(struct i40e_vf *vf, - enum virtchnl_ops opcode, - i40e_status retval) +static void i40e_trigger_vf_reset(struct i40e_vf *vf, bool flr) { - return i40e_vc_send_msg_to_vf(vf, opcode, retval, NULL, 0); + struct i40e_pf *pf = vf->pf; + struct i40e_hw *hw = &pf->hw; + u32 reg, reg_idx, bit_idx; + + /* warn the VF */ + clear_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states); + + /* Disable VF's configuration API during reset. The flag is re-enabled + * in i40e_alloc_vf_res(), when it's safe again to access VF's VSI. + * It's normally disabled in i40e_free_vf_res(), but it's safer + * to do it earlier to give some time to finish to any VF config + * functions that may still be running at this point. + */ + clear_bit(I40E_VF_STATE_INIT, &vf->vf_states); + + /* In the case of a VFLR, the HW has already reset the VF and we + * just need to clean up, so don't hit the VFRTRIG register. + */ + if (!flr) { + /* reset VF using VPGEN_VFRTRIG reg */ + reg = rd32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id)); + reg |= I40E_VPGEN_VFRTRIG_VFSWR_MASK; + wr32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id), reg); + } + /* clear the VFLR bit in GLGEN_VFLRSTAT */ + reg_idx = (hw->func_caps.vf_base_id + vf->vf_id) / 32; + bit_idx = (hw->func_caps.vf_base_id + vf->vf_id) % 32; + wr32(hw, I40E_GLGEN_VFLRSTAT(reg_idx), BIT(bit_idx)); + i40e_flush(hw); + + if (i40e_quiesce_vf_pci(vf)) + dev_err(&pf->pdev->dev, "VF %d PCI transactions stuck\n", + vf->vf_id); } /** - * i40e_vc_get_version_msg - * @vf: pointer to the VF info + * i40e_cleanup_reset_vf + * @vf: pointer to the VF structure * - * called from the VF to request the API version used by the PF + * Cleanup a VF after the hardware reset is finished. Expects the caller to + * have verified whether the reset is finished properly, and ensure the + * minimum amount of wait time has passed. **/ -static int i40e_vc_get_version_msg(struct i40e_vf *vf, u8 *msg) +static void i40e_cleanup_reset_vf(struct i40e_vf *vf) { - struct virtchnl_version_info info = { - VIRTCHNL_VERSION_MAJOR, VIRTCHNL_VERSION_MINOR - }; + struct i40e_pf *pf = vf->pf; + struct i40e_hw *hw = &pf->hw; + u32 reg; - vf->vf_ver = *(struct virtchnl_version_info *)msg; - /* VFs running the 1.0 API expect to get 1.0 back or they will cry. */ - if (VF_IS_V10(&vf->vf_ver)) - info.minor = VIRTCHNL_VERSION_MINOR_NO_VF_CAPS; - return i40e_vc_send_msg_to_vf(vf, VIRTCHNL_OP_VERSION, - I40E_SUCCESS, (u8 *)&info, - sizeof(struct virtchnl_version_info)); + /* disable promisc modes in case they were enabled */ + i40e_config_vf_promiscuous_mode(vf, vf->lan_vsi_id, false, false); + + /* free VF resources to begin resetting the VSI state */ + i40e_free_vf_res(vf); + + /* Enable hardware by clearing the reset bit in the VPGEN_VFRTRIG reg. + * By doing this we allow HW to access VF memory at any point. If we + * did it any sooner, HW could access memory while it was being freed + * in i40e_free_vf_res(), causing an IOMMU fault. + * + * On the other hand, this needs to be done ASAP, because the VF driver + * is waiting for this to happen and may report a timeout. It's + * harmless, but it gets logged into Guest OS kernel log, so best avoid + * it. + */ + reg = rd32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id)); + reg &= ~I40E_VPGEN_VFRTRIG_VFSWR_MASK; + wr32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id), reg); + + /* reallocate VF resources to finish resetting the VSI state */ + if (!i40e_alloc_vf_res(vf)) { + int abs_vf_id = vf->vf_id + (int)hw->func_caps.vf_base_id; + i40e_enable_vf_mappings(vf); + set_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states); + clear_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); + /* Do not notify the client during VF init */ + if (!test_and_clear_bit(I40E_VF_STATE_PRE_ENABLE, + &vf->vf_states)) + i40e_notify_client_of_vf_reset(pf, abs_vf_id); + vf->num_vlan = 0; + } + + /* Tell the VF driver the reset is done. This needs to be done only + * after VF has been fully initialized, because the VF driver may + * request resources immediately after setting this flag. + */ + wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_id), VIRTCHNL_VFR_VFACTIVE); } /** - * i40e_vc_get_vf_resources_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_reset_vf + * @vf: pointer to the VF structure + * @flr: VFLR was issued or not * - * called from the VF to request its resources + * Returns true if the VF is reset, false otherwise. **/ -static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) +bool i40e_reset_vf(struct i40e_vf *vf, bool flr) { - struct virtchnl_vf_resource *vfres = NULL; struct i40e_pf *pf = vf->pf; - i40e_status aq_ret = 0; - struct i40e_vsi *vsi; - int num_vsis = 1; - int len = 0; - int ret; - - if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { - aq_ret = I40E_ERR_PARAM; - goto err; - } - - len = (sizeof(struct virtchnl_vf_resource) + - sizeof(struct virtchnl_vsi_resource) * num_vsis); + struct i40e_hw *hw = &pf->hw; + bool rsd = false; + u32 reg; + int i; - vfres = kzalloc(len, GFP_KERNEL); - if (!vfres) { - aq_ret = I40E_ERR_NO_MEMORY; - len = 0; - goto err; - } - if (VF_IS_V11(&vf->vf_ver)) - vf->driver_caps = *(u32 *)msg; - else - vf->driver_caps = VIRTCHNL_VF_OFFLOAD_L2 | - VIRTCHNL_VF_OFFLOAD_RSS_REG | - VIRTCHNL_VF_OFFLOAD_VLAN; + /* If the VFs have been disabled, this means something else is + * resetting the VF, so we shouldn't continue. + */ + if (test_and_set_bit(__I40E_VF_DISABLE, pf->state)) + return false; - vfres->vf_cap_flags = VIRTCHNL_VF_OFFLOAD_L2; - vsi = pf->vsi[vf->lan_vsi_idx]; - if (!vsi->info.pvid) - vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_VLAN; + i40e_trigger_vf_reset(vf, flr); - if (i40e_vf_client_capable(pf, vf->vf_id) && - (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_IWARP)) { - vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_IWARP; - set_bit(I40E_VF_STATE_IWARPENA, &vf->vf_states); + /* poll VPGEN_VFRSTAT reg to make sure + * that reset is complete + */ + for (i = 0; i < 10; i++) { + /* VF reset requires driver to first reset the VF and then + * poll the status register to make sure that the reset + * completed successfully. Due to internal HW FIFO flushes, + * we must wait 10ms before the register will be valid. + */ + usleep_range(10000, 20000); + reg = rd32(hw, I40E_VPGEN_VFRSTAT(vf->vf_id)); + if (reg & I40E_VPGEN_VFRSTAT_VFRD_MASK) { + rsd = true; + break; + } } - if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PF) { - vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PF; - } else { - if ((pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) && - (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_AQ)) - vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_AQ; - else - vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_REG; - } + if (flr) + usleep_range(10000, 20000); - if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) { - if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2) - vfres->vf_cap_flags |= - VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2; - } + if (!rsd) + dev_err(&pf->pdev->dev, "VF reset check timeout on VF %d\n", + vf->vf_id); + usleep_range(10000, 20000); - if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP) - vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP; + /* On initial reset, we don't have any queues to disable */ + if (vf->lan_vsi_idx != 0) + i40e_vsi_stop_rings(pf->vsi[vf->lan_vsi_idx]); - if ((pf->hw_features & I40E_HW_OUTER_UDP_CSUM_CAPABLE) && - (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM)) - vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM; + i40e_cleanup_reset_vf(vf); - if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RX_POLLING) { - if (pf->flags & I40E_FLAG_MFP_ENABLED) { - dev_err(&pf->pdev->dev, - "VF %d requested polling mode: this feature is supported only when the device is running in single function per port (SFP) mode\n", - vf->vf_id); - aq_ret = I40E_ERR_PARAM; - goto err; - } - vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RX_POLLING; - } + i40e_flush(hw); + clear_bit(__I40E_VF_DISABLE, pf->state); - if (pf->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) { - if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_WB_ON_ITR) - vfres->vf_cap_flags |= - VIRTCHNL_VF_OFFLOAD_WB_ON_ITR; - } + return true; +} - vfres->num_vsis = num_vsis; +/** + * i40e_reset_all_vfs + * @pf: pointer to the PF structure + * @flr: VFLR was issued or not + * + * Reset all allocated VFs in one go. First, tell the hardware to reset each + * VF, then do all the waiting in one chunk, and finally finish restoring each + * VF after the wait. This is useful during PF routines which need to reset + * all VFs, as otherwise it must perform these resets in a serialized fashion. + * + * Returns true if any VFs were reset, and false otherwise. + **/ +bool i40e_reset_all_vfs(struct i40e_pf *pf, bool flr) +{ + struct i40e_hw *hw = &pf->hw; + struct i40e_vf *vf; + int i, v; + u32 reg; + + /* If we don't have any VFs, then there is nothing to reset */ + if (!pf->num_alloc_vfs) + return false; + + /* If VFs have been disabled, there is no need to reset */ + if (test_and_set_bit(__I40E_VF_DISABLE, pf->state)) + return false; + + /* Begin reset on all VFs at once */ + for (v = 0; v < pf->num_alloc_vfs; v++) + i40e_trigger_vf_reset(&pf->vf[v], flr); + + /* HW requires some time to make sure it can flush the FIFO for a VF + * when it resets it. Poll the VPGEN_VFRSTAT register for each VF in + * sequence to make sure that it has completed. We'll keep track of + * the VFs using a simple iterator that increments once that VF has + * finished resetting. + */ + for (i = 0, v = 0; i < 10 && v < pf->num_alloc_vfs; i++) { + usleep_range(10000, 20000); + + /* Check each VF in sequence, beginning with the VF to fail + * the previous check. + */ + while (v < pf->num_alloc_vfs) { + vf = &pf->vf[v]; + reg = rd32(hw, I40E_VPGEN_VFRSTAT(vf->vf_id)); + if (!(reg & I40E_VPGEN_VFRSTAT_VFRD_MASK)) + break; + + /* If the current VF has finished resetting, move on + * to the next VF in sequence. + */ + v++; + } + } + + if (flr) + usleep_range(10000, 20000); + + /* Display a warning if at least one VF didn't manage to reset in + * time, but continue on with the operation. + */ + if (v < pf->num_alloc_vfs) + dev_err(&pf->pdev->dev, "VF reset check timeout on VF %d\n", + pf->vf[v].vf_id); + usleep_range(10000, 20000); + + /* Begin disabling all the rings associated with VFs, but do not wait + * between each VF. + */ + for (v = 0; v < pf->num_alloc_vfs; v++) { + /* On initial reset, we don't have any queues to disable */ + if (pf->vf[v].lan_vsi_idx == 0) + continue; + + i40e_vsi_stop_rings_no_wait(pf->vsi[pf->vf[v].lan_vsi_idx]); + } + + /* Now that we've notified HW to disable all of the VF rings, wait + * until they finish. + */ + for (v = 0; v < pf->num_alloc_vfs; v++) { + /* On initial reset, we don't have any queues to disable */ + if (pf->vf[v].lan_vsi_idx == 0) + continue; + + i40e_vsi_wait_queues_disabled(pf->vsi[pf->vf[v].lan_vsi_idx]); + } + + /* Hw may need up to 50ms to finish disabling the RX queues. We + * minimize the wait by delaying only once for all VFs. + */ + mdelay(50); + + /* Finish the reset on each VF */ + for (v = 0; v < pf->num_alloc_vfs; v++) + i40e_cleanup_reset_vf(&pf->vf[v]); + + i40e_flush(hw); + clear_bit(__I40E_VF_DISABLE, pf->state); + + return true; +} + +/** + * i40e_free_vfs + * @pf: pointer to the PF structure + * + * free VF resources + **/ +void i40e_free_vfs(struct i40e_pf *pf) +{ + struct i40e_hw *hw = &pf->hw; + u32 reg_idx, bit_idx; + int i, tmp, vf_id; + struct i40e_vsi *src_vsi; + u16 rule_type, rule_id; + int ret; + + if (!pf->vf) + return; + + set_bit(__I40E_VFS_RELEASING, pf->state); + + while (test_and_set_bit(__I40E_VF_DISABLE, pf->state)) + usleep_range(1000, 2000); + + i40e_notify_client_of_vf_enable(pf, 0); + + /* At start we need to clear all ingress and egress mirroring setup. + * We can contiune when we remove all mirroring. + */ + for (i = 0; i < pf->num_alloc_vfs; i++) { + src_vsi = pf->vsi[pf->vf[i].lan_vsi_idx]; + if (I40E_IS_MIRROR_VLAN_ID_VALID(pf->vf[i].ingress_vlan)) { + rule_type = I40E_AQC_MIRROR_RULE_TYPE_VPORT_EGRESS; + rule_id = pf->vf[i].ingress_rule_id; + ret = i40e_del_ingress_egress_mirror(src_vsi, rule_type, rule_id); + if (ret) + dev_warn(&pf->pdev->dev, + "Error %s when tried to remove ingress mirror on VF %d", + i40e_aq_str + (hw, hw->aq.asq_last_status), + pf->vf[i].vf_id); + } + if (I40E_IS_MIRROR_VLAN_ID_VALID(pf->vf[i].egress_vlan)) { + rule_type = I40E_AQC_MIRROR_RULE_TYPE_VPORT_INGRESS; + rule_id = pf->vf[i].egress_rule_id; + ret = i40e_del_ingress_egress_mirror(src_vsi, rule_type, rule_id); + if (ret) + dev_warn(&pf->pdev->dev, + "Error %s when tried to remove egress mirror on VF %d", + i40e_aq_str + (hw, hw->aq.asq_last_status), + pf->vf[i].vf_id); + } + } + + /* Amortize wait time by stopping all VFs at the same time */ + for (i = 0; i < pf->num_alloc_vfs; i++) { + if (test_bit(I40E_VF_STATE_INIT, &pf->vf[i].vf_states)) + continue; + + i40e_vsi_stop_rings_no_wait(pf->vsi[pf->vf[i].lan_vsi_idx]); + } + + for (i = 0; i < pf->num_alloc_vfs; i++) { + if (test_bit(I40E_VF_STATE_INIT, &pf->vf[i].vf_states)) + continue; + + i40e_vsi_wait_queues_disabled(pf->vsi[pf->vf[i].lan_vsi_idx]); + } + + /* Disable IOV before freeing resources. This lets any VF drivers + * running in the host get themselves cleaned up before we yank + * the carpet out from underneath their feet. + */ + if (!pci_vfs_assigned(pf->pdev)) + pci_disable_sriov(pf->pdev); + else + dev_warn(&pf->pdev->dev, "VFs are assigned - not disabling SR-IOV\n"); + + /* free up VF resources */ + tmp = pf->num_alloc_vfs; + pf->num_alloc_vfs = 0; + for (i = 0; i < tmp; i++) { + if (test_bit(I40E_VF_STATE_INIT, &pf->vf[i].vf_states)) + i40e_free_vf_res(&pf->vf[i]); + /* disable qp mappings */ + i40e_disable_vf_mappings(&pf->vf[i]); + } +#ifdef HAVE_NDO_SET_VF_LINK_STATE + if (pf->vfd_obj) { + destroy_vfd_sysfs(pf->pdev, pf->vfd_obj); + pf->vfd_obj = NULL; + } +#endif /* HAVE_NDO_SET_VF_LINK_STATE */ + + kfree(pf->vf); + pf->vf = NULL; + + /* This check is for when the driver is unloaded while VFs are + * assigned. Setting the number of VFs to 0 through sysfs is caught + * before this function ever gets called. + */ + if (!pci_vfs_assigned(pf->pdev)) { + /* Acknowledge VFLR for all VFS. Without this, VFs will fail to + * work correctly when SR-IOV gets re-enabled. + */ + for (vf_id = 0; vf_id < tmp; vf_id++) { + reg_idx = (hw->func_caps.vf_base_id + vf_id) / 32; + bit_idx = (hw->func_caps.vf_base_id + vf_id) % 32; + wr32(hw, I40E_GLGEN_VFLRSTAT(reg_idx), BIT(bit_idx)); + } + } + clear_bit(__I40E_VF_DISABLE, pf->state); + clear_bit(__I40E_VFS_RELEASING, pf->state); +} + +#ifdef CONFIG_PCI_IOV +/** + * i40e_alloc_vfs + * @pf: pointer to the PF structure + * @num_alloc_vfs: number of VFs to allocate + * + * allocate VF resources + **/ +int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs) +{ + struct i40e_vf *vfs; + int i, ret = 0; + + /* Disable interrupt 0 so we don't try to handle the VFLR. */ + i40e_irq_dynamic_disable_icr0(pf); + + /* Check to see if we're just allocating resources for extant VFs */ + if (pci_num_vf(pf->pdev) != num_alloc_vfs) { + ret = pci_enable_sriov(pf->pdev, num_alloc_vfs); + if (ret) { + pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED; + pf->num_alloc_vfs = 0; + goto err_iov; + } + } + /* allocate memory */ + vfs = kcalloc(num_alloc_vfs, sizeof(struct i40e_vf), GFP_KERNEL); + if (!vfs) { + ret = -ENOMEM; + goto err_alloc; + } + pf->vf = vfs; + +#ifdef HAVE_NDO_SET_VF_LINK_STATE + /* set vfd ops */ + vfd_ops = &i40e_vfd_ops; + /* create the sriov kobjects */ + pf->vfd_obj = create_vfd_sysfs(pf->pdev, num_alloc_vfs); +#endif /* HAVE_NDO_SET_VF_LINK_STATE */ + + /* apply default profile */ + for (i = 0; i < num_alloc_vfs; i++) { + vfs[i].pf = pf; + vfs[i].parent_type = I40E_SWITCH_ELEMENT_TYPE_VEB; + vfs[i].vf_id = i; + +#ifdef HAVE_NDO_SET_VF_LINK_STATE + /* setup default mirror values */ + vfs[i].ingress_vlan = I40E_NO_VF_MIRROR; + vfs[i].egress_vlan = I40E_NO_VF_MIRROR; +#endif /* HAVE_NDO_SET_VF_LINK_STATE */ + /* assign default loopback value */ + vfs[i].loopback = true; + /* assign default mac anti spoof value for untrusted VF */ + vfs[i].mac_anti_spoof = true; + /* assign default allow_untagged value */ + vfs[i].allow_untagged = true; + /* assign default capabilities */ + set_bit(I40E_VIRTCHNL_VF_CAP_L2, &vfs[i].vf_caps); + set_bit(I40E_VF_STATE_PRE_ENABLE, &vfs[i].vf_states); + } + pf->num_alloc_vfs = num_alloc_vfs; + + /* VF resources get allocated during reset */ + i40e_reset_all_vfs(pf, false); + + i40e_notify_client_of_vf_enable(pf, num_alloc_vfs); + +err_alloc: + if (ret) + i40e_free_vfs(pf); +err_iov: + /* Re-enable interrupt 0. */ + i40e_irq_dynamic_enable_icr0(pf); + return ret; +} + +#endif +#if defined(HAVE_SRIOV_CONFIGURE) || defined(HAVE_RHEL6_SRIOV_CONFIGURE) +/** + * i40e_pci_sriov_enable + * @pdev: pointer to a pci_dev structure + * @num_vfs: number of VFs to allocate + * + * Enable or change the number of VFs + **/ +static int i40e_pci_sriov_enable(struct pci_dev *pdev, int num_vfs) +{ +#ifdef CONFIG_PCI_IOV + struct i40e_pf *pf = pci_get_drvdata(pdev); + int pre_existing_vfs = pci_num_vf(pdev); + int err = 0; + + if (test_bit(__I40E_TESTING, pf->state)) { + dev_warn(&pdev->dev, + "Cannot enable SR-IOV virtual functions while the device is undergoing diagnostic testing\n"); + err = -EPERM; + goto err_out; + } + + if (pre_existing_vfs && pre_existing_vfs != num_vfs) + i40e_free_vfs(pf); + else if (pre_existing_vfs && pre_existing_vfs == num_vfs) + goto out; + + if (num_vfs > pf->num_req_vfs) { + dev_warn(&pdev->dev, "Unable to enable %d VFs. Limited to %d VFs due to device resource constraints.\n", + num_vfs, pf->num_req_vfs); + err = -EPERM; + goto err_out; + } + + dev_info(&pdev->dev, "Allocating %d VFs.\n", num_vfs); + err = i40e_alloc_vfs(pf, num_vfs); + if (err) { + dev_warn(&pdev->dev, "Failed to enable SR-IOV: %d\n", err); + goto err_out; + } + +out: + return num_vfs; + +err_out: + return err; +#endif + return 0; +} + +/** + * i40e_pci_sriov_configure + * @pdev: pointer to a pci_dev structure + * @num_vfs: number of vfs to allocate + * + * Enable or change the number of VFs. Called when the user updates the number + * of VFs in sysfs. + **/ +int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + int ret = 0; + + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; + } + + if (num_vfs) { + if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) { + pf->flags |= I40E_FLAG_VEB_MODE_ENABLED; + i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG); + } + ret = i40e_pci_sriov_enable(pdev, num_vfs); + goto sriov_configure_out; + } + if (!pci_vfs_assigned(pdev)) { + i40e_free_vfs(pf); + pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED; + i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG); + } else { + dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n"); + ret = -EINVAL; + goto sriov_configure_out; + } +sriov_configure_out: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; +} +#endif + +/***********************virtual channel routines******************/ + +/** + * i40e_vc_send_msg_to_vf_ex + * @vf: pointer to the VF info + * @v_opcode: virtual channel opcode + * @v_retval: virtual channel return value + * @msg: pointer to the msg buffer + * @msglen: msg length + * @is_quiet: true for not printing unsuccessful return values, false otherwise + * + * send msg to VF + **/ +static int i40e_vc_send_msg_to_vf_ex(struct i40e_vf *vf, u32 v_opcode, + u32 v_retval, u8 *msg, u16 msglen, + bool is_quiet) +{ + struct i40e_pf *pf; + struct i40e_hw *hw; + int abs_vf_id; + i40e_status aq_ret; + + /* validate the request */ + if (!vf || vf->vf_id >= vf->pf->num_alloc_vfs) + return -EINVAL; + + pf = vf->pf; + hw = &pf->hw; + abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id; + + /* single place to detect unsuccessful return values */ + if (v_retval && !is_quiet) { + vf->num_invalid_msgs++; + dev_info(&pf->pdev->dev, "VF %d failed opcode %d, retval: %d\n", + vf->vf_id, v_opcode, v_retval); + if (vf->num_invalid_msgs > + I40E_DEFAULT_NUM_INVALID_MSGS_ALLOWED) { + dev_err(&pf->pdev->dev, + "Number of invalid messages exceeded for VF %d\n", + vf->vf_id); + dev_err(&pf->pdev->dev, "Use PF Control I/F to enable the VF\n"); + set_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); + } + } else { + vf->num_valid_msgs++; + /* reset the invalid counter, if a valid message is received. */ + vf->num_invalid_msgs = 0; + } + + aq_ret = i40e_aq_send_msg_to_vf(hw, abs_vf_id, v_opcode, v_retval, + msg, msglen, NULL); + if (aq_ret) { + dev_info(&pf->pdev->dev, + "Unable to send the message to VF %d aq_err %d\n", + vf->vf_id, pf->hw.aq.asq_last_status); + return -EIO; + } + + return 0; +} + +/** + * i40e_vc_send_msg_to_vf + * @vf: pointer to the VF info + * @v_opcode: virtual channel opcode + * @v_retval: virtual channel return value + * @msg: pointer to the msg buffer + * @msglen: msg length + * + * send msg to VF + **/ +static int i40e_vc_send_msg_to_vf(struct i40e_vf *vf, u32 v_opcode, + u32 v_retval, u8 *msg, u16 msglen) +{ + return i40e_vc_send_msg_to_vf_ex(vf, v_opcode, v_retval, + msg, msglen, false); +} + +/** + * i40e_vc_send_resp_to_vf + * @vf: pointer to the VF info + * @opcode: operation code + * @retval: return value + * + * send resp msg to VF + **/ +static int i40e_vc_send_resp_to_vf(struct i40e_vf *vf, + enum virtchnl_ops opcode, + i40e_status retval) +{ + return i40e_vc_send_msg_to_vf(vf, opcode, retval, NULL, 0); +} + +/** + * i40e_vc_get_version_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * called from the VF to request the API version used by the PF + **/ +static int i40e_vc_get_version_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_version_info info = { + VIRTCHNL_VERSION_MAJOR, VIRTCHNL_VERSION_MINOR + }; + + vf->vf_ver = *(struct virtchnl_version_info *)msg; + /* VFs running the 1.0 API expect to get 1.0 back or they will cry. */ + if (VF_IS_V10(&vf->vf_ver)) + info.minor = VIRTCHNL_VERSION_MINOR_NO_VF_CAPS; + return i40e_vc_send_msg_to_vf(vf, VIRTCHNL_OP_VERSION, + I40E_SUCCESS, (u8 *)&info, + sizeof(struct virtchnl_version_info)); +} + +#ifdef __TC_MQPRIO_MODE_MAX +/** + * i40e_del_qch - delete all the additional VSIs created as a part of ADq + * @vf: pointer to VF structure + **/ +static void i40e_del_qch(struct i40e_vf *vf) +{ + struct i40e_pf *pf = vf->pf; + int i; + + /* first element in the array belongs to primary VF VSI and we shouldn't + * delete it. We should however delete the rest of the VSIs created + */ + for (i = 1; i < vf->num_tc; i++) { + if (vf->ch[i].vsi_idx) { + i40e_vsi_release(pf->vsi[vf->ch[i].vsi_idx]); + vf->ch[i].vsi_idx = 0; + vf->ch[i].vsi_id = 0; + } + } +} + +#endif /* __TC_MQPRIO_MODE_MAX */ + +/** + * i40e_vc_get_vf_resources_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * called from the VF to request its resources + **/ +static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_vf_resource *vfres = NULL; + struct i40e_pf *pf = vf->pf; + i40e_status aq_ret = 0; + struct i40e_vsi *vsi; + int num_vsis = 1; + int len = 0; + int ret; + + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + len = (sizeof(struct virtchnl_vf_resource) + + sizeof(struct virtchnl_vsi_resource) * num_vsis); + + vfres = kzalloc(len, GFP_KERNEL); + if (!vfres) { + aq_ret = I40E_ERR_NO_MEMORY; + len = 0; + goto err; + } + if (VF_IS_V11(&vf->vf_ver)) + vf->driver_caps = *(u32 *)msg; + else + vf->driver_caps = VIRTCHNL_VF_OFFLOAD_L2 | + VIRTCHNL_VF_OFFLOAD_RSS_REG | + VIRTCHNL_VF_OFFLOAD_VLAN; + + vfres->vf_cap_flags = VIRTCHNL_VF_OFFLOAD_L2; +#ifdef VIRTCHNL_VF_CAP_ADV_LINK_SPEED + vfres->vf_cap_flags |= VIRTCHNL_VF_CAP_ADV_LINK_SPEED; +#endif /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + + vsi = pf->vsi[vf->lan_vsi_idx]; + if (!vsi->info.pvid) + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_VLAN; + + if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PF) { + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PF; + } else { + if ((pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) && + (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_AQ)) + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_AQ; + else + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_REG; + } + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) { + if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2) + vfres->vf_cap_flags |= + VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2; + } + + if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP) + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP; + + if ((pf->hw_features & I40E_HW_OUTER_UDP_CSUM_CAPABLE) && + (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM)) + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM; + + if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RX_POLLING) { + if (pf->flags & I40E_FLAG_MFP_ENABLED) { + dev_err(&pf->pdev->dev, + "VF %d requested polling mode: this feature is supported only when the device is running in single function per port (SFP) mode\n", + vf->vf_id); + aq_ret = I40E_ERR_PARAM; + goto err; + } + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RX_POLLING; + } + + if (pf->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) { + if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_WB_ON_ITR) + vfres->vf_cap_flags |= + VIRTCHNL_VF_OFFLOAD_WB_ON_ITR; + } + + if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_REQ_QUEUES) + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_REQ_QUEUES; + +#ifdef __TC_MQPRIO_MODE_MAX + if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ADQ) + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ADQ; +#endif /* __TC_MQPRIO_MODE_MAX */ + + vfres->num_vsis = num_vsis; vfres->num_queue_pairs = vf->num_queue_pairs; vfres->max_vectors = pf->hw.func_caps.num_msix_vectors_vf; vfres->rss_key_size = I40E_HKEY_ARRAY_SIZE; vfres->rss_lut_size = I40E_VF_HLUT_ARRAY_SIZE; - if (vf->lan_vsi_idx) { - vfres->vsi_res[0].vsi_id = vf->lan_vsi_id; - vfres->vsi_res[0].vsi_type = VIRTCHNL_VSI_SRIOV; - vfres->vsi_res[0].num_queue_pairs = vsi->alloc_queue_pairs; - /* VFs only use TC 0 */ - vfres->vsi_res[0].qset_handle - = le16_to_cpu(vsi->info.qs_handle[0]); - ether_addr_copy(vfres->vsi_res[0].default_mac_addr, - vf->default_lan_addr.addr); + if (vf->lan_vsi_idx) { + vfres->vsi_res[0].vsi_id = vf->lan_vsi_id; + vfres->vsi_res[0].vsi_type = VIRTCHNL_VSI_SRIOV; + vfres->vsi_res[0].num_queue_pairs = vsi->alloc_queue_pairs; + /* VFs only use TC 0 */ + vfres->vsi_res[0].qset_handle + = LE16_TO_CPU(vsi->info.qs_handle[0]); + ether_addr_copy(vfres->vsi_res[0].default_mac_addr, + vf->default_lan_addr.addr); + } + set_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states); + set_bit(I40E_VF_STATE_LOADED_VF_DRIVER, &vf->vf_states); + /* if vf is in base mode, keep only the base capabilities that are + * negotiated + */ + if (pf->vf_base_mode_only) + vfres->vf_cap_flags &= VF_BASE_MODE_OFFLOADS; +err: + /* send the response back to the VF */ + ret = i40e_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_VF_RESOURCES, + aq_ret, (u8 *)vfres, len); + + kfree(vfres); + return ret; +} + +/** + * i40e_getnum_vf_vsi_vlan_filters + * @vsi: pointer to the vsi + * + * called to get the number of VLANs offloaded on this VF + **/ +static inline int i40e_getnum_vf_vsi_vlan_filters(struct i40e_vsi *vsi) +{ + struct i40e_mac_filter *f; + int num_vlans = 0, bkt; + + hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { + if (f->vlan >= 0 && f->vlan <= I40E_MAX_VLANID) + num_vlans++; + } + + return num_vlans; +} + +/** + * i40e_get_vlan_list_sync + * @vsi: pointer to the vsi + * @num_vlans: number of vlans present in mac_filter_hash, returned to caller + * @vlan_list: list of vlans present in mac_filter_hash, returned to caller. + * This array is allocated here, but has to be freed in caller. + * + * Called to get number of vlans and vlan list present in mac_filter_hash. + **/ + +static inline void i40e_get_vlan_list_sync(struct i40e_vsi *vsi, int *num_vlans, + s16 **vlan_list) +{ + struct i40e_mac_filter *f; + int bkt; + int i; + + spin_lock_bh(&vsi->mac_filter_hash_lock); + *num_vlans = i40e_getnum_vf_vsi_vlan_filters(vsi); + *vlan_list = kcalloc(*num_vlans, sizeof(**vlan_list), + GFP_ATOMIC); + if (!(*vlan_list)) + goto err; + + i = 0; + hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { + if (f->vlan < 0 || f->vlan > I40E_MAX_VLANID) + continue; + (*vlan_list)[i++] = f->vlan; + } +err: + spin_unlock_bh(&vsi->mac_filter_hash_lock); +} + +/** + * i40e_set_vsi_promisc + * @vf: pointer to the vf struct + * @seid: vsi number + * @multi_enable: set MAC L2 layer multicast promiscuous enable/disable + * for a given VLAN + * @unicast_enable: set MAC L2 layer unicast promiscuous enable/disable + * for a given VLAN + * @vl: List of vlans - apply filter for given vlans + * @num_vlans: Number of elements in @vl + **/ +static inline i40e_status +i40e_set_vsi_promisc(struct i40e_vf *vf, u16 seid, bool multi_enable, + bool unicast_enable, s16 *vl, int num_vlans) +{ + struct i40e_pf *pf = vf->pf; + struct i40e_hw *hw = &pf->hw; + i40e_status aq_ret = 0; + int i; + + /* No vlan to set promisc on, set on vsi */ + if (!num_vlans || !vl) { + aq_ret = i40e_aq_set_vsi_multicast_promiscuous(hw, seid, + multi_enable, + NULL); + if (aq_ret) { + int aq_err = pf->hw.aq.asq_last_status; + + dev_err(&pf->pdev->dev, + "VF %d failed to set multicast promiscuous mode err %s aq_err %s\n", + vf->vf_id, + i40e_stat_str(&pf->hw, aq_ret), + i40e_aq_str(&pf->hw, aq_err)); + + return aq_ret; + } + + aq_ret = i40e_aq_set_vsi_unicast_promiscuous(hw, seid, + unicast_enable, + NULL, true); + + if (aq_ret) { + int aq_err = pf->hw.aq.asq_last_status; + + dev_err(&pf->pdev->dev, + "VF %d failed to set unicast promiscuous mode err %s aq_err %s\n", + vf->vf_id, + i40e_stat_str(&pf->hw, aq_ret), + i40e_aq_str(&pf->hw, aq_err)); + } + + return aq_ret; + } + + for (i = 0; i < num_vlans; i++) { + aq_ret = i40e_aq_set_vsi_mc_promisc_on_vlan(hw, seid, + multi_enable, + vl[i], NULL); + if (aq_ret) { + int aq_err = pf->hw.aq.asq_last_status; + + dev_err(&pf->pdev->dev, + "VF %d failed to set multicast promiscuous mode err %s aq_err %s\n", + vf->vf_id, + i40e_stat_str(&pf->hw, aq_ret), + i40e_aq_str(&pf->hw, aq_err)); + } + + aq_ret = i40e_aq_set_vsi_uc_promisc_on_vlan(hw, seid, + unicast_enable, + vl[i], NULL); + if (aq_ret) { + int aq_err = pf->hw.aq.asq_last_status; + + dev_err(&pf->pdev->dev, + "VF %d failed to set unicast promiscuous mode err %s aq_err %s\n", + vf->vf_id, + i40e_stat_str(&pf->hw, aq_ret), + i40e_aq_str(&pf->hw, aq_err)); + } + } + return aq_ret; +} + +/** + * i40e_vc_config_promiscuous_mode_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * called from the VF to configure the promiscuous mode of + * VF vsis + **/ +static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_promisc_info *info = + (struct virtchnl_promisc_info *)msg; + i40e_status aq_ret = I40E_SUCCESS; + struct i40e_pf *pf = vf->pf; + bool allmulti = false; + bool alluni = false; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err_out; + } + if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) { + dev_err(&pf->pdev->dev, + "Unprivileged VF %d is attempting to configure promiscuous mode\n", + vf->vf_id); + if (pf->vf_base_mode_only) + dev_err(&pf->pdev->dev, "VF %d is in base mode only, promiscuous mode is not be supported\n", + vf->vf_id); + + /* Lie to the VF on purpose, because this is an error we can + * ignore. Unprivileged VF is not a virtual channel error. + */ + aq_ret = I40E_SUCCESS; + goto err_out; + } + + if (info->flags > I40E_MAX_VF_PROMISC_FLAGS) { + aq_ret = I40E_ERR_PARAM; + goto err_out; + } + + if (!i40e_vc_isvalid_vsi_id(vf, info->vsi_id)) { + aq_ret = I40E_ERR_PARAM; + goto err_out; + } + + /* Multicast promiscuous handling*/ + if (info->flags & FLAG_VF_MULTICAST_PROMISC) + allmulti = true; + + if (info->flags & FLAG_VF_UNICAST_PROMISC) + alluni = true; + + aq_ret = i40e_config_vf_promiscuous_mode(vf, info->vsi_id, allmulti, + alluni); + if (aq_ret) + goto err_out; + + if (allmulti) { + if (!test_and_set_bit(I40E_VF_STATE_MC_PROMISC, + &vf->vf_states)) + dev_info(&pf->pdev->dev, + "VF %d successfully set multicast promiscuous mode\n", + vf->vf_id); + } else if (test_and_clear_bit(I40E_VF_STATE_MC_PROMISC, + &vf->vf_states)) + dev_info(&pf->pdev->dev, + "VF %d successfully unset multicast promiscuous mode\n", + vf->vf_id); + + if (alluni) { + if (!test_and_set_bit(I40E_VF_STATE_UC_PROMISC, + &vf->vf_states)) + dev_info(&pf->pdev->dev, + "VF %d successfully set unicast promiscuous mode\n", + vf->vf_id); + } else if (test_and_clear_bit(I40E_VF_STATE_UC_PROMISC, + &vf->vf_states)) + dev_info(&pf->pdev->dev, + "VF %d successfully unset unicast promiscuous mode\n", + vf->vf_id); + +err_out: + /* send the response to the VF */ + return i40e_vc_send_resp_to_vf(vf, + VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE, + aq_ret); +} + +/** + * i40e_vc_config_queues_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * called from the VF to configure the rx/tx + * queues + **/ +static int i40e_vc_config_queues_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_vsi_queue_config_info *qci = + (struct virtchnl_vsi_queue_config_info *)msg; + struct virtchnl_queue_pair_info *qpi; + struct i40e_pf *pf = vf->pf; + u16 vsi_id, vsi_queue_id = 0; + u16 num_qps_all = 0; + i40e_status aq_ret = 0; + int i, j = 0, idx = 0; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + if (!i40e_vc_isvalid_vsi_id(vf, qci->vsi_id)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + if (qci->num_queue_pairs > I40E_MAX_VF_QUEUES) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + if (vf->adq_enabled) { + for (i = 0; i < I40E_MAX_VF_VSI; i++) + num_qps_all += vf->ch[i].num_qps; + if (num_qps_all != qci->num_queue_pairs) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + } + + vsi_id = qci->vsi_id; + + for (i = 0; i < qci->num_queue_pairs; i++) { + qpi = &qci->qpair[i]; + + if (!vf->adq_enabled) { + if (!i40e_vc_isvalid_queue_id(vf, vsi_id, + qpi->txq.queue_id)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + vsi_queue_id = qpi->txq.queue_id; + + if (qpi->txq.vsi_id != qci->vsi_id || + qpi->rxq.vsi_id != qci->vsi_id || + qpi->rxq.queue_id != vsi_queue_id) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + } + + if (vf->adq_enabled) { + if (idx >= ARRAY_SIZE(vf->ch)) { + aq_ret = I40E_ERR_NO_AVAILABLE_VSI; + goto error_param; + } + vsi_id = vf->ch[idx].vsi_id; + } + + if (i40e_config_vsi_rx_queue(vf, vsi_id, vsi_queue_id, + &qpi->rxq) || + i40e_config_vsi_tx_queue(vf, vsi_id, vsi_queue_id, + &qpi->txq)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + /* For ADq there can be up to 4 VSIs with max 4 queues each. + * VF does not know about these additional VSIs and all + * it cares is about its own queues. PF configures these queues + * to its appropriate VSIs based on TC mapping + */ + if (vf->adq_enabled) { + if (idx >= ARRAY_SIZE(vf->ch)) { + aq_ret = I40E_ERR_NO_AVAILABLE_VSI; + goto error_param; + } + if (j == (vf->ch[idx].num_qps - 1)) { + idx++; + j = 0; /* resetting the queue count */ + vsi_queue_id = 0; + } else { + j++; + vsi_queue_id++; + } + } + } + + /* set vsi num_queue_pairs in use to num configured by VF */ + if (!vf->adq_enabled) { + pf->vsi[vf->lan_vsi_idx]->num_queue_pairs = + qci->num_queue_pairs; + } else { + for (i = 0; i < vf->num_tc; i++) + pf->vsi[vf->ch[i].vsi_idx]->num_queue_pairs = + vf->ch[i].num_qps; + } + +error_param: + /* send the response to the VF */ + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_CONFIG_VSI_QUEUES, + aq_ret); +} + +/** + * i40e_validate_queue_map + * @vf: pointer to the VF info + * @vsi_id: vsi id + * @queuemap: Tx or Rx queue map + * + * check if Tx or Rx queue map is valid + **/ +static int i40e_validate_queue_map(struct i40e_vf *vf, u16 vsi_id, + unsigned long queuemap) +{ + u16 vsi_queue_id, queue_id; + + for_each_set_bit(vsi_queue_id, &queuemap, I40E_MAX_VSI_QP) { + if (vf->adq_enabled) { + vsi_id = vf->ch[vsi_queue_id / I40E_MAX_VF_VSI].vsi_id; + queue_id = (vsi_queue_id % I40E_DEFAULT_QUEUES_PER_VF); + } else { + queue_id = vsi_queue_id; + } + + if (!i40e_vc_isvalid_queue_id(vf, vsi_id, queue_id)) + return -EINVAL; + } + + return 0; +} + +/** + * i40e_vc_config_irq_map_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * called from the VF to configure the irq to + * queue map + **/ +static int i40e_vc_config_irq_map_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_irq_map_info *irqmap_info = + (struct virtchnl_irq_map_info *)msg; + struct virtchnl_vector_map *map; + u16 vsi_id; + i40e_status aq_ret = 0; + int i; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + if (irqmap_info->num_vectors > + vf->pf->hw.func_caps.num_msix_vectors_vf) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + for (i = 0; i < irqmap_info->num_vectors; i++) { + map = &irqmap_info->vecmap[i]; + /* validate msg params */ + if (!i40e_vc_isvalid_vector_id(vf, map->vector_id) || + !i40e_vc_isvalid_vsi_id(vf, map->vsi_id)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + vsi_id = map->vsi_id; + + if (i40e_validate_queue_map(vf, vsi_id, map->rxq_map)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + if (i40e_validate_queue_map(vf, vsi_id, map->txq_map)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + i40e_config_irq_link_list(vf, vsi_id, map); + } +error_param: + /* send the response to the VF */ + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_CONFIG_IRQ_MAP, + aq_ret); +} + +/** + * i40e_ctrl_vf_tx_rings + * @vsi: the SRIOV VSI being configured + * @q_map: bit map of the queues to be enabled + * @enable: start or stop the queue + **/ +static int i40e_ctrl_vf_tx_rings(struct i40e_vsi *vsi, unsigned long q_map, + bool enable) +{ + struct i40e_pf *pf = vsi->back; + int ret = 0; + u16 q_id; + + for_each_set_bit(q_id, &q_map, I40E_MAX_VF_QUEUES) { + ret = i40e_control_wait_tx_q(vsi->seid, pf, + vsi->base_queue + q_id, + false /*is xdp*/, enable); + if (ret) + break; + } + return ret; +} + +/** + * i40e_ctrl_vf_rx_rings + * @vsi: the SRIOV VSI being configured + * @q_map: bit map of the queues to be enabled + * @enable: start or stop the queue + **/ +static int i40e_ctrl_vf_rx_rings(struct i40e_vsi *vsi, unsigned long q_map, + bool enable) +{ + struct i40e_pf *pf = vsi->back; + int ret = 0; + u16 q_id; + + for_each_set_bit(q_id, &q_map, I40E_MAX_VF_QUEUES) { + ret = i40e_control_wait_rx_q(pf, vsi->base_queue + q_id, + enable); + if (ret) + break; + } + return ret; +} + +/** + * i40e_vc_enable_queues_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * called from the VF to enable all or specific queue(s) + **/ +static int i40e_vc_enable_queues_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_queue_select *vqs = + (struct virtchnl_queue_select *)msg; + struct i40e_pf *pf = vf->pf; + i40e_status aq_ret = 0; + int i; + + if (vf->pf_ctrl_disable) { + aq_ret = I40E_ERR_PARAM; + dev_err(&pf->pdev->dev, + "Admin has disabled VF %d via sysfs, will not enable queues", + vf->vf_id); + goto error_param; + } + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + if (!i40e_vc_isvalid_vsi_id(vf, vqs->vsi_id)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + if ((0 == vqs->rx_queues) && (0 == vqs->tx_queues)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + /* Use the queue bit map sent by the VF */ + if (i40e_ctrl_vf_rx_rings(pf->vsi[vf->lan_vsi_idx], vqs->rx_queues, + true)) { + aq_ret = I40E_ERR_TIMEOUT; + goto error_param; + } + if (i40e_ctrl_vf_tx_rings(pf->vsi[vf->lan_vsi_idx], vqs->tx_queues, + true)) { + aq_ret = I40E_ERR_TIMEOUT; + goto error_param; + } + + /* need to start the rings for additional ADq VSI's as well */ + if (vf->adq_enabled) { + /* zero belongs to LAN VSI */ + for (i = 1; i < vf->num_tc; i++) { + if (i40e_ctrl_vf_rx_rings(pf->vsi[vf->ch[i].vsi_idx], + vqs->rx_queues, true)) { + aq_ret = I40E_ERR_TIMEOUT; + goto error_param; + } + if (i40e_ctrl_vf_tx_rings(pf->vsi[vf->ch[i].vsi_idx], + vqs->tx_queues, true)) { + aq_ret = I40E_ERR_TIMEOUT; + goto error_param; + } + } + } + + vf->queues_enabled = true; + +error_param: + /* send the response to the VF */ + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ENABLE_QUEUES, + aq_ret); +} + +/** + * i40e_vc_disable_queues_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * called from the VF to disable all or specific + * queue(s) + **/ +static int i40e_vc_disable_queues_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_queue_select *vqs = + (struct virtchnl_queue_select *)msg; + struct i40e_pf *pf = vf->pf; + i40e_status aq_ret = 0; + + /* Immediately mark queues as disabled */ + vf->queues_enabled = false; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + if (!i40e_vc_isvalid_vsi_id(vf, vqs->vsi_id)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + if ((vqs->rx_queues == 0 && vqs->tx_queues == 0) || + vqs->rx_queues > I40E_MAX_VF_QUEUES || + vqs->tx_queues > I40E_MAX_VF_QUEUES) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + /* Use the queue bit map sent by the VF */ + if (i40e_ctrl_vf_tx_rings(pf->vsi[vf->lan_vsi_idx], vqs->tx_queues, + false)) { + aq_ret = I40E_ERR_TIMEOUT; + goto error_param; + } + if (i40e_ctrl_vf_rx_rings(pf->vsi[vf->lan_vsi_idx], vqs->rx_queues, + false)) { + aq_ret = I40E_ERR_TIMEOUT; + goto error_param; + } +error_param: + /* send the response to the VF */ + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DISABLE_QUEUES, + aq_ret); +} + +/** + * i40e_vc_request_queues_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * VFs get a default number of queues but can use this message to request a + * different number. If the request is successful, PF will reset the VF and + * return 0. If unsuccessful, PF will send message informing VF of number of + * available queues and return result of sending VF a message. + **/ +static int i40e_vc_request_queues_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_vf_res_request *vfres = + (struct virtchnl_vf_res_request *)msg; + u16 req_pairs = vfres->num_queue_pairs; + u8 cur_pairs = vf->num_queue_pairs; + struct i40e_pf *pf = vf->pf; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) + return -EINVAL; + + if (req_pairs > I40E_MAX_VF_QUEUES) { + dev_err(&pf->pdev->dev, + "VF %d tried to request more than %d queues.\n", + vf->vf_id, + I40E_MAX_VF_QUEUES); + vfres->num_queue_pairs = I40E_MAX_VF_QUEUES; + } else if (req_pairs - cur_pairs > pf->queues_left) { + dev_warn(&pf->pdev->dev, + "VF %d requested %d more queues, but only %d left.\n", + vf->vf_id, + req_pairs - cur_pairs, + pf->queues_left); + vfres->num_queue_pairs = pf->queues_left + cur_pairs; + } else { + /* successful request */ + vf->num_req_queues = req_pairs; + i40e_vc_reset_vf(vf, true); + return 0; + } + + return i40e_vc_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0, + (u8 *)vfres, sizeof(*vfres)); +} + +/** + * i40e_vc_get_stats_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * called from the VF to get vsi stats + **/ +static int i40e_vc_get_stats_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_queue_select *vqs = + (struct virtchnl_queue_select *)msg; + struct i40e_pf *pf = vf->pf; + struct i40e_eth_stats stats; + i40e_status aq_ret = 0; + struct i40e_vsi *vsi; + + memset(&stats, 0, sizeof(struct i40e_eth_stats)); + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + if (!i40e_vc_isvalid_vsi_id(vf, vqs->vsi_id)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + vsi = pf->vsi[vf->lan_vsi_idx]; + if (!vsi) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + i40e_update_eth_stats(vsi); + stats = vsi->eth_stats; + +error_param: + /* send the response back to the VF */ + return i40e_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS, aq_ret, + (u8 *)&stats, sizeof(stats)); +} + +/* If the VF is not trusted restrict the number of MAC/VLAN it can program + * MAC filters: 16 for multicast, 1 for MAC, 1 for broadcast + */ +#define I40E_VC_MAX_MAC_ADDR_PER_VF (16 + 1 + 1) +#define I40E_VC_MAX_VLAN_PER_VF 16 + +/** + * i40e_check_vf_permission + * @vf: pointer to the VF info + * @al: MAC address list from virtchnl + * @is_quiet: set true for printing msg without opcode info, false otherwise + * + * Check that the given list of MAC addresses is allowed. Will return -EPERM + * if any address in the list is not valid. Checks the following conditions: + * + * 1) broadcast and zero addresses are never valid + * 2) unicast addresses are not allowed if the VMM has administratively set + * the VF MAC address, unless the VF is marked as privileged. + * 3) There is enough space to add all the addresses. + * + * Note that to guarantee consistency, it is expected this function be called + * while holding the mac_filter_hash_lock, as otherwise the current number of + * addresses might not be accurate. + **/ +static inline int i40e_check_vf_permission(struct i40e_vf *vf, + struct virtchnl_ether_addr_list *al, + bool *is_quiet) +{ + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = pf->vsi[vf->lan_vsi_idx]; + int mac2add_cnt = 0; + int i; + + if (!is_quiet) + return -EINVAL; + + *is_quiet = false; + for (i = 0; i < al->num_elements; i++) { + struct i40e_mac_filter *f; + u8 *addr = al->list[i].addr; + + if (is_broadcast_ether_addr(addr) || + is_zero_ether_addr(addr)) { + dev_err(&pf->pdev->dev, "invalid VF MAC addr %pM\n", addr); + return I40E_ERR_INVALID_MAC_ADDR; + } + + /* If the host VMM administrator has set the VF MAC address + * administratively via the ndo_set_vf_mac command then deny + * permission to the VF to add or delete unicast MAC addresses. + * Unless the VF is privileged and then it can do whatever. + * The VF may request to set the MAC address filter already + * assigned to it so do not return an error in that case. + */ + if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) && + !is_multicast_ether_addr(addr) && vf->pf_set_mac && + !ether_addr_equal(addr, vf->default_lan_addr.addr)) { + dev_err(&pf->pdev->dev, + "VF attempting to override administratively set MAC address, bring down and up the VF interface to resume normal operation\n"); + *is_quiet = true; + return -EPERM; + } + + /* count filters that really will be added */ + f = i40e_find_mac(vsi, addr); + if (!f) + ++mac2add_cnt; + } + + /* If this VF is not privileged, then we can't add more than a limited + * number of addresses. Check to make sure that the additions do not + * push us over the limit. + */ + if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) && + (i40e_count_filters(vsi) + mac2add_cnt) > + I40E_VC_MAX_MAC_ADDR_PER_VF) { + dev_err(&pf->pdev->dev, + "Cannot add more MAC addresses, VF is not trusted, switch the VF to trusted to add more functionality\n"); + if (pf->vf_base_mode_only) + dev_err(&pf->pdev->dev, "VF %d is in base mode only, cannot add more than %d filters\n", + vf->vf_id, I40E_VC_MAX_MAC_ADDR_PER_VF); + return -EPERM; + } + return 0; +} + +/** + * i40e_vc_add_mac_addr_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * add guest mac address filter + **/ +static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_ether_addr_list *al = + (struct virtchnl_ether_addr_list *)msg; + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = NULL; + bool is_quiet = false; + i40e_status ret = 0; + int i; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || + !i40e_vc_isvalid_vsi_id(vf, al->vsi_id)) { + ret = I40E_ERR_PARAM; + goto error_param; + } + + vsi = pf->vsi[vf->lan_vsi_idx]; + + /* Lock once, because all function inside for loop accesses VSI's + * MAC filter list which needs to be protected using same lock. + */ + spin_lock_bh(&vsi->mac_filter_hash_lock); + + ret = i40e_check_vf_permission(vf, al, &is_quiet); + if (ret) { + spin_unlock_bh(&vsi->mac_filter_hash_lock); + goto error_param; + } + + /* add new addresses to the list */ + for (i = 0; i < al->num_elements; i++) { + struct i40e_mac_filter *f; + + f = i40e_find_mac(vsi, al->list[i].addr); + if (!f) { + f = i40e_add_mac_filter(vsi, al->list[i].addr); + if (!f) { + dev_err(&pf->pdev->dev, + "Unable to add MAC filter %pM for VF %d\n", + al->list[i].addr, vf->vf_id); + ret = I40E_ERR_PARAM; + spin_unlock_bh(&vsi->mac_filter_hash_lock); + goto error_param; + } + if (is_valid_ether_addr(al->list[i].addr)) + ether_addr_copy(vf->default_lan_addr.addr, + al->list[i].addr); + } + } + spin_unlock_bh(&vsi->mac_filter_hash_lock); + + /* program the updated filter list */ + ret = i40e_sync_vsi_filters(vsi); + if (ret) + dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters, error %d\n", + vf->vf_id, ret); + +error_param: + /* send the response to the VF */ + return i40e_vc_send_msg_to_vf_ex(vf, VIRTCHNL_OP_ADD_ETH_ADDR, + ret, NULL, 0, is_quiet); +} + +/** + * i40e_vc_del_mac_addr_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * remove guest mac address filter + **/ +static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_ether_addr_list *al = + (struct virtchnl_ether_addr_list *)msg; + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = NULL; + i40e_status ret = 0; + int i; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || + !i40e_vc_isvalid_vsi_id(vf, al->vsi_id)) { + ret = I40E_ERR_PARAM; + goto error_param; + } + + for (i = 0; i < al->num_elements; i++) { + if (is_broadcast_ether_addr(al->list[i].addr) || + is_zero_ether_addr(al->list[i].addr)) { + dev_err(&pf->pdev->dev, "Invalid MAC addr %pM for VF %d\n", + al->list[i].addr, vf->vf_id); + ret = I40E_ERR_INVALID_MAC_ADDR; + goto error_param; + } + } + vsi = pf->vsi[vf->lan_vsi_idx]; + + spin_lock_bh(&vsi->mac_filter_hash_lock); + /* delete addresses from the list */ + for (i = 0; i < al->num_elements; i++) + if (i40e_del_mac_filter(vsi, al->list[i].addr)) { + ret = I40E_ERR_INVALID_MAC_ADDR; + spin_unlock_bh(&vsi->mac_filter_hash_lock); + goto error_param; + } + + spin_unlock_bh(&vsi->mac_filter_hash_lock); + + /* program the updated filter list */ + ret = i40e_sync_vsi_filters(vsi); + if (ret) + dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters, error %d\n", + vf->vf_id, ret); + +error_param: + /* send the response to the VF */ + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DEL_ETH_ADDR, + ret); +} + +/** + * i40e_vc_add_vlan_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * program guest vlan id + **/ +static int i40e_vc_add_vlan_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_vlan_filter_list *vfl = + (struct virtchnl_vlan_filter_list *)msg; + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = NULL; + i40e_status aq_ret = 0; + int i; + + if ((vf->num_vlan >= I40E_VC_MAX_VLAN_PER_VF) && + !test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) { + dev_err(&pf->pdev->dev, + "VF is not trusted, switch the VF to trusted to add more VLAN addresses\n"); + if (pf->vf_base_mode_only) + dev_err(&pf->pdev->dev, "VF %d is in base mode only, cannot add more than %d vlans\n", + vf->vf_id, I40E_VC_MAX_VLAN_PER_VF); + goto error_param; + } + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || + !i40e_vc_isvalid_vsi_id(vf, vfl->vsi_id)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) && + bitmap_weight(vf->trunk_vlans, VLAN_N_VID)) + /* Silently fail the request if the VF is untrusted and trunk + * VLANs are configured. + */ + goto error_param; + + for (i = 0; i < vfl->num_elements; i++) { + if (vfl->vlan_id[i] > I40E_MAX_VLANID) { + aq_ret = I40E_ERR_PARAM; + dev_err(&pf->pdev->dev, + "invalid VF VLAN id %d\n", vfl->vlan_id[i]); + goto error_param; + } + } + vsi = pf->vsi[vf->lan_vsi_idx]; + if (vsi->info.pvid) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + i40e_vlan_stripping_enable(vsi); + for (i = 0; i < vfl->num_elements; i++) { + /* add new VLAN filter */ + int ret = i40e_vsi_add_vlan(vsi, vfl->vlan_id[i]); + if (!ret) + vf->num_vlan++; + + if (test_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states)) + i40e_aq_set_vsi_uc_promisc_on_vlan(&pf->hw, vsi->seid, + true, + vfl->vlan_id[i], + NULL); + if (test_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states)) + i40e_aq_set_vsi_mc_promisc_on_vlan(&pf->hw, vsi->seid, + true, + vfl->vlan_id[i], + NULL); + + if (ret) + dev_err(&pf->pdev->dev, + "Unable to add VLAN filter %d for VF %d, error %d\n", + vfl->vlan_id[i], vf->vf_id, ret); + } + +error_param: + /* send the response to the VF */ + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ADD_VLAN, aq_ret); +} + +/** + * i40e_vc_remove_vlan_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * remove programmed guest vlan id + **/ +static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_vlan_filter_list *vfl = + (struct virtchnl_vlan_filter_list *)msg; + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = NULL; + i40e_status aq_ret = 0; + int i; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || + !i40e_vc_isvalid_vsi_id(vf, vfl->vsi_id)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) && + bitmap_weight(vf->trunk_vlans, VLAN_N_VID)) + /* Silently fail the request if the VF is untrusted and trunk + * VLANs are configured. + */ + goto error_param; + + for (i = 0; i < vfl->num_elements; i++) { + if (vfl->vlan_id[i] > I40E_MAX_VLANID) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + } + + vsi = pf->vsi[vf->lan_vsi_idx]; + if (vsi->info.pvid) { + if (vfl->num_elements > 1 || vfl->vlan_id[0]) + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + + for (i = 0; i < vfl->num_elements; i++) { + i40e_vsi_kill_vlan(vsi, vfl->vlan_id[i]); + vf->num_vlan--; + + if (test_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states)) + i40e_aq_set_vsi_uc_promisc_on_vlan(&pf->hw, vsi->seid, + false, + vfl->vlan_id[i], + NULL); + if (test_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states)) + i40e_aq_set_vsi_mc_promisc_on_vlan(&pf->hw, vsi->seid, + false, + vfl->vlan_id[i], + NULL); + } + +error_param: + /* send the response to the VF */ + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DEL_VLAN, aq_ret); +} + +/** + * i40e_vc_config_rss_key + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * Configure the VF's RSS key + **/ +static int i40e_vc_config_rss_key(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_rss_key *vrk = + (struct virtchnl_rss_key *)msg; + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = NULL; + i40e_status aq_ret = 0; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || + !i40e_vc_isvalid_vsi_id(vf, vrk->vsi_id) || + (vrk->key_len != I40E_HKEY_ARRAY_SIZE)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + vsi = pf->vsi[vf->lan_vsi_idx]; + aq_ret = i40e_config_rss(vsi, vrk->key, NULL, 0); +err: + /* send the response to the VF */ + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_CONFIG_RSS_KEY, + aq_ret); +} + +/** + * i40e_vc_config_rss_lut + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * Configure the VF's RSS LUT + **/ +static int i40e_vc_config_rss_lut(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_rss_lut *vrl = + (struct virtchnl_rss_lut *)msg; + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = NULL; + i40e_status aq_ret = 0; + u16 i; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || + !i40e_vc_isvalid_vsi_id(vf, vrl->vsi_id) || + (vrl->lut_entries != I40E_VF_HLUT_ARRAY_SIZE)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + for (i = 0; i < vrl->lut_entries; i++) + if (vrl->lut[i] >= vf->num_queue_pairs) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + vsi = pf->vsi[vf->lan_vsi_idx]; + aq_ret = i40e_config_rss(vsi, NULL, vrl->lut, I40E_VF_HLUT_ARRAY_SIZE); + /* send the response to the VF */ +err: + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_CONFIG_RSS_LUT, + aq_ret); +} + +/** + * i40e_vc_get_rss_hena + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * Return the RSS HENA bits allowed by the hardware + **/ +static int i40e_vc_get_rss_hena(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_rss_hena *vrh = NULL; + struct i40e_pf *pf = vf->pf; + i40e_status aq_ret = 0; + int len = 0; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + len = sizeof(struct virtchnl_rss_hena); + + vrh = kzalloc(len, GFP_KERNEL); + if (!vrh) { + aq_ret = I40E_ERR_NO_MEMORY; + len = 0; + goto err; + } + vrh->hena = i40e_pf_get_default_rss_hena(pf); +err: + /* send the response back to the VF */ + aq_ret = i40e_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_RSS_HENA_CAPS, + aq_ret, (u8 *)vrh, len); + kfree(vrh); + return aq_ret; +} + +/** + * i40e_vc_set_rss_hena + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * Set the RSS HENA bits for the VF + **/ +static int i40e_vc_set_rss_hena(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_rss_hena *vrh = + (struct virtchnl_rss_hena *)msg; + struct i40e_pf *pf = vf->pf; + struct i40e_hw *hw = &pf->hw; + i40e_status aq_ret = 0; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + i40e_write_rx_ctl(hw, I40E_VFQF_HENA1(0, vf->vf_id), (u32)vrh->hena); + i40e_write_rx_ctl(hw, I40E_VFQF_HENA1(1, vf->vf_id), + (u32)(vrh->hena >> 32)); + + /* send the response to the VF */ +err: + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_SET_RSS_HENA, aq_ret); +} + +/** + * i40e_vc_enable_vlan_stripping + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * Enable vlan header stripping for the VF + **/ +static int i40e_vc_enable_vlan_stripping(struct i40e_vf *vf, u8 *msg) +{ + i40e_status aq_ret = 0; + struct i40e_vsi *vsi; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + vsi = vf->pf->vsi[vf->lan_vsi_idx]; + i40e_vlan_stripping_enable(vsi); + + /* send the response to the VF */ +err: + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ENABLE_VLAN_STRIPPING, + aq_ret); +} + +/** + * i40e_vc_disable_vlan_stripping + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * Disable vlan header stripping for the VF + **/ +static int i40e_vc_disable_vlan_stripping(struct i40e_vf *vf, u8 *msg) +{ + i40e_status aq_ret = 0; + struct i40e_vsi *vsi; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + vsi = vf->pf->vsi[vf->lan_vsi_idx]; + i40e_vlan_stripping_disable(vsi); + + /* send the response to the VF */ +err: + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DISABLE_VLAN_STRIPPING, + aq_ret); +} + +#ifdef __TC_MQPRIO_MODE_MAX +/** + * i40e_validate_cloud_filter + * @vf: pointer to the VF info + * @tc_filter: pointer to virtchnl_filter + * + * This function validates cloud filter programmed as TC filter for ADq + **/ +static int i40e_validate_cloud_filter(struct i40e_vf *vf, + struct virtchnl_filter *tc_filter) +{ + struct virtchnl_l4_spec mask = tc_filter->mask.tcp_spec; + struct virtchnl_l4_spec data = tc_filter->data.tcp_spec; + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = NULL; + struct i40e_mac_filter *f; + struct hlist_node *h; + bool found = false; + int bkt; + + if (!tc_filter->action) { + dev_info(&pf->pdev->dev, + "VF %d: Currently ADq doesn't support Drop Action\n", + vf->vf_id); + goto err; + } + + /* action_meta is TC number here to which the filter is applied */ + if (!tc_filter->action_meta || + tc_filter->action_meta > I40E_MAX_VF_VSI) { + dev_info(&pf->pdev->dev, "VF %d: Invalid TC number %u\n", + vf->vf_id, tc_filter->action_meta); + goto err; + } + + /* Check filter if it's programmed for advanced mode or basic mode. + * There are two ADq modes (for VF only), + * 1. Basic mode: intended to allow as many filter options as possible + * to be added to a VF in Non-trusted mode. Main goal is + * to add filters to its own MAC and VLAN id. + * 2. Advanced mode: is for allowing filters to be applied other than + * its own MAC or VLAN. This mode requires the VF to be + * Trusted. + */ + if (mask.dst_mac[0] && !mask.dst_ip[0]) { + vsi = pf->vsi[vf->lan_vsi_idx]; + f = i40e_find_mac(vsi, data.dst_mac); + + if (!f) { + dev_info(&pf->pdev->dev, + "Destination MAC %pM doesn't belong to VF %d\n", + data.dst_mac, vf->vf_id); + goto err; + } + + if (mask.vlan_id) { + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, + hlist) { + if (f->vlan == ntohs(data.vlan_id)) { + found = true; + break; + } + } + if (!found) { + dev_info(&pf->pdev->dev, + "VF %d doesn't have any VLAN id %u\n", + vf->vf_id, ntohs(data.vlan_id)); + goto err; + } + } + } else { + /* Check if VF is trusted */ + if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) { + dev_err(&pf->pdev->dev, + "VF %d not trusted, make VF trusted to add advanced mode ADq cloud filters\n", + vf->vf_id); + return I40E_ERR_CONFIG; + } + } + + if (mask.dst_mac[0] & data.dst_mac[0]) { + if (is_broadcast_ether_addr(data.dst_mac) || + is_zero_ether_addr(data.dst_mac)) { + dev_info(&pf->pdev->dev, "VF %d: Invalid Dest MAC addr %pM\n", + vf->vf_id, data.dst_mac); + goto err; + } + } + + if (mask.src_mac[0] & data.src_mac[0]) { + if (is_broadcast_ether_addr(data.src_mac) || + is_zero_ether_addr(data.src_mac)) { + dev_info(&pf->pdev->dev, "VF %d: Invalid Source MAC addr %pM\n", + vf->vf_id, data.src_mac); + goto err; + } + } + + if (mask.dst_port & data.dst_port) { + if (!data.dst_port) { + dev_info(&pf->pdev->dev, "VF %d: Invalid Dest port\n", + vf->vf_id); + goto err; + } + } + + if (mask.src_port & data.src_port) { + if (!data.src_port) { + dev_info(&pf->pdev->dev, "VF %d: Invalid Source port\n", + vf->vf_id); + goto err; + } + } + + if (tc_filter->flow_type != VIRTCHNL_TCP_V6_FLOW && + tc_filter->flow_type != VIRTCHNL_TCP_V4_FLOW) { + dev_info(&pf->pdev->dev, "VF %d: Invalid Flow type\n", + vf->vf_id); + goto err; + } + + if (mask.vlan_id & data.vlan_id) { + if (ntohs(data.vlan_id) > I40E_MAX_VLANID) { + dev_info(&pf->pdev->dev, "VF %d: invalid VLAN ID\n", + vf->vf_id); + goto err; + } + } + + return I40E_SUCCESS; +err: + return I40E_ERR_CONFIG; +} + +/** + * i40e_find_vf_vsi_from_seid - searches for the vsi with the given seid + * @vf: pointer to the VF info + * @seid: seid of the vsi it is searching for + **/ +static struct i40e_vsi *i40e_find_vf_vsi_from_seid(struct i40e_vf *vf, u16 seid) +{ + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = NULL; + int i; + + for (i = 0; i < vf->num_tc ; i++) { + vsi = i40e_find_vsi_from_id(pf, vf->ch[i].vsi_id); + if (vsi && vsi->seid == seid) + return vsi; + } + return NULL; +} + +/** + * i40e_del_all_cloud_filters + * @vf: pointer to the VF info + * + * This function deletes all cloud filters + **/ +static void i40e_del_all_cloud_filters(struct i40e_vf *vf) +{ + struct i40e_cloud_filter *cfilter = NULL; + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = NULL; + struct hlist_node *node; + int ret; + + hlist_for_each_entry_safe(cfilter, node, + &vf->cloud_filter_list, cloud_node) { + vsi = i40e_find_vf_vsi_from_seid(vf, cfilter->seid); + + if (!vsi) { + dev_err(&pf->pdev->dev, "VF %d: no VSI found for matching %u seid, can't delete cloud filter\n", + vf->vf_id, cfilter->seid); + continue; + } + + if (cfilter->dst_port) + ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter, + false); + else + ret = i40e_add_del_cloud_filter(vsi, cfilter, false); + if (ret) + dev_err(&pf->pdev->dev, + "VF %d: Failed to delete cloud filter, err %s aq_err %s\n", + vf->vf_id, i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); + + hlist_del(&cfilter->cloud_node); + kfree(cfilter); + vf->num_cloud_filters--; + } +} + +/** + * i40e_vc_del_cloud_filter + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * This function deletes a cloud filter programmed as TC filter for ADq + **/ +static int i40e_vc_del_cloud_filter(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_filter *vcf = (struct virtchnl_filter *)msg; + struct virtchnl_l4_spec mask = vcf->mask.tcp_spec; + struct virtchnl_l4_spec tcf = vcf->data.tcp_spec; + struct i40e_cloud_filter cfilter, *cf = NULL; + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = NULL; + struct hlist_node *node; + i40e_status aq_ret = 0; + int i, ret; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + if (!vf->adq_enabled) { + dev_info(&pf->pdev->dev, + "VF %d: ADq not enabled, can't apply cloud filter\n", + vf->vf_id); + aq_ret = I40E_ERR_PARAM; + goto err; + } + + if (i40e_validate_cloud_filter(vf, vcf)) { + dev_info(&pf->pdev->dev, + "VF %d: Invalid input, can't apply cloud filter\n", + vf->vf_id); + aq_ret = I40E_ERR_PARAM; + goto err; + } + + memset(&cfilter, 0, sizeof(cfilter)); + /* parse destination mac address */ + for (i = 0; i < ETH_ALEN; i++) + cfilter.dst_mac[i] = mask.dst_mac[i] & tcf.dst_mac[i]; + + /* parse source mac address */ + for (i = 0; i < ETH_ALEN; i++) + cfilter.src_mac[i] = mask.src_mac[i] & tcf.src_mac[i]; + + cfilter.vlan_id = mask.vlan_id & tcf.vlan_id; + cfilter.dst_port = mask.dst_port & tcf.dst_port; + cfilter.src_port = mask.src_port & tcf.src_port; + + switch (vcf->flow_type) { + case VIRTCHNL_TCP_V4_FLOW: + cfilter.n_proto = ETH_P_IP; + if (mask.dst_ip[0] & tcf.dst_ip[0]) + memcpy(&cfilter.ip.v4.dst_ip, tcf.dst_ip, + ARRAY_SIZE(tcf.dst_ip)); + else if (mask.src_ip[0] & tcf.dst_ip[0]) + memcpy(&cfilter.ip.v4.src_ip, tcf.src_ip, + ARRAY_SIZE(tcf.dst_ip)); + break; + case VIRTCHNL_TCP_V6_FLOW: + cfilter.n_proto = ETH_P_IPV6; + if (mask.dst_ip[3] & tcf.dst_ip[3]) + memcpy(&cfilter.ip.v6.dst_ip6, tcf.dst_ip, + sizeof(cfilter.ip.v6.dst_ip6)); + if (mask.src_ip[3] & tcf.src_ip[3]) + memcpy(&cfilter.ip.v6.src_ip6, tcf.src_ip, + sizeof(cfilter.ip.v6.src_ip6)); + break; + default: + /* TC filter can be configured based on different combinations + * and in this case IP is not a part of filter config + */ + dev_info(&pf->pdev->dev, "VF %d: Flow type not configured\n", + vf->vf_id); + } + + /* get the vsi to which the tc belongs to */ + vsi = pf->vsi[vf->ch[vcf->action_meta].vsi_idx]; + cfilter.seid = vsi->seid; + cfilter.flags = vcf->field_flags; + + /* Deleting TC filter */ + if (tcf.dst_port) + ret = i40e_add_del_cloud_filter_big_buf(vsi, &cfilter, false); + else + ret = i40e_add_del_cloud_filter(vsi, &cfilter, false); + if (ret) { + dev_err(&pf->pdev->dev, + "VF %d: Failed to delete cloud filter, err %s aq_err %s\n", + vf->vf_id, i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + goto err; + } + + hlist_for_each_entry_safe(cf, node, + &vf->cloud_filter_list, cloud_node) { + if (cf->seid != cfilter.seid) + continue; + if (mask.dst_port) + if (cfilter.dst_port != cf->dst_port) + continue; + if (mask.dst_mac[0]) + if (!ether_addr_equal(cf->src_mac, cfilter.src_mac)) + continue; + /* for ipv4 data to be valid, only first byte of mask is set */ + if (cfilter.n_proto == ETH_P_IP && mask.dst_ip[0]) + if (memcmp(&cfilter.ip.v4.dst_ip, &cf->ip.v4.dst_ip, + ARRAY_SIZE(tcf.dst_ip))) + continue; + /* for ipv6, mask is set for all sixteen bytes (4 words) */ + if (cfilter.n_proto == ETH_P_IPV6 && mask.dst_ip[3]) + if (memcmp(&cfilter.ip.v6.dst_ip6, &cf->ip.v6.dst_ip6, + sizeof(cfilter.ip.v6.src_ip6))) + continue; + if (mask.vlan_id) + if (cfilter.vlan_id != cf->vlan_id) + continue; + + hlist_del(&cf->cloud_node); + kfree(cf); + vf->num_cloud_filters--; + } + +err: + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DEL_CLOUD_FILTER, + aq_ret); +} + +/** + * i40e_vc_add_cloud_filter + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * + * This function adds a cloud filter programmed as TC filter for ADq + **/ +static int i40e_vc_add_cloud_filter(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_filter *vcf = (struct virtchnl_filter *)msg; + struct virtchnl_l4_spec mask = vcf->mask.tcp_spec; + struct virtchnl_l4_spec tcf = vcf->data.tcp_spec; + struct i40e_cloud_filter *cfilter = NULL; + struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = NULL; + i40e_status aq_ret = 0; + char err_msg_buf[100]; + bool is_quiet = false; + u16 err_msglen = 0; + u8 *err_msg = NULL; + int i, ret; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err_out; + } + + if (!vf->adq_enabled) { + dev_info(&pf->pdev->dev, + "VF %d: ADq is not enabled, can't apply cloud filter\n", + vf->vf_id); + aq_ret = I40E_ERR_PARAM; + goto err_out; + } + + if (pf->fdir_pf_active_filters || + (!hlist_empty(&pf->fdir_filter_list))) { + aq_ret = I40E_ERR_PARAM; + err_msglen = strlcpy(err_msg_buf, + "Flow Director Sideband filters exists, turn ntuple off to configure cloud filters", + sizeof(err_msg_buf)); + err_msg = err_msg_buf; + is_quiet = true; + goto err_out; + } + + if (i40e_validate_cloud_filter(vf, vcf)) { + dev_info(&pf->pdev->dev, + "VF %d: Invalid input/s, can't apply cloud filter\n", + vf->vf_id); + aq_ret = I40E_ERR_PARAM; + goto err_out; + } + + cfilter = kzalloc(sizeof(*cfilter), GFP_KERNEL); + if (!cfilter) + return -ENOMEM; + + /* parse destination mac address */ + for (i = 0; i < ETH_ALEN; i++) + cfilter->dst_mac[i] = mask.dst_mac[i] & tcf.dst_mac[i]; + + /* parse source mac address */ + for (i = 0; i < ETH_ALEN; i++) + cfilter->src_mac[i] = mask.src_mac[i] & tcf.src_mac[i]; + + cfilter->vlan_id = mask.vlan_id & tcf.vlan_id; + cfilter->dst_port = mask.dst_port & tcf.dst_port; + cfilter->src_port = mask.src_port & tcf.src_port; + + switch (vcf->flow_type) { + case VIRTCHNL_TCP_V4_FLOW: + cfilter->n_proto = ETH_P_IP; + if (mask.dst_ip[0] & tcf.dst_ip[0]) + memcpy(&cfilter->ip.v4.dst_ip, tcf.dst_ip, + ARRAY_SIZE(tcf.dst_ip)); + else if (mask.src_ip[0] & tcf.dst_ip[0]) + memcpy(&cfilter->ip.v4.src_ip, tcf.src_ip, + ARRAY_SIZE(tcf.dst_ip)); + break; + case VIRTCHNL_TCP_V6_FLOW: + cfilter->n_proto = ETH_P_IPV6; + if (mask.dst_ip[3] & tcf.dst_ip[3]) + memcpy(&cfilter->ip.v6.dst_ip6, tcf.dst_ip, + sizeof(cfilter->ip.v6.dst_ip6)); + if (mask.src_ip[3] & tcf.src_ip[3]) + memcpy(&cfilter->ip.v6.src_ip6, tcf.src_ip, + sizeof(cfilter->ip.v6.src_ip6)); + break; + default: + /* TC filter can be configured based on different combinations + * and in this case IP is not a part of filter config + */ + dev_info(&pf->pdev->dev, "VF %d: Flow type not configured\n", + vf->vf_id); + } + + /* get the VSI to which the TC belongs to */ + vsi = pf->vsi[vf->ch[vcf->action_meta].vsi_idx]; + cfilter->seid = vsi->seid; + cfilter->flags = vcf->field_flags; + + /* Adding cloud filter programmed as TC filter */ + if (tcf.dst_port) + ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter, true); + else + ret = i40e_add_del_cloud_filter(vsi, cfilter, true); + if (ret) { + dev_err(&pf->pdev->dev, + "VF %d: Failed to add cloud filter, err %s aq_err %s\n", + vf->vf_id, i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + goto err_free; + } + + INIT_HLIST_NODE(&cfilter->cloud_node); + hlist_add_head(&cfilter->cloud_node, &vf->cloud_filter_list); + /* release the pointer passing it to the collection */ + cfilter = NULL; + vf->num_cloud_filters++; +err_free: + kfree(cfilter); +err_out: + return i40e_vc_send_msg_to_vf_ex(vf, VIRTCHNL_OP_ADD_CLOUD_FILTER, + aq_ret, err_msg, err_msglen, + is_quiet); +} + +/** + * i40e_vc_add_qch_msg: Add queue channel and enable ADq + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + **/ +static int i40e_vc_add_qch_msg(struct i40e_vf *vf, u8 *msg) +{ + struct virtchnl_tc_info *tci = (struct virtchnl_tc_info *)msg; + struct i40e_pf *pf = vf->pf; + struct i40e_link_status *ls; + int i, adq_request_qps = 0; + i40e_status aq_ret = 0; + u32 speed; + + ls = &pf->hw.phy.link_info; + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + /* ADq cannot be applied if spoof check is ON */ + if (vf->mac_anti_spoof) { + dev_err(&pf->pdev->dev, + "Spoof check is ON, turn OFF both MAC and VLAN anti spoof to enable ADq\n"); + aq_ret = I40E_ERR_PARAM; + goto err; + } + + if (!(vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ADQ)) { + dev_err(&pf->pdev->dev, + "VF %d attempting to enable ADq, but hasn't properly negotiated that capability\n", + vf->vf_id); + aq_ret = I40E_ERR_PARAM; + goto err; + } + + /* max number of traffic classes for VF currently capped at 4 */ + if (!tci->num_tc || tci->num_tc > I40E_MAX_VF_VSI) { + dev_err(&pf->pdev->dev, + "VF %d trying to set %u TCs, valid range 1-%u TCs per VF\n", + vf->vf_id, tci->num_tc, I40E_MAX_VF_VSI); + aq_ret = I40E_ERR_PARAM; + goto err; + } + + /* validate queues for each TC */ + for (i = 0; i < tci->num_tc; i++) + if (!tci->list[i].count || + tci->list[i].count > I40E_DEFAULT_QUEUES_PER_VF) { + dev_err(&pf->pdev->dev, + "VF %d: TC %d trying to set %u queues, valid range 1-%u queues per TC\n", + vf->vf_id, i, tci->list[i].count, + I40E_DEFAULT_QUEUES_PER_VF); + aq_ret = I40E_ERR_PARAM; + goto err; + } + + /* need Max VF queues but already have default number of queues */ + adq_request_qps = I40E_MAX_VF_QUEUES - I40E_DEFAULT_QUEUES_PER_VF; + + if (pf->queues_left < adq_request_qps) { + dev_err(&pf->pdev->dev, + "No queues left to allocate to VF %d\n", + vf->vf_id); + aq_ret = I40E_ERR_PARAM; + goto err; + } else { + /* we need to allocate max VF queues to enable ADq so as to + * make sure ADq enabled VF always gets back queues when it + * goes through a reset. + */ + vf->num_queue_pairs = I40E_MAX_VF_QUEUES; + } + + /* get link speed in MB to validate rate limit */ + speed = i40e_vc_link_speed2mbps(ls->link_speed); + if (speed == SPEED_UNKNOWN) { + dev_err(&pf->pdev->dev, "Cannot detect link speed\n"); + aq_ret = I40E_ERR_PARAM; + goto err; + } + + /* parse data from the queue channel info */ + vf->num_tc = tci->num_tc; + for (i = 0; i < vf->num_tc; i++) { + if (tci->list[i].max_tx_rate) { + if (tci->list[i].max_tx_rate > speed) { + dev_err(&pf->pdev->dev, + "Invalid max tx rate %llu specified for VF %d.", + tci->list[i].max_tx_rate, + vf->vf_id); + aq_ret = I40E_ERR_PARAM; + goto err; + } else { + vf->ch[i].max_tx_rate = + tci->list[i].max_tx_rate; + } + } + vf->ch[i].num_qps = tci->list[i].count; + } + + /* set this flag only after making sure all inputs are sane */ + vf->adq_enabled = true; + /* num_req_queues is set when user changes number of queues via ethtool + * and this causes issue for default VSI(which depends on this variable) + * when ADq is enabled, hence reset it. + */ + vf->num_req_queues = 0; + + /* reset the VF in order to allocate resources */ + i40e_vc_reset_vf(vf, true); + + return I40E_SUCCESS; + + /* send the response to the VF */ +err: + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ENABLE_CHANNELS, + aq_ret); +} + +/** + * i40e_vc_del_qch_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + **/ +static int i40e_vc_del_qch_msg(struct i40e_vf *vf, u8 *msg) +{ + struct i40e_pf *pf = vf->pf; + i40e_status aq_ret = 0; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + if (vf->adq_enabled) { + i40e_del_all_cloud_filters(vf); + i40e_del_qch(vf); + vf->adq_enabled = false; + vf->num_tc = 0; + dev_info(&pf->pdev->dev, + "Deleting Queue Channels and cloud filters for ADq on VF %d\n", + vf->vf_id); + } else { + dev_info(&pf->pdev->dev, "VF %d trying to delete queue channels but ADq isn't enabled\n", + vf->vf_id); + aq_ret = I40E_ERR_PARAM; + } + + /* reset the VF in order to allocate resources */ + i40e_vc_reset_vf(vf, true); + + return I40E_SUCCESS; + +err: + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DISABLE_CHANNELS, + aq_ret); +} + +#endif /* __TC_MQPRIO_MODE_MAX */ + +/** + * i40e_vc_process_vf_msg + * @pf: pointer to the PF structure + * @vf_id: source VF id + * @v_opcode: operation code + * @v_retval: unused return value code + * @msg: pointer to the msg buffer + * @msglen: msg length + * + * called from the common aeq/arq handler to + * process request from VF + **/ +int i40e_vc_process_vf_msg(struct i40e_pf *pf, s16 vf_id, u32 v_opcode, + u32 __always_unused v_retval, u8 *msg, u16 msglen) +{ + struct i40e_hw *hw = &pf->hw; + int local_vf_id = vf_id - (s16)hw->func_caps.vf_base_id; + struct i40e_vf *vf; + int ret; + + pf->vf_aq_requests++; + if (local_vf_id < 0 || local_vf_id >= pf->num_alloc_vfs) + return -EINVAL; + vf = &(pf->vf[local_vf_id]); + + /* Check if VF is disabled. */ + if (test_bit(I40E_VF_STATE_DISABLED, &vf->vf_states)) + return I40E_ERR_PARAM; + + /* perform basic checks on the msg */ + ret = virtchnl_vc_validate_vf_msg(&vf->vf_ver, v_opcode, msg, msglen); + + if (ret) { + i40e_vc_send_resp_to_vf(vf, v_opcode, I40E_ERR_PARAM); + dev_err(&pf->pdev->dev, "Invalid message from VF %d, opcode %d, len %d\n", + local_vf_id, v_opcode, msglen); + switch (ret) { + case VIRTCHNL_STATUS_ERR_PARAM: + return -EPERM; + default: + return -EINVAL; + } + } + + switch (v_opcode) { + case VIRTCHNL_OP_VERSION: + ret = i40e_vc_get_version_msg(vf, msg); + break; + case VIRTCHNL_OP_GET_VF_RESOURCES: + ret = i40e_vc_get_vf_resources_msg(vf, msg); + i40e_vc_notify_vf_link_state(vf); + break; + case VIRTCHNL_OP_RESET_VF: + i40e_vc_reset_vf(vf, false); + if (!test_bit(__I40E_VFS_RELEASING, pf->state)) + clear_bit(I40E_VF_STATE_LOADED_VF_DRIVER, + &vf->vf_states); + ret = 0; + break; + case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE: + ret = i40e_vc_config_promiscuous_mode_msg(vf, msg); + break; + case VIRTCHNL_OP_CONFIG_VSI_QUEUES: + ret = i40e_vc_config_queues_msg(vf, msg); + break; + case VIRTCHNL_OP_CONFIG_IRQ_MAP: + ret = i40e_vc_config_irq_map_msg(vf, msg); + break; + case VIRTCHNL_OP_ENABLE_QUEUES: + ret = i40e_vc_enable_queues_msg(vf, msg); + i40e_vc_notify_vf_link_state(vf); + break; + case VIRTCHNL_OP_DISABLE_QUEUES: + ret = i40e_vc_disable_queues_msg(vf, msg); + break; + case VIRTCHNL_OP_ADD_ETH_ADDR: + ret = i40e_vc_add_mac_addr_msg(vf, msg); + break; + case VIRTCHNL_OP_DEL_ETH_ADDR: + ret = i40e_vc_del_mac_addr_msg(vf, msg); + break; + case VIRTCHNL_OP_ADD_VLAN: + ret = i40e_vc_add_vlan_msg(vf, msg); + break; + case VIRTCHNL_OP_DEL_VLAN: + ret = i40e_vc_remove_vlan_msg(vf, msg); + break; + case VIRTCHNL_OP_GET_STATS: + ret = i40e_vc_get_stats_msg(vf, msg); + break; + case VIRTCHNL_OP_CONFIG_RSS_KEY: + ret = i40e_vc_config_rss_key(vf, msg); + break; + case VIRTCHNL_OP_CONFIG_RSS_LUT: + ret = i40e_vc_config_rss_lut(vf, msg); + break; + case VIRTCHNL_OP_GET_RSS_HENA_CAPS: + ret = i40e_vc_get_rss_hena(vf, msg); + break; + case VIRTCHNL_OP_SET_RSS_HENA: + ret = i40e_vc_set_rss_hena(vf, msg); + break; + case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING: + ret = i40e_vc_enable_vlan_stripping(vf, msg); + break; + case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING: + ret = i40e_vc_disable_vlan_stripping(vf, msg); + break; + case VIRTCHNL_OP_REQUEST_QUEUES: + ret = i40e_vc_request_queues_msg(vf, msg); + break; +#ifdef __TC_MQPRIO_MODE_MAX + case VIRTCHNL_OP_ENABLE_CHANNELS: + ret = i40e_vc_add_qch_msg(vf, msg); + break; + case VIRTCHNL_OP_DISABLE_CHANNELS: + ret = i40e_vc_del_qch_msg(vf, msg); + break; + case VIRTCHNL_OP_ADD_CLOUD_FILTER: + ret = i40e_vc_add_cloud_filter(vf, msg); + break; + case VIRTCHNL_OP_DEL_CLOUD_FILTER: + ret = i40e_vc_del_cloud_filter(vf, msg); + break; +#endif /* __TC_MQPRIO_MODE_MAX */ + case VIRTCHNL_OP_UNKNOWN: + default: + dev_err(&pf->pdev->dev, "Unsupported opcode %d from VF %d\n", + v_opcode, local_vf_id); + ret = i40e_vc_send_resp_to_vf(vf, v_opcode, + I40E_ERR_NOT_IMPLEMENTED); + break; + } + + return ret; +} + +/** + * i40e_vc_process_vflr_event + * @pf: pointer to the PF structure + * + * called from the vlfr irq handler to + * free up VF resources and state variables + **/ +int i40e_vc_process_vflr_event(struct i40e_pf *pf) +{ + struct i40e_hw *hw = &pf->hw; + u32 reg, reg_idx, bit_idx; + struct i40e_vf *vf; + int vf_id; + + if (!test_bit(__I40E_VFLR_EVENT_PENDING, pf->state)) + return 0; + + /* Re-enable the VFLR interrupt cause here, before looking for which + * VF got reset. Otherwise, if another VF gets a reset while the + * first one is being processed, that interrupt will be lost, and + * that VF will be stuck in reset forever. + */ + reg = rd32(hw, I40E_PFINT_ICR0_ENA); + reg |= I40E_PFINT_ICR0_ENA_VFLR_MASK; + wr32(hw, I40E_PFINT_ICR0_ENA, reg); + i40e_flush(hw); + + clear_bit(__I40E_VFLR_EVENT_PENDING, pf->state); + for (vf_id = 0; vf_id < pf->num_alloc_vfs; vf_id++) { + reg_idx = (hw->func_caps.vf_base_id + vf_id) / 32; + bit_idx = (hw->func_caps.vf_base_id + vf_id) % 32; + /* read GLGEN_VFLRSTAT register to find out the flr VFs */ + vf = &pf->vf[vf_id]; + reg = rd32(hw, I40E_GLGEN_VFLRSTAT(reg_idx)); + if (reg & BIT(bit_idx)) + /* i40e_reset_vf will clear the bit in GLGEN_VFLRSTAT */ + i40e_reset_vf(vf, true); + } + + return 0; +} + +#ifdef IFLA_VF_MAX + +/** + * i40e_set_vf_mac + * @vf: the VF + * @vsi: VF VSI to configure + * @mac: the mac address + * + * This function allows the administrator to set the mac address for the VF + * + * Returns 0 on success, negative on failure + * + **/ +static int i40e_set_vf_mac(struct i40e_vf *vf, struct i40e_vsi *vsi, + const u8 *mac) +{ + struct i40e_pf *pf = vsi->back; + struct i40e_mac_filter *f; + struct hlist_node *h; + int ret = 0; + int bkt; + u8 i; + + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; + } + + if (is_multicast_ether_addr(mac)) { + dev_err(&pf->pdev->dev, + "Invalid Ethernet address %pM for VF %d\n", + mac, vf->vf_id); + ret = -EINVAL; + goto error_param; + } + + /* When the VF is resetting wait until it is done. + * It can take up to 200 milliseconds, + * but wait for up to 300 milliseconds to be safe. + * If the VF is indeed in reset, the vsi pointer has + * to show on the newly loaded vsi under pf->vsi[id]. + */ + for (i = 0; i < 15; i++) { + if (test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { + if (i > 0) + vsi = pf->vsi[vf->lan_vsi_idx]; + break; + } + msleep(20); + } + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { + dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", + vf->vf_id); + ret = -EAGAIN; + goto error_param; + } + + /* Lock once because below invoked function add/del_filter requires + * mac_filter_hash_lock to be held + */ + spin_lock_bh(&vsi->mac_filter_hash_lock); + + /* delete the temporary mac address */ + if (!is_zero_ether_addr(vf->default_lan_addr.addr)) + i40e_del_mac_filter(vsi, vf->default_lan_addr.addr); + + /* Delete all the filters for this VSI - we're going to kill it + * anyway. + */ + hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) + __i40e_del_filter(vsi, f); + + spin_unlock_bh(&vsi->mac_filter_hash_lock); + + /* program mac filter */ + if (i40e_sync_vsi_filters(vsi)) { + dev_err(&pf->pdev->dev, "Unable to program ucast filters\n"); + ret = -EIO; + goto error_param; + } + ether_addr_copy(vf->default_lan_addr.addr, mac); + + if (is_zero_ether_addr(mac)) { + vf->pf_set_mac = false; + dev_info(&pf->pdev->dev, "Removing MAC on VF %d\n", vf->vf_id); + } else { + vf->pf_set_mac = true; + dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", + mac, vf->vf_id); + } + + /* Force the VF interface down so it has to bring up with new MAC + * address + */ + i40e_vc_reset_vf(vf, true); + dev_info(&pf->pdev->dev, "Bring down and up the VF interface to make this change effective.\n"); +error_param: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; +} + +/** + * i40e_ndo_set_vf_mac + * @netdev: network interface device structure + * @vf_id: VF identifier + * @mac: mac address + * + * program VF mac address + **/ +int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + struct i40e_vf *vf; + int ret = 0; + + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto error_param; + + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + ret = i40e_set_vf_mac(vf, vsi, mac); +error_param: + return ret; +} + +/** + * i40e_vsi_has_vlans - True if VSI has configured VLANs + * @vsi: pointer to the vsi + * + * Check if a VSI has configured any VLANs. False if we have a port VLAN or if + * we have no configured VLANs. Do not call while holding the + * mac_filter_hash_lock. + */ +static bool i40e_vsi_has_vlans(struct i40e_vsi *vsi) +{ + bool have_vlans; + + /* If we have a port VLAN, then the VSI cannot have any VLANs + * configured, as all MAC/VLAN filters will be assigned to the PVID. + */ + if (vsi->info.pvid) + return false; + + /* Since we don't have a PVID, we know that if the device is in VLAN + * mode it must be because of a VLAN filter configured on this VSI. + */ + spin_lock_bh(&vsi->mac_filter_hash_lock); + have_vlans = i40e_is_vsi_in_vlan(vsi); + spin_unlock_bh(&vsi->mac_filter_hash_lock); + + return have_vlans; +} + +/** + * i40e_ndo_set_vf_port_vlan + * @netdev: network interface device structure + * @vf_id: VF identifier + * @vlan_id: mac address + * @qos: priority setting + * @vlan_proto: vlan protocol + * + * program VF vlan id and/or qos + **/ +#ifdef IFLA_VF_VLAN_INFO_MAX +int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, + u16 vlan_id, u8 qos, __be16 vlan_proto) +#else +int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, + int vf_id, u16 vlan_id, u8 qos) +#endif /* IFLA_VF_VLAN_INFO_MAX */ +{ + u16 vlanprio = vlan_id | (qos << I40E_VLAN_PRIORITY_SHIFT); + struct i40e_netdev_priv *np = netdev_priv(netdev); + bool allmulti = false, alluni = false; + struct i40e_pf *pf = np->vsi->back; + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; + + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; + } + + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto error_pvid; + + if ((vlan_id > I40E_MAX_VLANID) || (qos > 7)) { + dev_err(&pf->pdev->dev, "Invalid VF Parameters\n"); + ret = -EINVAL; + goto error_pvid; + } +#ifdef IFLA_VF_VLAN_INFO_MAX + + if (vlan_proto != htons(ETH_P_8021Q)) { + dev_err(&pf->pdev->dev, "VF VLAN protocol is not supported\n"); + ret = -EPROTONOSUPPORT; + goto error_pvid; + } +#endif + + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { + dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", + vf_id); + ret = -EAGAIN; + goto error_pvid; + } + + if (le16_to_cpu(vsi->info.pvid) == vlanprio) { +#ifdef HAVE_NDO_SET_VF_LINK_STATE + /* if vlan is being removed then clear trunk_vlan */ + if (!vsi->info.pvid) + memset(vf->trunk_vlans, 0, + BITS_TO_LONGS(VLAN_N_VID) * sizeof(long)); +#endif /* HAVE_NDO_SET_VF_LINK_STATE */ + goto error_pvid; + } + + if (i40e_vsi_has_vlans(vsi)) { + dev_err(&pf->pdev->dev, + "VF %d has already configured VLAN filters and the administrator is requesting a port VLAN override.\nPlease unload and reload the VF driver for this change to take effect.\n", + vf_id); + /* Administrator Error - knock the VF offline until he does + * the right thing by reconfiguring his network correctly + * and then reloading the VF driver. + */ + i40e_vc_reset_vf(vf, true); + /* During reset the VF got a new VSI, so refresh the pointer. */ + vsi = pf->vsi[vf->lan_vsi_idx]; + } + + /* Locked once because multiple functions below iterate list */ + spin_lock_bh(&vsi->mac_filter_hash_lock); + + /* Check for condition where there was already a port VLAN ID + * filter set and now it is being deleted by setting it to zero. + * Additionally check for the condition where there was a port + * VLAN but now there is a new and different port VLAN being set. + * Before deleting all the old VLAN filters we must add new ones + * with -1 (I40E_VLAN_ANY) or otherwise we're left with all our + * MAC addresses deleted. + */ + if ((!(vlan_id || qos) || + vlanprio != le16_to_cpu(vsi->info.pvid)) && + vsi->info.pvid) { + ret = i40e_add_vlan_all_mac(vsi, I40E_VLAN_ANY); + if (ret) { + dev_info(&vsi->back->pdev->dev, + "add VF VLAN failed, ret=%d aq_err=%d\n", ret, + vsi->back->hw.aq.asq_last_status); + spin_unlock_bh(&vsi->mac_filter_hash_lock); + goto error_pvid; + } + } + + if (vsi->info.pvid) { + /* remove all filters on the old VLAN */ + i40e_rm_vlan_all_mac(vsi, (le16_to_cpu(vsi->info.pvid) & + VLAN_VID_MASK)); + } + + spin_unlock_bh(&vsi->mac_filter_hash_lock); + + /* disable promisc modes in case they were enabled */ + ret = i40e_config_vf_promiscuous_mode(vf, vf->lan_vsi_id, + allmulti, alluni); + if (ret) { + dev_err(&pf->pdev->dev, "Unable to config VF promiscuous mode\n"); + goto error_pvid; + } + + if (vlan_id || qos) { + ret = i40e_vsi_add_pvid(vsi, vlanprio); + if (ret) { + dev_info(&vsi->back->pdev->dev, + "add VF VLAN failed, ret=%d aq_err=%d\n", ret, + vsi->back->hw.aq.asq_last_status); + goto error_pvid; + } + } else { + i40e_vsi_remove_pvid(vsi); +#ifdef HAVE_NDO_SET_VF_LINK_STATE + /* if vlan is being removed then clear also trunk_vlan */ + if (!vsi->info.pvid) + memset(vf->trunk_vlans, 0, + BITS_TO_LONGS(VLAN_N_VID) * sizeof(long)); +#endif /* HAVE_NDO_SET_VF_LINK_STATE */ + } + spin_lock_bh(&vsi->mac_filter_hash_lock); + + if (vlan_id) { + dev_info(&pf->pdev->dev, "Setting VLAN %d, QOS 0x%x on VF %d\n", + vlan_id, qos, vf_id); + + /* add new VLAN filter for each MAC */ + ret = i40e_add_vlan_all_mac(vsi, vlan_id); + if (ret) { + dev_info(&vsi->back->pdev->dev, + "add VF VLAN failed, ret=%d aq_err=%d\n", ret, + vsi->back->hw.aq.asq_last_status); + spin_unlock_bh(&vsi->mac_filter_hash_lock); + goto error_pvid; + } +#ifdef HAVE_NDO_SET_VF_LINK_STATE + /* only pvid should be present in trunk */ + memset(vf->trunk_vlans, 0, + BITS_TO_LONGS(VLAN_N_VID) * sizeof(long)); + set_bit(vsi->info.pvid, vf->trunk_vlans); +#endif /* HAVE_NDO_SET_VF_LINK_STATE */ + + /* remove the previously added non-VLAN MAC filters */ + i40e_rm_vlan_all_mac(vsi, I40E_VLAN_ANY); + } + + spin_unlock_bh(&vsi->mac_filter_hash_lock); + + if (test_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states)) + alluni = true; + + if (test_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states)) + allmulti = true; + + /* The Port VLAN needs to be saved across resets the same as the + * default LAN MAC address. + */ + vf->port_vlan_id = le16_to_cpu(vsi->info.pvid); + if (vsi->info.pvid) { + ret = i40e_config_vf_promiscuous_mode(vf, + vsi->id, + allmulti, + alluni); + if (ret) { + dev_err(&pf->pdev->dev, "Unable to config vf promiscuous mode\n"); + goto error_pvid; + } + } + + /* Schedule the worker thread to take care of applying changes */ + i40e_service_event_schedule(vsi->back); + +error_pvid: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; +} + +/** + * i40e_ndo_set_vf_bw + * @netdev: network interface device structure + * @vf_id: VF identifier + * @min_tx_rate: Minimum Tx rate + * @max_tx_rate: Maximum Tx rate + * + * configure VF Tx rate + **/ +#ifdef HAVE_NDO_SET_VF_MIN_MAX_TX_RATE +int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, + int max_tx_rate) +#else +int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int max_tx_rate) +#endif +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_pf *pf = np->vsi->back; + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret = 0; + + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; + } + + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto error; + + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { + dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", + vf_id); + ret = -EAGAIN; + goto error; + } + + ret = i40e_set_bw_limit(vsi, vsi->seid, max_tx_rate); + if (ret) + goto error; + + vf->tx_rate = max_tx_rate; +error: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; +} + +/** + * i40e_ndo_enable_vf + * @netdev: network interface device structure + * @vf_id: VF identifier + * @enable: true to enable & false to disable + * + * enable/disable VF + **/ +int i40e_ndo_enable_vf(struct net_device *netdev, int vf_id, bool enable) +{ + return -EOPNOTSUPP; +} + +/** + * i40e_ndo_get_vf_config + * @netdev: network interface device structure + * @vf_id: VF identifier + * @ivi: VF configuration structure + * + * return VF configuration + **/ +int i40e_ndo_get_vf_config(struct net_device *netdev, + int vf_id, struct ifla_vf_info *ivi) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + struct i40e_vf *vf; + int ret = 0; + + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; } - set_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states); -err: - /* send the response back to the VF */ - ret = i40e_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_VF_RESOURCES, - aq_ret, (u8 *)vfres, len); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto error_param; - kfree(vfres); + vf = &pf->vf[vf_id]; + /* first vsi is always the LAN vsi */ + vsi = pf->vsi[vf->lan_vsi_idx]; + if (!vsi) { + ret = -ENOENT; + goto error_param; + } + + ivi->vf = vf_id; + + ether_addr_copy(ivi->mac, vf->default_lan_addr.addr); + +#ifdef HAVE_NDO_SET_VF_MIN_MAX_TX_RATE + ivi->max_tx_rate = vf->tx_rate; + ivi->min_tx_rate = 0; +#else + ivi->tx_rate = vf->tx_rate; +#endif + ivi->vlan = le16_to_cpu(vsi->info.pvid) & I40E_VLAN_MASK; + ivi->qos = (le16_to_cpu(vsi->info.pvid) & I40E_PRIORITY_MASK) >> + I40E_VLAN_PRIORITY_SHIFT; +#ifdef HAVE_NDO_SET_VF_LINK_STATE + if (vf->link_forced == false) + ivi->linkstate = IFLA_VF_LINK_STATE_AUTO; + else if (vf->link_up == true) + ivi->linkstate = IFLA_VF_LINK_STATE_ENABLE; + else + ivi->linkstate = IFLA_VF_LINK_STATE_DISABLE; +#endif +#ifdef HAVE_VF_SPOOFCHK_CONFIGURE + ivi->spoofchk = vf->mac_anti_spoof; +#endif +#ifdef HAVE_NDO_SET_VF_TRUST + ivi->trusted = vf->trusted; +#endif /* HAVE_NDO_SET_VF_TRUST */ + ret = 0; + +error_param: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; +} + +#ifdef HAVE_NDO_SET_VF_LINK_STATE +/** + * i40e_ndo_set_vf_link_state + * @netdev: network interface device structure + * @vf_id: VF identifier + * @link: required link state + * + * Set the link state of a specified VF, regardless of physical link state + **/ +int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_pf *pf = np->vsi->back; + struct i40e_link_status *ls = &pf->hw.phy.link_info; + struct virtchnl_pf_event pfe; + struct i40e_hw *hw = &pf->hw; + struct i40e_vf *vf; + int abs_vf_id; + int ret = 0; + + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; + } + + /* validate the request */ + if (vf_id >= pf->num_alloc_vfs) { + dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id); + ret = -EINVAL; + goto error_out; + } + + vf = &pf->vf[vf_id]; + abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id; + + pfe.event = VIRTCHNL_EVENT_LINK_CHANGE; + pfe.severity = PF_EVENT_SEVERITY_INFO; + + switch (link) { + case IFLA_VF_LINK_STATE_AUTO: + vf->link_forced = false; +#ifdef VIRTCHNL_VF_CAP_ADV_LINK_SPEED + pfe.event_data.link_event_adv.link_status = + ls->link_info & I40E_AQ_LINK_UP; + pfe.event_data.link_event_adv.link_speed = + i40e_vc_link_speed2mbps(ls->link_speed); +#else /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + pfe.event_data.link_event.link_status = + ls->link_info & I40E_AQ_LINK_UP; + pfe.event_data.link_event.link_speed = + i40e_virtchnl_link_speed(ls->link_speed); +#endif /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + break; + case IFLA_VF_LINK_STATE_ENABLE: + vf->link_forced = true; + vf->link_up = true; +#ifdef VIRTCHNL_VF_CAP_ADV_LINK_SPEED + pfe.event_data.link_event_adv.link_status = true; + pfe.event_data.link_event_adv.link_speed = + i40e_vc_link_speed2mbps(ls->link_speed); +#else /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + pfe.event_data.link_event.link_status = true; + pfe.event_data.link_event.link_speed = + i40e_virtchnl_link_speed(ls->link_speed); +#endif /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + break; + case IFLA_VF_LINK_STATE_DISABLE: + vf->link_forced = true; + vf->link_up = false; +#ifdef VIRTCHNL_VF_CAP_ADV_LINK_SPEED + pfe.event_data.link_event_adv.link_status = false; + pfe.event_data.link_event_adv.link_speed = 0; +#else /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + pfe.event_data.link_event.link_status = false; + pfe.event_data.link_event.link_speed = 0; +#endif /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + break; + default: + ret = -EINVAL; + goto error_out; + } + /* Do not allow change link state when VF is disabled + * Check if requested link state is not IFLA_VF_LINK_STATE_DISABLE, + * to prevent false positive warning in case of reloading the driver + */ + if (vf->pf_ctrl_disable && link != IFLA_VF_LINK_STATE_DISABLE) { + vf->link_up = false; +#ifdef VIRTCHNL_VF_CAP_ADV_LINK_SPEED + pfe.event_data.link_event_adv.link_status = false; + pfe.event_data.link_event_adv.link_speed = 0; +#else /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + pfe.event_data.link_event.link_status = false; + pfe.event_data.link_event.link_speed = 0; +#endif /* VIRTCHNL_VF_CAP_ADV_LINK_SPEED */ + dev_warn(&pf->pdev->dev, + "Not possible to change VF link state, please enable it first\n"); + } + + /* Notify the VF of its new link state */ + i40e_aq_send_msg_to_vf(hw, abs_vf_id, VIRTCHNL_OP_EVENT, + I40E_SUCCESS, (u8 *)&pfe, sizeof(pfe), NULL); + +error_out: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; +} + +#endif /* HAVE_NDO_SET_VF_LINK_STATE */ +#ifdef HAVE_VF_SPOOFCHK_CONFIGURE +/** + * i40e_ndo_set_vf_spoofchk + * @netdev: network interface device structure + * @vf_id: VF identifier + * @enable: flag to enable or disable feature + * + * Enable or disable VF spoof checking + **/ +int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + struct i40e_vsi_context ctxt; + struct i40e_hw *hw = &pf->hw; + struct i40e_vf *vf; + int ret = 0; + + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; + } + + /* validate the request */ + if (vf_id >= pf->num_alloc_vfs) { + dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id); + ret = -EINVAL; + goto out; + } + + vf = &(pf->vf[vf_id]); + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { + dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", + vf_id); + ret = -EAGAIN; + goto out; + } + + if (enable == vf->mac_anti_spoof) + goto out; + + vf->mac_anti_spoof = enable; + memset(&ctxt, 0, sizeof(ctxt)); + ctxt.seid = pf->vsi[vf->lan_vsi_idx]->seid; + ctxt.pf_num = pf->hw.pf_id; + ctxt.info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID); + if (enable) + ctxt.info.sec_flags |= I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK; + ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL); + if (ret) { + dev_err(&pf->pdev->dev, "Error %d updating VSI parameters\n", + ret); + ret = -EIO; + } +out: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; +} + +#endif /* HAVE_VF_SPOOFCHK_CONFIGURE */ +#ifdef HAVE_NDO_SET_VF_TRUST +/** + * i40e_ndo_set_vf_trust + * @netdev: network interface device structure of the pf + * @vf_id: VF identifier + * @setting: trust setting + * + * Enable or disable VF trust setting + **/ +int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_pf *pf = np->vsi->back; + struct i40e_vf *vf; + int ret = 0; + + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; + } + + /* validate the request */ + if (vf_id >= pf->num_alloc_vfs) { + dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id); + ret = -EINVAL; + goto out; + } + + if (pf->flags & I40E_FLAG_MFP_ENABLED) { + dev_err(&pf->pdev->dev, "Trusted VF not supported in MFP mode.\n"); + ret = -EINVAL; + goto out; + } + + vf = &pf->vf[vf_id]; + + /* if vf is in base mode, make it untrusted */ + if (pf->vf_base_mode_only) + setting = false; + if (setting == vf->trusted) + goto out; + + vf->trusted = setting; + i40e_vc_reset_vf(vf, true); + dev_info(&pf->pdev->dev, "VF %u is now %strusted\n", + vf_id, setting ? "" : "un"); + +#ifdef __TC_MQPRIO_MODE_MAX + if (vf->adq_enabled) { + if (!vf->trusted) { + dev_info(&pf->pdev->dev, + "VF %u no longer Trusted, deleting all cloud filters\n", + vf_id); + i40e_del_all_cloud_filters(vf); + } + } +#endif /* __TC_MQPRIO_MODE_MAX */ + +out: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); return ret; } +#endif /* HAVE_NDO_SET_VF_TRUST */ +#ifdef HAVE_VF_STATS /** - * i40e_vc_reset_vf_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length - * - * called from the VF to reset itself, - * unlike other virtchnl messages, PF driver - * doesn't send the response back to the VF - **/ -static void i40e_vc_reset_vf_msg(struct i40e_vf *vf) + * i40e_get_vf_stats - populate some stats for the VF + * @netdev: the netdev of the PF + * @vf_id: the host OS identifier (0-127) + * @vf_stats: pointer to the OS memory to be initialized + */ +int i40e_get_vf_stats(struct net_device *netdev, int vf_id, + struct ifla_vf_stats *vf_stats) { - if (test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) - i40e_reset_vf(vf, false); + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_pf *pf = np->vsi->back; + struct i40e_eth_stats *stats; + struct i40e_vsi *vsi; + struct i40e_vf *vf; + + /* validate the request */ + if (i40e_validate_vf(pf, vf_id)) + return -EINVAL; + + vf = &pf->vf[vf_id]; + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { + dev_err(&pf->pdev->dev, "VF %d in reset. Try again.\n", vf_id); + return -EBUSY; + } + + vsi = pf->vsi[vf->lan_vsi_idx]; + if (!vsi) + return -EINVAL; + + i40e_update_eth_stats(vsi); + stats = &vsi->eth_stats; + + memset(vf_stats, 0, sizeof(*vf_stats)); + + vf_stats->rx_packets = stats->rx_unicast + stats->rx_broadcast + + stats->rx_multicast; + vf_stats->tx_packets = stats->tx_unicast + stats->tx_broadcast + + stats->tx_multicast; + vf_stats->rx_bytes = stats->rx_bytes; + vf_stats->tx_bytes = stats->tx_bytes; + vf_stats->broadcast = stats->rx_broadcast; + vf_stats->multicast = stats->rx_multicast; +#ifdef HAVE_VF_STATS_DROPPED + vf_stats->rx_dropped = stats->rx_discards; + vf_stats->tx_dropped = stats->tx_discards; +#endif + + return 0; } +#endif /* HAVE_VF_STATS */ +#endif /* IFLA_VF_MAX */ +#ifdef HAVE_NDO_SET_VF_LINK_STATE /** - * i40e_getnum_vf_vsi_vlan_filters - * @vsi: pointer to the vsi + * i40e_get_trunk - Gets the configured VLAN filters + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @trunk_vlans: trunk vlans * - * called to get the number of VLANs offloaded on this VF + * Gets the active trunk vlans + * + * Returns the number of active vlans filters on success, + * negative on failure **/ -static inline int i40e_getnum_vf_vsi_vlan_filters(struct i40e_vsi *vsi) +static int i40e_get_trunk(struct pci_dev *pdev, int vf_id, + unsigned long *trunk_vlans) { - struct i40e_mac_filter *f; - int num_vlans = 0, bkt; + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; - hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { - if (f->vlan >= 0 && f->vlan <= I40E_MAX_VLANID) - num_vlans++; + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; } - return num_vlans; + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto out; + vf = &pf->vf[vf_id]; + /* checking if pvid has been set through netdev */ + vsi = pf->vsi[vf->lan_vsi_idx]; + if (vsi->info.pvid) { + memset(trunk_vlans, 0, + BITS_TO_LONGS(VLAN_N_VID) * sizeof(long)); + set_bit(vsi->info.pvid, trunk_vlans); + } else { + bitmap_copy(trunk_vlans, vf->trunk_vlans, VLAN_N_VID); + } + + bitmap_copy(trunk_vlans, vf->trunk_vlans, VLAN_N_VID); + ret = bitmap_weight(trunk_vlans, VLAN_N_VID); +out: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; } /** - * i40e_vc_config_promiscuous_mode_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_set_trunk - Configure VLAN filters + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @vlan_bitmap: vlans to filter on * - * called from the VF to configure the promiscuous mode of - * VF vsis + * Applies the VLAN filters + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, - u8 *msg, u16 msglen) +static int i40e_set_trunk(struct pci_dev *pdev, int vf_id, + const unsigned long *vlan_bitmap) { - struct virtchnl_promisc_info *info = - (struct virtchnl_promisc_info *)msg; - struct i40e_pf *pf = vf->pf; - struct i40e_hw *hw = &pf->hw; - struct i40e_mac_filter *f; - i40e_status aq_ret = 0; - bool allmulti = false; + struct i40e_pf *pf = pci_get_drvdata(pdev); struct i40e_vsi *vsi; - bool alluni = false; - int aq_err = 0; - int bkt; + struct i40e_vf *vf; + int ret; + u16 vid; - vsi = i40e_find_vsi_from_id(pf, info->vsi_id); - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || - !i40e_vc_isvalid_vsi_id(vf, info->vsi_id) || - !vsi) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } - if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) { - dev_err(&pf->pdev->dev, - "Unprivileged VF %d is attempting to configure promiscuous mode\n", - vf->vf_id); - /* Lie to the VF on purpose. */ - aq_ret = 0; - goto error_param; + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; } - /* Multicast promiscuous handling*/ - if (info->flags & FLAG_VF_MULTICAST_PROMISC) - allmulti = true; - if (vf->port_vlan_id) { - aq_ret = i40e_aq_set_vsi_mc_promisc_on_vlan(hw, vsi->seid, - allmulti, - vf->port_vlan_id, - NULL); - } else if (i40e_getnum_vf_vsi_vlan_filters(vsi)) { - hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { - if (f->vlan < 0 || f->vlan > I40E_MAX_VLANID) - continue; - aq_ret = i40e_aq_set_vsi_mc_promisc_on_vlan(hw, - vsi->seid, - allmulti, - f->vlan, - NULL); - aq_err = pf->hw.aq.asq_last_status; - if (aq_ret) { - dev_err(&pf->pdev->dev, - "Could not add VLAN %d to multicast promiscuous domain err %s aq_err %s\n", - f->vlan, - i40e_stat_str(&pf->hw, aq_ret), - i40e_aq_str(&pf->hw, aq_err)); - break; - } + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + i40e_vlan_stripping_enable(vsi); + + /* checking if pvid has been set through netdev */ + vid = vsi->info.pvid; + if (vid) { + struct i40e_vsi *pf_vsi = pf->vsi[pf->lan_vsi]; + + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); +#ifdef IFLA_VF_VLAN_INFO_MAX +#ifdef HAVE_RHEL7_NETDEV_OPS_EXT_NDO_SET_VF_VLAN + pf_vsi->netdev->netdev_ops->extended.ndo_set_vf_vlan + (pf_vsi->netdev, vf_id, 0, 0, htons(ETH_P_8021Q)); +#else + pf_vsi->netdev->netdev_ops->ndo_set_vf_vlan + (pf_vsi->netdev, vf_id, 0, 0, htons(ETH_P_8021Q)); +#endif // HAVE_RHEL7_NETDEV_OPS_EXT_NDO_SET_VF_VLAN +#else // IFLA_VF_VLAN_INFO_MAX +#ifdef HAVE_RHEL7_NETDEV_OPS_EXT_NDO_SET_VF_VLAN + pf_vsi->netdev->netdev_ops->extended.ndo_set_vf_vlan + (pf_vsi->netdev, vf_id, 0, 0); +#else + pf_vsi->netdev->netdev_ops->ndo_set_vf_vlan + (pf_vsi->netdev, vf_id, 0, 0); +#endif // HAVE_RHEL7_NETDEV_OPS_EXT_NDO_SET_VF_VLAN +#endif // IFLA_VF_VLAN_INFO_MAX + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; } - } else { - aq_ret = i40e_aq_set_vsi_multicast_promiscuous(hw, vsi->seid, - allmulti, NULL); - aq_err = pf->hw.aq.asq_last_status; - if (aq_ret) { - dev_err(&pf->pdev->dev, - "VF %d failed to set multicast promiscuous mode err %s aq_err %s\n", - vf->vf_id, - i40e_stat_str(&pf->hw, aq_ret), - i40e_aq_str(&pf->hw, aq_err)); - goto error_param; + if (i40e_vsi_add_vlan(vsi, vid)) { + dev_warn(&pdev->dev, "Unable to restore Port VLAN for trunking.\n"); + goto out; } } - if (!aq_ret) { - dev_info(&pf->pdev->dev, - "VF %d successfully set multicast promiscuous mode\n", - vf->vf_id); - if (allmulti) - set_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states); - else - clear_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states); + /* Add vlans */ + for_each_set_bit(vid, vlan_bitmap, VLAN_N_VID) { + if (!test_bit(vid, vf->trunk_vlans)) { + ret = i40e_vsi_add_vlan(vsi, vid); + if (ret) + goto out; + } } - if (info->flags & FLAG_VF_UNICAST_PROMISC) - alluni = true; - if (vf->port_vlan_id) { - aq_ret = i40e_aq_set_vsi_uc_promisc_on_vlan(hw, vsi->seid, - alluni, - vf->port_vlan_id, - NULL); - } else if (i40e_getnum_vf_vsi_vlan_filters(vsi)) { - hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { - if (f->vlan < 0 || f->vlan > I40E_MAX_VLANID) - continue; - aq_ret = i40e_aq_set_vsi_uc_promisc_on_vlan(hw, - vsi->seid, - alluni, - f->vlan, - NULL); - aq_err = pf->hw.aq.asq_last_status; - if (aq_ret) - dev_err(&pf->pdev->dev, - "Could not add VLAN %d to Unicast promiscuous domain err %s aq_err %s\n", - f->vlan, - i40e_stat_str(&pf->hw, aq_ret), - i40e_aq_str(&pf->hw, aq_err)); - } - } else { - aq_ret = i40e_aq_set_vsi_unicast_promiscuous(hw, vsi->seid, - alluni, NULL, - true); - aq_err = pf->hw.aq.asq_last_status; - if (aq_ret) { - dev_err(&pf->pdev->dev, - "VF %d failed to set unicast promiscuous mode %8.8x err %s aq_err %s\n", - vf->vf_id, info->flags, - i40e_stat_str(&pf->hw, aq_ret), - i40e_aq_str(&pf->hw, aq_err)); - goto error_param; - } + /* If to empty trunk filter is added, remove I40E_VLAN_ANY. + * Removal of this filter sets allow_untagged to false. + */ + if (bitmap_weight(vlan_bitmap, VLAN_N_VID) && + !bitmap_weight(vf->trunk_vlans, VLAN_N_VID)) { + i40e_vsi_kill_vlan(vsi, I40E_VLAN_ANY); + vf->allow_untagged = false; } - if (!aq_ret) { - dev_info(&pf->pdev->dev, - "VF %d successfully set unicast promiscuous mode\n", - vf->vf_id); - if (alluni) - set_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states); - else - clear_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states); + /* If deleting all vlan filters, check if we have VLAN 0 filters + * existing. If we don't, add filters to allow all traffic i.e + * VLAN tag = -1 before deleting all filters because the in the + * delete all filters flow, we check if there are VLAN 0 filters + * and then replace them with filters of VLAN id = -1 + */ + if (!bitmap_weight(vlan_bitmap, VLAN_N_VID) && !vf->allow_untagged) { + ret = i40e_vsi_add_vlan(vsi, I40E_VLAN_ANY); + if (ret) + goto out; + vf->allow_untagged = true; } -error_param: - /* send the response to the VF */ - return i40e_vc_send_resp_to_vf(vf, - VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE, - aq_ret); + /* Del vlans */ + for_each_set_bit(vid, vf->trunk_vlans, VLAN_N_VID) { + if (!test_bit(vid, vlan_bitmap)) + i40e_vsi_kill_vlan(vsi, vid); + } + /* Copy over the updated bitmap */ + bitmap_copy(vf->trunk_vlans, vlan_bitmap, VLAN_N_VID); +out: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; } /** - * i40e_vc_config_queues_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_get_mirror - Gets the configured VLAN mirrors + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @mirror_vlans: mirror vlans * - * called from the VF to configure the rx/tx - * queues + * Gets the active mirror vlans + * + * Returns the number of active mirror vlans on success, + * negative on failure **/ -static int i40e_vc_config_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_get_mirror(struct pci_dev *pdev, int vf_id, + unsigned long *mirror_vlans) { - struct virtchnl_vsi_queue_config_info *qci = - (struct virtchnl_vsi_queue_config_info *)msg; - struct virtchnl_queue_pair_info *qpi; - struct i40e_pf *pf = vf->pf; - u16 vsi_id, vsi_queue_id; - i40e_status aq_ret = 0; - int i; - - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } - - vsi_id = qci->vsi_id; - if (!i40e_vc_isvalid_vsi_id(vf, vsi_id)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } - for (i = 0; i < qci->num_queue_pairs; i++) { - qpi = &qci->qpair[i]; - vsi_queue_id = qpi->txq.queue_id; - if ((qpi->txq.vsi_id != vsi_id) || - (qpi->rxq.vsi_id != vsi_id) || - (qpi->rxq.queue_id != vsi_queue_id) || - !i40e_vc_isvalid_queue_id(vf, vsi_id, vsi_queue_id)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vf *vf; + int ret; - if (i40e_config_vsi_rx_queue(vf, vsi_id, vsi_queue_id, - &qpi->rxq) || - i40e_config_vsi_tx_queue(vf, vsi_id, vsi_queue_id, - &qpi->txq)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; } - /* set vsi num_queue_pairs in use to num configured by VF */ - pf->vsi[vf->lan_vsi_idx]->num_queue_pairs = qci->num_queue_pairs; -error_param: - /* send the response to the VF */ - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_CONFIG_VSI_QUEUES, - aq_ret); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto out; + vf = &pf->vf[vf_id]; + bitmap_copy(mirror_vlans, vf->mirror_vlans, VLAN_N_VID); + ret = bitmap_weight(mirror_vlans, VLAN_N_VID); +out: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; } /** - * i40e_vc_config_irq_map_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_set_mirror - Configure VLAN mirrors + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @vlan_bitmap: vlans to configure as mirrors * - * called from the VF to configure the irq to - * queue map + * Configures the mirror vlans + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_config_irq_map_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_set_mirror(struct pci_dev *pdev, int vf_id, + const unsigned long *vlan_bitmap) { - struct virtchnl_irq_map_info *irqmap_info = - (struct virtchnl_irq_map_info *)msg; - struct virtchnl_vector_map *map; - u16 vsi_id, vsi_queue_id, vector_id; - i40e_status aq_ret = 0; - unsigned long tempmap; - int i; + u16 vid, sw_seid, dst_seid, rule_id, rule_type; + struct i40e_pf *pf = pci_get_drvdata(pdev); + int ret = 0, num = 0, cnt = 0, add = 0; + u16 rules_used, rules_free; + struct i40e_vsi *vsi; + struct i40e_vf *vf; + __le16 *mr_list; - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } + DECLARE_BITMAP(num_vlans, VLAN_N_VID); - for (i = 0; i < irqmap_info->num_vectors; i++) { - map = &irqmap_info->vecmap[i]; + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; + } - vector_id = map->vector_id; - vsi_id = map->vsi_id; - /* validate msg params */ - if (!i40e_vc_isvalid_vector_id(vf, vector_id) || - !i40e_vc_isvalid_vsi_id(vf, vsi_id)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + sw_seid = vsi->uplink_seid; + dst_seid = vsi->seid; + rule_type = I40E_AQC_MIRROR_RULE_TYPE_VLAN; + bitmap_xor(num_vlans, vf->mirror_vlans, vlan_bitmap, VLAN_N_VID); + cnt = bitmap_weight(num_vlans, VLAN_N_VID); + if (!cnt) + goto out; + mr_list = kcalloc(cnt, sizeof(__le16), GFP_KERNEL); + if (!mr_list) { + ret = -ENOMEM; + goto out; + } - /* lookout for the invalid queue index */ - tempmap = map->rxq_map; - for_each_set_bit(vsi_queue_id, &tempmap, I40E_MAX_VSI_QP) { - if (!i40e_vc_isvalid_queue_id(vf, vsi_id, - vsi_queue_id)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; + /* figure out if adding or deleting */ + bitmap_and(num_vlans, vlan_bitmap, num_vlans, VLAN_N_VID); + add = bitmap_weight(num_vlans, VLAN_N_VID); + if (add) { + /* Add mirrors */ + for_each_set_bit(vid, vlan_bitmap, VLAN_N_VID) { + if (!test_bit(vid, vf->mirror_vlans)) { + mr_list[num] = CPU_TO_LE16(vid); + num++; } } - - tempmap = map->txq_map; - for_each_set_bit(vsi_queue_id, &tempmap, I40E_MAX_VSI_QP) { - if (!i40e_vc_isvalid_queue_id(vf, vsi_id, - vsi_queue_id)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; + ret = i40e_aq_add_mirrorrule(&pf->hw, sw_seid, + rule_type, dst_seid, + cnt, mr_list, NULL, + &rule_id, &rules_used, + &rules_free); + if (ret) + goto err_free; + vf->vlan_rule_id = rule_id; + } else { + /* Del mirrors */ + for_each_set_bit(vid, vf->mirror_vlans, VLAN_N_VID) { + if (!test_bit(vid, vlan_bitmap)) { + mr_list[num] = CPU_TO_LE16(vid); + num++; } } + ret = i40e_aq_delete_mirrorrule(&pf->hw, sw_seid, rule_type, + vf->vlan_rule_id, cnt, mr_list, + NULL, &rules_used, + &rules_free); + if (ret) + goto err_free; + } - i40e_config_irq_link_list(vf, vsi_id, map); + /* Copy over the updated bitmap */ + bitmap_copy(vf->mirror_vlans, vlan_bitmap, VLAN_N_VID); +err_free: + kfree(mr_list); +out: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; +} + +/** + * i40e_get_allow_untagged + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @on: on or off + * + * This functions checks if the untagged packets + * are allowed or not. + * + * Returns 0 on success, negative on failure + **/ +static int i40e_get_allow_untagged(struct pci_dev *pdev, int vf_id, + bool *on) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vf *vf; + int ret; + + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; } -error_param: - /* send the response to the VF */ - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_CONFIG_IRQ_MAP, - aq_ret); + + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto out; + vf = &pf->vf[vf_id]; + *on = vf->allow_untagged; +out: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; } /** - * i40e_vc_enable_queues_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_set_allow_untagged + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @on: on or off * - * called from the VF to enable all or specific queue(s) + * This functions allows or stops untagged packets + * on the VF. + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_enable_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_set_allow_untagged(struct pci_dev *pdev, int vf_id, + const bool on) { - struct virtchnl_queue_select *vqs = - (struct virtchnl_queue_select *)msg; - struct i40e_pf *pf = vf->pf; - u16 vsi_id = vqs->vsi_id; - i40e_status aq_ret = 0; + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; } - if (!i40e_vc_isvalid_vsi_id(vf, vsi_id)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + if (vsi->info.pvid) { + ret = -EINVAL; + goto out; } - - if ((0 == vqs->rx_queues) && (0 == vqs->tx_queues)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; + spin_lock_bh(&vsi->mac_filter_hash_lock); + if (!on) { + i40e_rm_vlan_all_mac(vsi, 0); + i40e_service_event_schedule(vsi->back); + } else { + ret = i40e_add_vlan_all_mac(vsi, 0); + i40e_service_event_schedule(vsi->back); } - - if (i40e_vsi_start_rings(pf->vsi[vf->lan_vsi_idx])) - aq_ret = I40E_ERR_TIMEOUT; -error_param: - /* send the response to the VF */ - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ENABLE_QUEUES, - aq_ret); + spin_unlock_bh(&vsi->mac_filter_hash_lock); + if (!ret) + vf->allow_untagged = on; +out: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); + return ret; } /** - * i40e_vc_disable_queues_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_get_loopback + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @enable: enable or disable * - * called from the VF to disable all or specific - * queue(s) + * This function checks loopback is enabled + * + * Returns 1 if enabled, 0 if disabled **/ -static int i40e_vc_disable_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_get_loopback(struct pci_dev *pdev, int vf_id, bool *enable) { - struct virtchnl_queue_select *vqs = - (struct virtchnl_queue_select *)msg; - struct i40e_pf *pf = vf->pf; - i40e_status aq_ret = 0; + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vf *vf; + int ret = 0; - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + *enable = vf->loopback; +err_out: + return ret; +} - if (!i40e_vc_isvalid_vsi_id(vf, vqs->vsi_id)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } +/** + * i40e_set_loopback + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @enable: enable or disable + * + * This function enables or disables loopback + * + * Returns 0 on success, negative on failure + **/ +static int i40e_set_loopback(struct pci_dev *pdev, int vf_id, + const bool enable) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; - if ((0 == vqs->rx_queues) && (0 == vqs->tx_queues)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + ret = i40e_configure_vf_loopback(vsi, vf_id, enable); + if (!ret) + vf->loopback = enable; +err_out: + return ret; +} - i40e_vsi_stop_rings(pf->vsi[vf->lan_vsi_idx]); +/** + * i40e_get_vlan_strip + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @enable: enable or disable + * + * This function checks if vlan stripping is enabled or not + * + * Returns 0 on success, negative on failure + **/ +static int i40e_get_vlan_strip(struct pci_dev *pdev, int vf_id, bool *enable) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vf *vf; + int ret; -error_param: - /* send the response to the VF */ - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DISABLE_QUEUES, - aq_ret); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + *enable = vf->vlan_stripping; +err_out: + return ret; } /** - * i40e_vc_get_stats_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_set_vlan_strip + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @enable: enable/disable * - * called from the VF to get vsi stats + * This function enables or disables VLAN stripping on a VF + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_get_stats_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_set_vlan_strip(struct pci_dev *pdev, int vf_id, + const bool enable) { - struct virtchnl_queue_select *vqs = - (struct virtchnl_queue_select *)msg; - struct i40e_pf *pf = vf->pf; - struct i40e_eth_stats stats; - i40e_status aq_ret = 0; + struct i40e_pf *pf = pci_get_drvdata(pdev); struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; - memset(&stats, 0, sizeof(struct i40e_eth_stats)); - - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + ret = i40e_configure_vf_vlan_stripping(vsi, vf_id, enable); + if (!ret) + vf->vlan_stripping = enable; +err_out: + return ret; +} - if (!i40e_vc_isvalid_vsi_id(vf, vqs->vsi_id)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } +/** + * i40e_reset_vf_stats + * @pdev: PCI device information struct + * @vf_id: VF identifier + * + * This function resets all the stats for the VF + * + * Returns 0 on success, negative on failure + **/ +static int i40e_reset_vf_stats(struct pci_dev *pdev, int vf_id) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret = 0; + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; vsi = pf->vsi[vf->lan_vsi_idx]; - if (!vsi) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } - i40e_update_eth_stats(vsi); - stats = vsi->eth_stats; + i40e_vsi_reset_stats(vsi); -error_param: - /* send the response back to the VF */ - return i40e_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS, aq_ret, - (u8 *)&stats, sizeof(stats)); +err_out: + return ret; } -/* If the VF is not trusted restrict the number of MAC/VLAN it can program */ -#define I40E_VC_MAX_MAC_ADDR_PER_VF 12 -#define I40E_VC_MAX_VLAN_PER_VF 8 +/** + * i40e_get_vf_bw_share + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @bw_share: bw share of the VF + * + * This function retrieves the bw share configured for the VF + * + * Returns 0 on success, negative on failure + **/ +static int i40e_get_vf_bw_share(struct pci_dev *pdev, int vf_id, u8 *bw_share) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vf *vf; + int ret = 0; + + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + if (vf->bw_share_applied) + *bw_share = vf->bw_share; + else + ret = -EINVAL; + +err_out: + return ret; +} /** - * i40e_check_vf_permission - * @vf: pointer to the VF info - * @macaddr: pointer to the MAC Address being checked + * i40e_store_vf_bw_share + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @bw_share: bw share of the VF + * + * This function stores bw share configured for the VF * - * Check if the VF has permission to add or delete unicast MAC address - * filters and return error code -EPERM if not. Then check if the - * address filter requested is broadcast or zero and if so return - * an invalid MAC address error code. + * Returns 0 on success, negative on failure **/ -static inline int i40e_check_vf_permission(struct i40e_vf *vf, u8 *macaddr) +static int i40e_store_vf_bw_share(struct pci_dev *pdev, int vf_id, u8 bw_share) { - struct i40e_pf *pf = vf->pf; + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vf *vf; int ret = 0; - if (is_broadcast_ether_addr(macaddr) || - is_zero_ether_addr(macaddr)) { - dev_err(&pf->pdev->dev, "invalid VF MAC addr %pM\n", macaddr); - ret = I40E_ERR_INVALID_MAC_ADDR; - } else if (vf->pf_set_mac && !is_multicast_ether_addr(macaddr) && - !test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) && - !ether_addr_equal(macaddr, vf->default_lan_addr.addr)) { - /* If the host VMM administrator has set the VF MAC address - * administratively via the ndo_set_vf_mac command then deny - * permission to the VF to add or delete unicast MAC addresses. - * Unless the VF is privileged and then it can do whatever. - * The VF may request to set the MAC address filter already - * assigned to it so do not return an error in that case. - */ - dev_err(&pf->pdev->dev, - "VF attempting to override administratively set MAC address, reload the VF driver to resume normal operation\n"); - ret = -EPERM; - } else if ((vf->num_mac >= I40E_VC_MAX_MAC_ADDR_PER_VF) && - !test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) { - dev_err(&pf->pdev->dev, - "VF is not trusted, switch the VF to trusted to add more functionality\n"); - ret = -EPERM; + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + vf->bw_share = bw_share; + + /* this tracking bool is set to true when 'apply' attribute is used */ + vf->bw_share_applied = false; + pf->vf_bw_applied = false; +err_out: + return ret; +} + +/** + * i40e_get_link_state + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @enabled: link state + * @link_speed: link speed of the VF + * + * Gets the status of link and the link speed + * + * Returns 0 on success, negative on failure + **/ +static int i40e_get_link_state(struct pci_dev *pdev, int vf_id, bool *enabled, + enum vfd_link_speed *link_speed) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_link_status *ls; + struct i40e_vf *vf; + int ret; + + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + ls = &pf->hw.phy.link_info; + if (vf->link_forced) + *enabled = vf->link_up; + else + *enabled = ls->link_info & I40E_AQ_LINK_UP; + switch (ls->link_speed) { + case I40E_LINK_SPEED_UNKNOWN: + *link_speed = VFD_LINK_SPEED_UNKNOWN; + break; + case I40E_LINK_SPEED_100MB: + *link_speed = VFD_LINK_SPEED_100MB; + break; + case I40E_LINK_SPEED_1GB: + *link_speed = VFD_LINK_SPEED_1GB; + break; + case I40E_LINK_SPEED_2_5GB: + *link_speed = VFD_LINK_SPEED_2_5GB; + break; + case I40E_LINK_SPEED_5GB: + *link_speed = VFD_LINK_SPEED_5GB; + break; + case I40E_LINK_SPEED_10GB: + *link_speed = VFD_LINK_SPEED_10GB; + break; + case I40E_LINK_SPEED_20GB: + *link_speed = VFD_LINK_SPEED_20GB; + break; + case I40E_LINK_SPEED_25GB: + *link_speed = VFD_LINK_SPEED_25GB; + break; + case I40E_LINK_SPEED_40GB: + *link_speed = VFD_LINK_SPEED_40GB; + break; + default: + *link_speed = VFD_LINK_SPEED_UNKNOWN; } +err_out: return ret; } /** - * i40e_vc_add_mac_addr_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_set_link_state + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @link: the link state to configure * - * add guest mac address filter + * Configures link for a vf + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_set_link_state(struct pci_dev *pdev, int vf_id, const u8 link) { - struct virtchnl_ether_addr_list *al = - (struct virtchnl_ether_addr_list *)msg; - struct i40e_pf *pf = vf->pf; - struct i40e_vsi *vsi = NULL; - u16 vsi_id = al->vsi_id; - i40e_status ret = 0; - int i; + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vf *vf; + int ret; - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || - !i40e_vc_isvalid_vsi_id(vf, vsi_id)) { - ret = I40E_ERR_PARAM; - goto error_param; - } + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto error_out; + vf = &pf->vf[vf_id]; + ret = i40e_configure_vf_link(vf, link); +error_out: + return ret; +} - for (i = 0; i < al->num_elements; i++) { - ret = i40e_check_vf_permission(vf, al->list[i].addr); - if (ret) - goto error_param; - } - vsi = pf->vsi[vf->lan_vsi_idx]; +/** + * i40e_set_vf_enable + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @enable: enable/disable + * + * This function enables or disables a VF + * + * Returns 0 on success, negative on failure + **/ +static int i40e_set_vf_enable(struct pci_dev *pdev, int vf_id, const bool enable) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + unsigned long q_map; + struct i40e_vf *vf; + int ret; - /* Lock once, because all function inside for loop accesses VSI's - * MAC filter list which needs to be protected using same lock. - */ - spin_lock_bh(&vsi->mac_filter_hash_lock); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + q_map = BIT(vsi->num_queue_pairs) - 1; - /* add new addresses to the list */ - for (i = 0; i < al->num_elements; i++) { - struct i40e_mac_filter *f; + /* force link down to prevent tx hangs */ + if (!enable) { + ret = i40e_set_link_state(pdev, vf_id, VFD_LINKSTATE_OFF); + if (ret) + goto err_out; + vf->pf_ctrl_disable = true; + ret = i40e_ctrl_vf_tx_rings(vsi, q_map, enable); + if (ret) + goto err_out; + ret = i40e_ctrl_vf_rx_rings(vsi, q_map, enable); + if (ret) + goto err_out; + vf->queues_enabled = false; + } else { + /* Do nothing when there is no iavf driver loaded */ + if (!test_bit(I40E_VF_STATE_LOADED_VF_DRIVER, &vf->vf_states)) + goto err_out; + ret = i40e_ctrl_vf_rx_rings(vsi, q_map, enable); + if (ret) + goto err_out; + ret = i40e_ctrl_vf_tx_rings(vsi, q_map, enable); + if (ret) + goto err_out; + vf->queues_enabled = true; + vf->pf_ctrl_disable = false; + /* reset need to reinit VF resources */ + i40e_vc_notify_vf_reset(vf); + i40e_reset_vf(vf, false); + ret = i40e_set_link_state(pdev, vf_id, VFD_LINKSTATE_AUTO); + } - f = i40e_find_mac(vsi, al->list[i].addr); - if (!f) - f = i40e_add_mac_filter(vsi, al->list[i].addr); +err_out: + return ret; +} - if (!f) { - dev_err(&pf->pdev->dev, - "Unable to add MAC filter %pM for VF %d\n", - al->list[i].addr, vf->vf_id); - ret = I40E_ERR_PARAM; - spin_unlock_bh(&vsi->mac_filter_hash_lock); - goto error_param; - } else { - vf->num_mac++; - } - } - spin_unlock_bh(&vsi->mac_filter_hash_lock); +static int i40e_get_vf_enable(struct pci_dev *pdev, int vf_id, bool *enable) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vf *vf; + int ret; - /* program the updated filter list */ - ret = i40e_sync_vsi_filters(vsi); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); if (ret) - dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters, error %d\n", - vf->vf_id, ret); - -error_param: - /* send the response to the VF */ - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ADD_ETH_ADDR, - ret); + return ret; + vf = &pf->vf[vf_id]; + *enable = !vf->pf_ctrl_disable; + return 0; } /** - * i40e_vc_del_mac_addr_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_get_rx_bytes + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @rx_bytes: pointer to the caller's rx_bytes variable * - * remove guest mac address filter + * This function gets the received bytes on the VF + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_get_rx_bytes(struct pci_dev *pdev, int vf_id, + u64 *rx_bytes) { - struct virtchnl_ether_addr_list *al = - (struct virtchnl_ether_addr_list *)msg; - struct i40e_pf *pf = vf->pf; - struct i40e_vsi *vsi = NULL; - u16 vsi_id = al->vsi_id; - i40e_status ret = 0; - int i; - - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || - !i40e_vc_isvalid_vsi_id(vf, vsi_id)) { - ret = I40E_ERR_PARAM; - goto error_param; - } + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; - for (i = 0; i < al->num_elements; i++) { - if (is_broadcast_ether_addr(al->list[i].addr) || - is_zero_ether_addr(al->list[i].addr)) { - dev_err(&pf->pdev->dev, "Invalid MAC addr %pM for VF %d\n", - al->list[i].addr, vf->vf_id); - ret = I40E_ERR_INVALID_MAC_ADDR; - goto error_param; - } - } + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; vsi = pf->vsi[vf->lan_vsi_idx]; + i40e_update_eth_stats(vsi); + *rx_bytes = vsi->eth_stats.rx_bytes; +err_out: + return ret; +} - spin_lock_bh(&vsi->mac_filter_hash_lock); - /* delete addresses from the list */ - for (i = 0; i < al->num_elements; i++) - if (i40e_del_mac_filter(vsi, al->list[i].addr)) { - ret = I40E_ERR_INVALID_MAC_ADDR; - spin_unlock_bh(&vsi->mac_filter_hash_lock); - goto error_param; - } else { - vf->num_mac--; - } - - spin_unlock_bh(&vsi->mac_filter_hash_lock); +/** + * i40e_get_rx_dropped + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @rx_dropped: pointer to the caller's rx_dropped variable + * + * This function gets the dropped received bytes on the VF + * + * Returns 0 on success, negative on failure + **/ +static int i40e_get_rx_dropped(struct pci_dev *pdev, int vf_id, + u64 *rx_dropped) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; - /* program the updated filter list */ - ret = i40e_sync_vsi_filters(vsi); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); if (ret) - dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters, error %d\n", - vf->vf_id, ret); - -error_param: - /* send the response to the VF */ - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DEL_ETH_ADDR, - ret); + goto err_out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + i40e_update_eth_stats(vsi); + *rx_dropped = vsi->eth_stats.rx_discards; +err_out: + return ret; } /** - * i40e_vc_add_vlan_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_get_rx_packets + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @rx_packets: pointer to the caller's rx_packets variable * - * program guest vlan id + * This function gets the number of packets received on the VF + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_add_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_get_rx_packets(struct pci_dev *pdev, int vf_id, + u64 *rx_packets) { - struct virtchnl_vlan_filter_list *vfl = - (struct virtchnl_vlan_filter_list *)msg; - struct i40e_pf *pf = vf->pf; - struct i40e_vsi *vsi = NULL; - u16 vsi_id = vfl->vsi_id; - i40e_status aq_ret = 0; - int i; - - if ((vf->num_vlan >= I40E_VC_MAX_VLAN_PER_VF) && - !test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) { - dev_err(&pf->pdev->dev, - "VF is not trusted, switch the VF to trusted to add more VLAN addresses\n"); - goto error_param; - } - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || - !i40e_vc_isvalid_vsi_id(vf, vsi_id)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; - for (i = 0; i < vfl->num_elements; i++) { - if (vfl->vlan_id[i] > I40E_MAX_VLANID) { - aq_ret = I40E_ERR_PARAM; - dev_err(&pf->pdev->dev, - "invalid VF VLAN id %d\n", vfl->vlan_id[i]); - goto error_param; - } - } + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; vsi = pf->vsi[vf->lan_vsi_idx]; - if (vsi->info.pvid) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } - - i40e_vlan_stripping_enable(vsi); - for (i = 0; i < vfl->num_elements; i++) { - /* add new VLAN filter */ - int ret = i40e_vsi_add_vlan(vsi, vfl->vlan_id[i]); - if (!ret) - vf->num_vlan++; - - if (test_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states)) - i40e_aq_set_vsi_uc_promisc_on_vlan(&pf->hw, vsi->seid, - true, - vfl->vlan_id[i], - NULL); - if (test_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states)) - i40e_aq_set_vsi_mc_promisc_on_vlan(&pf->hw, vsi->seid, - true, - vfl->vlan_id[i], - NULL); + i40e_update_eth_stats(vsi); + *rx_packets = vsi->eth_stats.rx_unicast + vsi->eth_stats.rx_multicast + + vsi->eth_stats.rx_broadcast; +err_out: + return ret; +} - if (ret) - dev_err(&pf->pdev->dev, - "Unable to add VLAN filter %d for VF %d, error %d\n", - vfl->vlan_id[i], vf->vf_id, ret); - } +/** + * i40e_get_tx_bytes + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @tx_bytes: pointer to the caller's tx_bytes variable + * + * This function gets the transmitted bytes by the VF + * + * Returns 0 on success, negative on failure + **/ +static int i40e_get_tx_bytes(struct pci_dev *pdev, int vf_id, + u64 *tx_bytes) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; -error_param: - /* send the response to the VF */ - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ADD_VLAN, aq_ret); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + i40e_update_eth_stats(vsi); + *tx_bytes = vsi->eth_stats.tx_bytes; +err_out: + return ret; } /** - * i40e_vc_remove_vlan_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_get_tx_dropped + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @tx_dropped: pointer to the caller's tx_dropped variable * - * remove programmed guest vlan id + * This function gets the dropped tx bytes by the VF + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_get_tx_dropped(struct pci_dev *pdev, int vf_id, + u64 *tx_dropped) { - struct virtchnl_vlan_filter_list *vfl = - (struct virtchnl_vlan_filter_list *)msg; - struct i40e_pf *pf = vf->pf; - struct i40e_vsi *vsi = NULL; - u16 vsi_id = vfl->vsi_id; - i40e_status aq_ret = 0; - int i; - - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || - !i40e_vc_isvalid_vsi_id(vf, vsi_id)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } - - for (i = 0; i < vfl->num_elements; i++) { - if (vfl->vlan_id[i] > I40E_MAX_VLANID) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } - } + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; vsi = pf->vsi[vf->lan_vsi_idx]; - if (vsi->info.pvid) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } - - for (i = 0; i < vfl->num_elements; i++) { - i40e_vsi_kill_vlan(vsi, vfl->vlan_id[i]); - vf->num_vlan--; - - if (test_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states)) - i40e_aq_set_vsi_uc_promisc_on_vlan(&pf->hw, vsi->seid, - false, - vfl->vlan_id[i], - NULL); - if (test_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states)) - i40e_aq_set_vsi_mc_promisc_on_vlan(&pf->hw, vsi->seid, - false, - vfl->vlan_id[i], - NULL); - } - -error_param: - /* send the response to the VF */ - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DEL_VLAN, aq_ret); + i40e_update_eth_stats(vsi); + *tx_dropped = vsi->eth_stats.tx_discards; +err_out: + return ret; } /** - * i40e_vc_iwarp_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_get_tx_packets + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @tx_packets: pointer to the caller's tx_packets variable * - * called from the VF for the iwarp msgs + * This function gets the number of packets transmitted by the VF + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_iwarp_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_get_tx_packets(struct pci_dev *pdev, int vf_id, + u64 *tx_packets) { - struct i40e_pf *pf = vf->pf; - int abs_vf_id = vf->vf_id + pf->hw.func_caps.vf_base_id; - i40e_status aq_ret = 0; - - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || - !test_bit(I40E_VF_STATE_IWARPENA, &vf->vf_states)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } - - i40e_notify_client_of_vf_msg(pf->vsi[pf->lan_vsi], abs_vf_id, - msg, msglen); + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; -error_param: - /* send the response to the VF */ - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_IWARP, - aq_ret); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + i40e_update_eth_stats(vsi); + *tx_packets = vsi->eth_stats.tx_unicast + vsi->eth_stats.tx_multicast + + vsi->eth_stats.tx_broadcast; +err_out: + return ret; } /** - * i40e_vc_iwarp_qvmap_msg - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length - * @config: config qvmap or release it + * i40e_get_tx_errors + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @tx_errors: pointer to the caller's tx_errors variable * - * called from the VF for the iwarp msgs + * This function gets the number of packets transmitted by the VF + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_iwarp_qvmap_msg(struct i40e_vf *vf, u8 *msg, u16 msglen, - bool config) +static int i40e_get_tx_errors(struct pci_dev *pdev, int vf_id, + u64 *tx_errors) { - struct virtchnl_iwarp_qvlist_info *qvlist_info = - (struct virtchnl_iwarp_qvlist_info *)msg; - i40e_status aq_ret = 0; - - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || - !test_bit(I40E_VF_STATE_IWARPENA, &vf->vf_states)) { - aq_ret = I40E_ERR_PARAM; - goto error_param; - } - - if (config) { - if (i40e_config_iwarp_qvlist(vf, qvlist_info)) - aq_ret = I40E_ERR_PARAM; - } else { - i40e_release_iwarp_qvlist(vf); - } + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; -error_param: - /* send the response to the VF */ - return i40e_vc_send_resp_to_vf(vf, - config ? VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP : - VIRTCHNL_OP_RELEASE_IWARP_IRQ_MAP, - aq_ret); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + i40e_update_eth_stats(vsi); + *tx_errors = vsi->eth_stats.tx_errors; +err_out: + return ret; } /** - * i40e_vc_config_rss_key - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_get_mac + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @mac: the default mac address * - * Configure the VF's RSS key + * This function gets the default mac address + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_config_rss_key(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_get_mac(struct pci_dev *pdev, int vf_id, u8 *mac) { - struct virtchnl_rss_key *vrk = - (struct virtchnl_rss_key *)msg; - struct i40e_pf *pf = vf->pf; - struct i40e_vsi *vsi = NULL; - u16 vsi_id = vrk->vsi_id; - i40e_status aq_ret = 0; - - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || - !i40e_vc_isvalid_vsi_id(vf, vsi_id) || - (vrk->key_len != I40E_HKEY_ARRAY_SIZE)) { - aq_ret = I40E_ERR_PARAM; - goto err; - } + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vf *vf; + int ret; - vsi = pf->vsi[vf->lan_vsi_idx]; - aq_ret = i40e_config_rss(vsi, vrk->key, NULL, 0); -err: - /* send the response to the VF */ - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_CONFIG_RSS_KEY, - aq_ret); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + ether_addr_copy(mac, vf->default_lan_addr.addr); +err_out: + return ret; } /** - * i40e_vc_config_rss_lut - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_set_mac + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @mac: the default mac address to set * - * Configure the VF's RSS LUT + * This function sets the default mac address for the VF + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_config_rss_lut(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_set_mac(struct pci_dev *pdev, int vf_id, const u8 *mac) { - struct virtchnl_rss_lut *vrl = - (struct virtchnl_rss_lut *)msg; - struct i40e_pf *pf = vf->pf; - struct i40e_vsi *vsi = NULL; - u16 vsi_id = vrl->vsi_id; - i40e_status aq_ret = 0; - - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || - !i40e_vc_isvalid_vsi_id(vf, vsi_id) || - (vrl->lut_entries != I40E_VF_HLUT_ARRAY_SIZE)) { - aq_ret = I40E_ERR_PARAM; - goto err; - } + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; vsi = pf->vsi[vf->lan_vsi_idx]; - aq_ret = i40e_config_rss(vsi, NULL, vrl->lut, I40E_VF_HLUT_ARRAY_SIZE); - /* send the response to the VF */ -err: - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_CONFIG_RSS_LUT, - aq_ret); + ret = i40e_set_vf_mac(vf, vsi, mac); +err_out: + return ret; } /** - * i40e_vc_get_rss_hena - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_get_promisc + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @promisc_mode: current promiscuous mode * - * Return the RSS HENA bits allowed by the hardware + * This function gets the current promiscuous mode configuration. + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_get_rss_hena(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_get_promisc(struct pci_dev *pdev, int vf_id, u8 *promisc_mode) { - struct virtchnl_rss_hena *vrh = NULL; - struct i40e_pf *pf = vf->pf; - i40e_status aq_ret = 0; - int len = 0; - - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { - aq_ret = I40E_ERR_PARAM; - goto err; - } - len = sizeof(struct virtchnl_rss_hena); + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vf *vf; + int ret; - vrh = kzalloc(len, GFP_KERNEL); - if (!vrh) { - aq_ret = I40E_ERR_NO_MEMORY; - len = 0; - goto err; - } - vrh->hena = i40e_pf_get_default_rss_hena(pf); -err: - /* send the response back to the VF */ - aq_ret = i40e_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_RSS_HENA_CAPS, - aq_ret, (u8 *)vrh, len); - kfree(vrh); - return aq_ret; + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + *promisc_mode = vf->promisc_mode; +err_out: + return ret; } /** - * i40e_vc_set_rss_hena - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_set_promisc + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @promisc_mode: promiscuous mode to be set * - * Set the RSS HENA bits for the VF + * This function sets the promiscuous mode configuration. + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_set_rss_hena(struct i40e_vf *vf, u8 *msg, u16 msglen) +static int i40e_set_promisc(struct pci_dev *pdev, int vf_id, + const u8 promisc_mode) { - struct virtchnl_rss_hena *vrh = - (struct virtchnl_rss_hena *)msg; - struct i40e_pf *pf = vf->pf; - struct i40e_hw *hw = &pf->hw; - i40e_status aq_ret = 0; + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret; - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { - aq_ret = I40E_ERR_PARAM; + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) goto err; - } - i40e_write_rx_ctl(hw, I40E_VFQF_HENA1(0, vf->vf_id), (u32)vrh->hena); - i40e_write_rx_ctl(hw, I40E_VFQF_HENA1(1, vf->vf_id), - (u32)(vrh->hena >> 32)); - - /* send the response to the VF */ + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + ret = i40e_configure_vf_promisc_mode(vf, vsi, promisc_mode); err: - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_SET_RSS_HENA, aq_ret); + return ret; } /** - * i40e_vc_enable_vlan_stripping - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_get_ingress_mirror - Gets the configured ingress mirror + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @mirror: pointer to return the ingress mirror * - * Enable vlan header stripping for the VF + * Gets the ingress mirror configured + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_enable_vlan_stripping(struct i40e_vf *vf, u8 *msg, - u16 msglen) +static int i40e_get_ingress_mirror(struct pci_dev *pdev, int vf_id, int *mirror) { - struct i40e_vsi *vsi = vf->pf->vsi[vf->lan_vsi_idx]; - i40e_status aq_ret = 0; - - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { - aq_ret = I40E_ERR_PARAM; - goto err; - } - - i40e_vlan_stripping_enable(vsi); + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vf *vf; + int ret; - /* send the response to the VF */ -err: - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ENABLE_VLAN_STRIPPING, - aq_ret); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + *mirror = vf->ingress_vlan; +err_out: + return ret; } /** - * i40e_vc_disable_vlan_stripping - * @vf: pointer to the VF info - * @msg: pointer to the msg buffer - * @msglen: msg length + * i40e_set_ingress_mirror - Configure ingress mirror + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @mirror: mirror vf * - * Disable vlan header stripping for the VF + * Configures the ingress mirror + * + * Returns 0 on success, negative on failure **/ -static int i40e_vc_disable_vlan_stripping(struct i40e_vf *vf, u8 *msg, - u16 msglen) +static int i40e_set_ingress_mirror(struct pci_dev *pdev, int vf_id, + const int mirror) { - struct i40e_vsi *vsi = vf->pf->vsi[vf->lan_vsi_idx]; - i40e_status aq_ret = 0; + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *src_vsi, *mirror_vsi; + struct i40e_vf *vf, *mirror_vf; + u16 rule_type, rule_id; + int ret; - if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { - aq_ret = I40E_ERR_PARAM; - goto err; - } + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; - i40e_vlan_stripping_disable(vsi); + /* The Admin Queue mirroring rules refer to the traffic + * directions from the perspective of the switch, not the VSI + * we apply the mirroring rule on - so the behaviour of a VSI + * ingress mirror is classified as an egress rule + */ + rule_type = I40E_AQC_MIRROR_RULE_TYPE_VPORT_EGRESS; + src_vsi = pf->vsi[vf->lan_vsi_idx]; + if (mirror == I40E_NO_VF_MIRROR) { + /* Del mirrors */ + rule_id = vf->ingress_rule_id; + ret = i40e_del_ingress_egress_mirror(src_vsi, rule_type, + rule_id); + if (ret) + goto err_out; + vf->ingress_vlan = I40E_NO_VF_MIRROR; + } else { + /* validate the mirror */ + ret = i40e_validate_vf(pf, mirror); + if (ret) + goto err_out; + mirror_vf = &pf->vf[mirror]; + mirror_vsi = pf->vsi[mirror_vf->lan_vsi_idx]; - /* send the response to the VF */ -err: - return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DISABLE_VLAN_STRIPPING, - aq_ret); + /* Add mirrors */ + ret = i40e_add_ingress_egress_mirror(src_vsi, mirror_vsi, + rule_type, &rule_id); + if (ret) + goto err_out; + vf->ingress_vlan = mirror; + vf->ingress_rule_id = rule_id; + } +err_out: + return ret; } /** - * i40e_vc_process_vf_msg - * @pf: pointer to the PF structure - * @vf_id: source VF id - * @msg: pointer to the msg buffer - * @msglen: msg length - * @msghndl: msg handle + * i40e_get_egress_mirror - Gets the configured egress mirror + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @mirror: pointer to return the egress mirror * - * called from the common aeq/arq handler to - * process request from VF + * Gets the egress mirror configured + * + * Returns 0 on success, negative on failure **/ -int i40e_vc_process_vf_msg(struct i40e_pf *pf, s16 vf_id, u32 v_opcode, - u32 v_retval, u8 *msg, u16 msglen) +static int i40e_get_egress_mirror(struct pci_dev *pdev, int vf_id, int *mirror) { - struct i40e_hw *hw = &pf->hw; - int local_vf_id = vf_id - (s16)hw->func_caps.vf_base_id; + struct i40e_pf *pf = pci_get_drvdata(pdev); struct i40e_vf *vf; int ret; - pf->vf_aq_requests++; - if (local_vf_id >= pf->num_alloc_vfs) - return -EINVAL; - vf = &(pf->vf[local_vf_id]); - - /* Check if VF is disabled. */ - if (test_bit(I40E_VF_STATE_DISABLED, &vf->vf_states)) - return I40E_ERR_PARAM; - - /* perform basic checks on the msg */ - ret = virtchnl_vc_validate_vf_msg(&vf->vf_ver, v_opcode, msg, msglen); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; + *mirror = vf->egress_vlan; +err_out: + return ret; +} - /* perform additional checks specific to this driver */ - if (v_opcode == VIRTCHNL_OP_CONFIG_RSS_KEY) { - struct virtchnl_rss_key *vrk = (struct virtchnl_rss_key *)msg; +/** + * i40e_set_egress_mirror - Configure egress mirror + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @mirror: mirror vf + * + * Configures the egress mirror + * + * Returns 0 on success, negative on failure + **/ +static int i40e_set_egress_mirror(struct pci_dev *pdev, int vf_id, + const int mirror) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *src_vsi, *mirror_vsi; + struct i40e_vf *vf, *mirror_vf; + u16 rule_type, rule_id; + int ret; - if (vrk->key_len != I40E_HKEY_ARRAY_SIZE) - ret = -EINVAL; - } else if (v_opcode == VIRTCHNL_OP_CONFIG_RSS_LUT) { - struct virtchnl_rss_lut *vrl = (struct virtchnl_rss_lut *)msg; + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto err_out; + vf = &pf->vf[vf_id]; - if (vrl->lut_entries != I40E_VF_HLUT_ARRAY_SIZE) - ret = -EINVAL; - } + /* The Admin Queue mirroring rules refer to the traffic + * directions from the perspective of the switch, not the VSI + * we apply the mirroring rule on - so the behaviour of a VSI + * egress mirror is classified as an ingress rule + */ + rule_type = I40E_AQC_MIRROR_RULE_TYPE_VPORT_INGRESS; + src_vsi = pf->vsi[vf->lan_vsi_idx]; + if (mirror == I40E_NO_VF_MIRROR) { + /* Del mirrors */ + rule_id = vf->egress_rule_id; + ret = i40e_del_ingress_egress_mirror(src_vsi, rule_type, + rule_id); + if (ret) + goto err_out; + vf->egress_vlan = I40E_NO_VF_MIRROR; + } else { + /* validate the mirror */ + ret = i40e_validate_vf(pf, mirror); + if (ret) + goto err_out; + mirror_vf = &pf->vf[mirror]; + mirror_vsi = pf->vsi[mirror_vf->lan_vsi_idx]; - if (ret) { - i40e_vc_send_resp_to_vf(vf, v_opcode, I40E_ERR_PARAM); - dev_err(&pf->pdev->dev, "Invalid message from VF %d, opcode %d, len %d\n", - local_vf_id, v_opcode, msglen); - switch (ret) { - case VIRTCHNL_ERR_PARAM: - return -EPERM; - default: - return -EINVAL; - } + /* Add mirrors */ + ret = i40e_add_ingress_egress_mirror(src_vsi, mirror_vsi, + rule_type, &rule_id); + if (ret) + goto err_out; + vf->egress_vlan = mirror; + vf->egress_rule_id = rule_id; } +err_out: + return ret; +} - switch (v_opcode) { - case VIRTCHNL_OP_VERSION: - ret = i40e_vc_get_version_msg(vf, msg); - break; - case VIRTCHNL_OP_GET_VF_RESOURCES: - ret = i40e_vc_get_vf_resources_msg(vf, msg); - break; - case VIRTCHNL_OP_RESET_VF: - i40e_vc_reset_vf_msg(vf); - ret = 0; - break; - case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE: - ret = i40e_vc_config_promiscuous_mode_msg(vf, msg, msglen); - break; - case VIRTCHNL_OP_CONFIG_VSI_QUEUES: - ret = i40e_vc_config_queues_msg(vf, msg, msglen); - break; - case VIRTCHNL_OP_CONFIG_IRQ_MAP: - ret = i40e_vc_config_irq_map_msg(vf, msg, msglen); - break; - case VIRTCHNL_OP_ENABLE_QUEUES: - ret = i40e_vc_enable_queues_msg(vf, msg, msglen); - i40e_vc_notify_vf_link_state(vf); - break; - case VIRTCHNL_OP_DISABLE_QUEUES: - ret = i40e_vc_disable_queues_msg(vf, msg, msglen); - break; - case VIRTCHNL_OP_ADD_ETH_ADDR: - ret = i40e_vc_add_mac_addr_msg(vf, msg, msglen); - break; - case VIRTCHNL_OP_DEL_ETH_ADDR: - ret = i40e_vc_del_mac_addr_msg(vf, msg, msglen); - break; - case VIRTCHNL_OP_ADD_VLAN: - ret = i40e_vc_add_vlan_msg(vf, msg, msglen); - break; - case VIRTCHNL_OP_DEL_VLAN: - ret = i40e_vc_remove_vlan_msg(vf, msg, msglen); - break; - case VIRTCHNL_OP_GET_STATS: - ret = i40e_vc_get_stats_msg(vf, msg, msglen); - break; - case VIRTCHNL_OP_IWARP: - ret = i40e_vc_iwarp_msg(vf, msg, msglen); - break; - case VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP: - ret = i40e_vc_iwarp_qvmap_msg(vf, msg, msglen, true); - break; - case VIRTCHNL_OP_RELEASE_IWARP_IRQ_MAP: - ret = i40e_vc_iwarp_qvmap_msg(vf, msg, msglen, false); - break; - case VIRTCHNL_OP_CONFIG_RSS_KEY: - ret = i40e_vc_config_rss_key(vf, msg, msglen); - break; - case VIRTCHNL_OP_CONFIG_RSS_LUT: - ret = i40e_vc_config_rss_lut(vf, msg, msglen); - break; - case VIRTCHNL_OP_GET_RSS_HENA_CAPS: - ret = i40e_vc_get_rss_hena(vf, msg, msglen); - break; - case VIRTCHNL_OP_SET_RSS_HENA: - ret = i40e_vc_set_rss_hena(vf, msg, msglen); - break; - case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING: - ret = i40e_vc_enable_vlan_stripping(vf, msg, msglen); - break; - case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING: - ret = i40e_vc_disable_vlan_stripping(vf, msg, msglen); - break; +/* + * i40e_get_mac_list + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @list_head: list of mac addresses + * + * This function returns the list of mac address configured on the VF. It is + * the responsibility of the caller to free the allocated list when finished. + * + * Returns 0 on success, negative on failure + */ +static int i40e_get_mac_list(struct pci_dev *pdev, int vf_id, + struct list_head *mac_list) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_mac_filter *f; + struct vfd_macaddr *elem; + struct i40e_vsi *vsi; + struct i40e_vf *vf; + int ret, bkt; - case VIRTCHNL_OP_UNKNOWN: - default: - dev_err(&pf->pdev->dev, "Unsupported opcode %d from VF %d\n", - v_opcode, local_vf_id); - ret = i40e_vc_send_resp_to_vf(vf, v_opcode, - I40E_ERR_NOT_IMPLEMENTED); - break; + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto error_out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + spin_lock_bh(&vsi->mac_filter_hash_lock); + hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { + elem = kzalloc(sizeof(*elem), GFP_KERNEL); + if (!elem) { + ret = -ENOMEM; + goto error_unlock; + } + INIT_LIST_HEAD(&elem->list); + ether_addr_copy(elem->mac, f->macaddr); + list_add_tail(&elem->list, mac_list); } - +error_unlock: + spin_unlock_bh(&vsi->mac_filter_hash_lock); +error_out: return ret; } -/** - * i40e_vc_process_vflr_event - * @pf: pointer to the PF structure +/* + * i40e_add_macs_to_list + * @pdev: PCI device information struct + * @vf_id: VF identifier + * @list_head: list of mac addresses * - * called from the vlfr irq handler to - * free up VF resources and state variables - **/ -int i40e_vc_process_vflr_event(struct i40e_pf *pf) + * This function adds a list of mac addresses for a VF + * + * Returns 0 on success, negative on failure + */ +static int i40e_add_macs_to_list(struct pci_dev *pdev, int vf_id, + struct list_head *mac_list) { - struct i40e_hw *hw = &pf->hw; - u32 reg, reg_idx, bit_idx; + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct vfd_macaddr *tmp; + struct i40e_vsi *vsi; struct i40e_vf *vf; - int vf_id; - - if (!test_bit(__I40E_VFLR_EVENT_PENDING, pf->state)) - return 0; + int ret; - /* Re-enable the VFLR interrupt cause here, before looking for which - * VF got reset. Otherwise, if another VF gets a reset while the - * first one is being processed, that interrupt will be lost, and - * that VF will be stuck in reset forever. - */ - reg = rd32(hw, I40E_PFINT_ICR0_ENA); - reg |= I40E_PFINT_ICR0_ENA_VFLR_MASK; - wr32(hw, I40E_PFINT_ICR0_ENA, reg); - i40e_flush(hw); + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto error_out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + spin_lock_bh(&vsi->mac_filter_hash_lock); + list_for_each_entry(tmp, mac_list, list) { + struct i40e_mac_filter *f; - clear_bit(__I40E_VFLR_EVENT_PENDING, pf->state); - for (vf_id = 0; vf_id < pf->num_alloc_vfs; vf_id++) { - reg_idx = (hw->func_caps.vf_base_id + vf_id) / 32; - bit_idx = (hw->func_caps.vf_base_id + vf_id) % 32; - /* read GLGEN_VFLRSTAT register to find out the flr VFs */ - vf = &pf->vf[vf_id]; - reg = rd32(hw, I40E_GLGEN_VFLRSTAT(reg_idx)); - if (reg & BIT(bit_idx)) - /* i40e_reset_vf will clear the bit in GLGEN_VFLRSTAT */ - i40e_reset_vf(vf, true); + f = i40e_find_mac(vsi, tmp->mac); + if (!f) { + f = i40e_add_mac_filter(vsi, tmp->mac); + if (!f) { + dev_err(&pf->pdev->dev, + "Unable to add MAC filter %pM for VF %d\n", + tmp->mac, vf->vf_id); + ret = I40E_ERR_PARAM; + spin_unlock_bh(&vsi->mac_filter_hash_lock); + goto error_out; + } + } } + spin_unlock_bh(&vsi->mac_filter_hash_lock); - return 0; + /* program the updated filter list */ + ret = i40e_sync_vsi_filters(vsi); + if (ret) + dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters, error %d\n", + vf->vf_id, ret); +error_out: + return ret; } -/** - * i40e_ndo_set_vf_mac - * @netdev: network interface device structure +/* + * i40e_rem_macs_from_list + * @pdev: PCI device information struct * @vf_id: VF identifier - * @mac: mac address + * @list_head: list of mac addresses * - * program VF mac address - **/ -int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) + * This function removes a list of mac addresses from a VF + * + * Returns 0 on success, negative on failure + */ +static int i40e_rem_macs_from_list(struct pci_dev *pdev, int vf_id, + struct list_head *mac_list) { - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_vsi *vsi = np->vsi; - struct i40e_pf *pf = vsi->back; - struct i40e_mac_filter *f; + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct vfd_macaddr *tmp; + struct i40e_vsi *vsi; struct i40e_vf *vf; - int ret = 0; - int bkt; + int ret; /* validate the request */ - if (vf_id >= pf->num_alloc_vfs) { - dev_err(&pf->pdev->dev, - "Invalid VF Identifier %d\n", vf_id); - ret = -EINVAL; - goto error_param; - } - - vf = &(pf->vf[vf_id]); + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto error_out; + vf = &pf->vf[vf_id]; vsi = pf->vsi[vf->lan_vsi_idx]; - if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { - dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", - vf_id); - ret = -EAGAIN; - goto error_param; + spin_lock_bh(&vsi->mac_filter_hash_lock); + list_for_each_entry(tmp, mac_list, list) { + if (i40e_del_mac_filter(vsi, tmp->mac)) { + ret = I40E_ERR_INVALID_MAC_ADDR; + spin_unlock_bh(&vsi->mac_filter_hash_lock); + goto error_out; + } } + spin_unlock_bh(&vsi->mac_filter_hash_lock); - if (is_multicast_ether_addr(mac)) { - dev_err(&pf->pdev->dev, - "Invalid Ethernet address %pM for VF %d\n", mac, vf_id); - ret = -EINVAL; - goto error_param; - } + /* program the updated filter list */ + ret = i40e_sync_vsi_filters(vsi); + if (ret) + dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters, error %d\n", + vf->vf_id, ret); +error_out: + return ret; +} - /* Lock once because below invoked function add/del_filter requires - * mac_filter_hash_lock to be held - */ - spin_lock_bh(&vsi->mac_filter_hash_lock); +/* + * i40e_set_pf_qos_apply + * @pdev: PCI device information struct + * + * This function applies the bw shares stored across all VFs + * + * Returns 0 on success, negative on failure + */ +static int i40e_set_pf_qos_apply(struct pci_dev *pdev) +{ + struct i40e_aqc_configure_vsi_tc_bw_data bw_data; + struct i40e_pf *pf = pci_get_drvdata(pdev); + int i, j, ret = 0, total_share = 0; + struct i40e_vf *vf = pf->vf; + struct i40e_vsi *vsi; - /* delete the temporary mac address */ - if (!is_zero_ether_addr(vf->default_lan_addr.addr)) - i40e_del_mac_filter(vsi, vf->default_lan_addr.addr); + for (i = 0; i < pf->num_alloc_vfs; i++, vf++) { + total_share += vf->bw_share; + } - /* Delete all the filters for this VSI - we're going to kill it - * anyway. - */ - hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) - __i40e_del_filter(vsi, f); + /* verify BW share distribution */ + if (total_share > 100) { + dev_err(&pdev->dev, "Total share is greater than 100 percent"); + return I40E_ERR_PARAM; + } - spin_unlock_bh(&vsi->mac_filter_hash_lock); + memset(&bw_data, 0, sizeof(struct i40e_aqc_configure_vsi_tc_bw_data)); + for (i = 0; i < pf->num_alloc_vfs; i++) { + ret = i40e_validate_vf(pf, vf->vf_id); + if (ret) + continue; + vf = &pf->vf[i]; + if (!vf->bw_share) + continue; + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { + dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", + vf->vf_id); + ret = I40E_ERR_PARAM; + goto error_param; + } + vsi = pf->vsi[vf->lan_vsi_idx]; + bw_data.tc_valid_bits = 1; + bw_data.tc_bw_credits[0] = vf->bw_share; - /* program mac filter */ - if (i40e_sync_vsi_filters(vsi)) { - dev_err(&pf->pdev->dev, "Unable to program ucast filters\n"); - ret = -EIO; - goto error_param; - } - ether_addr_copy(vf->default_lan_addr.addr, mac); + ret = i40e_aq_config_vsi_tc_bw(&pf->hw, vsi->seid, &bw_data, NULL); + if (ret) { + dev_info(&pf->pdev->dev, + "AQ command Config VSI BW allocation per TC failed = %d\n", + pf->hw.aq.asq_last_status); + vf->bw_share_applied = false; + return -EINVAL; + } - if (is_zero_ether_addr(mac)) { - vf->pf_set_mac = false; - dev_info(&pf->pdev->dev, "Removing MAC on VF %d\n", vf_id); - } else { - vf->pf_set_mac = true; - dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", - mac, vf_id); - } + for (j = 0; j < I40E_MAX_TRAFFIC_CLASS; j++) + vsi->info.qs_handle[j] = bw_data.qs_handles[j]; - /* Force the VF driver stop so it has to reload with new MAC address */ - i40e_vc_disable_vf(pf, vf); - dev_info(&pf->pdev->dev, "Reload the VF driver to make this change effective.\n"); + /* set the tracking bool to true */ + vf->bw_share_applied = true; + } + pf->vf_bw_applied = true; error_param: return ret; } /** - * i40e_ndo_set_vf_port_vlan - * @netdev: network interface device structure - * @vf_id: VF identifier - * @vlan_id: mac address - * @qos: priority setting - * @vlan_proto: vlan protocol + * i40e_get_pf_ingress_mirror - Gets the configured ingress mirror for PF + * @pdev: PCI device information struct + * @mirror: pointer to return the ingress mirror * - * program VF vlan id and/or qos + * Gets the ingress mirror configured + * + * Returns 0 on success, negative on failure **/ -int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, - u16 vlan_id, u8 qos, __be16 vlan_proto) +static int i40e_get_pf_ingress_mirror(struct pci_dev *pdev, int *mirror) { - u16 vlanprio = vlan_id | (qos << I40E_VLAN_PRIORITY_SHIFT); - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_pf *pf = np->vsi->back; - struct i40e_vsi *vsi; - struct i40e_vf *vf; - int ret = 0; + struct i40e_pf *pf = pci_get_drvdata(pdev); + *mirror = pf->ingress_vlan; + return 0; +} - /* validate the request */ - if (vf_id >= pf->num_alloc_vfs) { - dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id); - ret = -EINVAL; - goto error_pvid; - } +/** + * i40e_set_pf_ingress_mirror - Sets the configured ingress mirror for PF + * @pdev: PCI device information struct + * @mirror: pointer to return the ingress mirror + * + * Gets the ingress mirror configured + * + * Returns 0 on success, negative on failure + **/ +static int i40e_set_pf_ingress_mirror(struct pci_dev *pdev, const int mirror) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *src_vsi, *mirror_vsi; + struct i40e_vf *mirror_vf; + u16 rule_type, rule_id; + int ret; - if ((vlan_id > I40E_MAX_VLANID) || (qos > 7)) { - dev_err(&pf->pdev->dev, "Invalid VF Parameters\n"); - ret = -EINVAL; - goto error_pvid; - } + /* The Admin Queue mirroring rules refer to the traffic + * directions from the perspective of the switch, not the VSI + * we apply the mirroring rule on - so the behaviour of a VSI + * ingress mirror is classified as an egress rule + */ + rule_type = I40E_AQC_MIRROR_RULE_TYPE_VPORT_EGRESS; + src_vsi = pf->vsi[pf->lan_vsi]; + if (mirror == I40E_NO_VF_MIRROR) { + /* Del mirrors */ + rule_id = pf->ingress_rule_id; + ret = i40e_del_ingress_egress_mirror(src_vsi, rule_type, + rule_id); + if (ret) + goto err_out; + pf->ingress_vlan = I40E_NO_VF_MIRROR; + } else { + /* validate the mirror */ + ret = i40e_validate_vf(pf, mirror); + if (ret) + goto err_out; + mirror_vf = &pf->vf[mirror]; + mirror_vsi = pf->vsi[mirror_vf->lan_vsi_idx]; - if (vlan_proto != htons(ETH_P_8021Q)) { - dev_err(&pf->pdev->dev, "VF VLAN protocol is not supported\n"); - ret = -EPROTONOSUPPORT; - goto error_pvid; + /* Add mirrors */ + ret = i40e_add_ingress_egress_mirror(src_vsi, mirror_vsi, + rule_type, &rule_id); + if (ret) + goto err_out; + pf->ingress_vlan = mirror; + pf->ingress_rule_id = rule_id; } +err_out: + return ret; +} - vf = &(pf->vf[vf_id]); - vsi = pf->vsi[vf->lan_vsi_idx]; - if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { - dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", - vf_id); - ret = -EAGAIN; - goto error_pvid; - } +/** + * i40e_get_pf_egress_mirror - Gets the configured egress mirror for PF + * @pdev: PCI device information struct + * @mirror: pointer to return the ingress mirror + * + * Gets the ingress mirror configured + * + * Returns 0 on success, negative on failure + **/ +static int i40e_get_pf_egress_mirror(struct pci_dev *pdev, int *mirror) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + *mirror = pf->egress_vlan; + return 0; +} - if (le16_to_cpu(vsi->info.pvid) == vlanprio) - /* duplicate request, so just return success */ - goto error_pvid; +/** + * i40e_set_pf_egress_mirror - Sets the configured egress mirror for PF + * @pdev: PCI device information struct + * @mirror: pointer to return the ingress mirror + * + * Gets the ingress mirror configured + * + * Returns 0 on success, negative on failure + **/ +static int i40e_set_pf_egress_mirror(struct pci_dev *pdev, const int mirror) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *src_vsi, *mirror_vsi; + struct i40e_vf *mirror_vf; + u16 rule_type, rule_id; + int ret; - /* Locked once because multiple functions below iterate list */ - spin_lock_bh(&vsi->mac_filter_hash_lock); + /* The Admin Queue mirroring rules refer to the traffic + * directions from the perspective of the switch, not the VSI + * we apply the mirroring rule on - so the behaviour of a VSI + * egress mirror is classified as an ingress rule + */ + rule_type = I40E_AQC_MIRROR_RULE_TYPE_VPORT_INGRESS; + src_vsi = pf->vsi[pf->lan_vsi]; + if (mirror == I40E_NO_VF_MIRROR) { + /* Del mirrors */ + rule_id = pf->egress_rule_id; + ret = i40e_del_ingress_egress_mirror(src_vsi, rule_type, + rule_id); + if (ret) + goto err_out; + pf->egress_vlan = I40E_NO_VF_MIRROR; + } else { + /* validate the mirror */ + ret = i40e_validate_vf(pf, mirror); + if (ret) + goto err_out; + mirror_vf = &pf->vf[mirror]; + mirror_vsi = pf->vsi[mirror_vf->lan_vsi_idx]; - if (le16_to_cpu(vsi->info.pvid) == 0 && i40e_is_vsi_in_vlan(vsi)) { - dev_err(&pf->pdev->dev, - "VF %d has already configured VLAN filters and the administrator is requesting a port VLAN override.\nPlease unload and reload the VF driver for this change to take effect.\n", - vf_id); - /* Administrator Error - knock the VF offline until he does - * the right thing by reconfiguring his network correctly - * and then reloading the VF driver. - */ - i40e_vc_disable_vf(pf, vf); - /* During reset the VF got a new VSI, so refresh the pointer. */ - vsi = pf->vsi[vf->lan_vsi_idx]; + /* Add mirrors */ + ret = i40e_add_ingress_egress_mirror(src_vsi, mirror_vsi, + rule_type, &rule_id); + if (ret) + goto err_out; + pf->egress_vlan = mirror; + pf->egress_rule_id = rule_id; } +err_out: + return ret; +} - /* Check for condition where there was already a port VLAN ID - * filter set and now it is being deleted by setting it to zero. - * Additionally check for the condition where there was a port - * VLAN but now there is a new and different port VLAN being set. - * Before deleting all the old VLAN filters we must add new ones - * with -1 (I40E_VLAN_ANY) or otherwise we're left with all our - * MAC addresses deleted. - */ - if ((!(vlan_id || qos) || - vlanprio != le16_to_cpu(vsi->info.pvid)) && - vsi->info.pvid) { - ret = i40e_add_vlan_all_mac(vsi, I40E_VLAN_ANY); - if (ret) { - dev_info(&vsi->back->pdev->dev, - "add VF VLAN failed, ret=%d aq_err=%d\n", ret, - vsi->back->hw.aq.asq_last_status); - spin_unlock_bh(&vsi->mac_filter_hash_lock); - goto error_pvid; - } - } +#define I40E_GL_SWT_L2TAGCTRL(_i) (0x001C0A70 + ((_i) * 4)) +#define I40E_GL_SWT_L2TAGCTRL_ETHERTYPE_SHIFT 16 +#define OUTER_TAG_IDX 2 +static int i40e_get_pf_tpid(struct pci_dev *pdev, u16 *tp_id) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); - if (vsi->info.pvid) { - /* remove all filters on the old VLAN */ - i40e_rm_vlan_all_mac(vsi, (le16_to_cpu(vsi->info.pvid) & - VLAN_VID_MASK)); - } + if (!(pf->hw.flags & I40E_HW_FLAG_802_1AD_CAPABLE)) + return -EOPNOTSUPP; - spin_unlock_bh(&vsi->mac_filter_hash_lock); - if (vlan_id || qos) - ret = i40e_vsi_add_pvid(vsi, vlanprio); - else - i40e_vsi_remove_pvid(vsi); - spin_lock_bh(&vsi->mac_filter_hash_lock); + *tp_id = (u16)(rd32(&pf->hw, I40E_GL_SWT_L2TAGCTRL(OUTER_TAG_IDX)) >> + I40E_GL_SWT_L2TAGCTRL_ETHERTYPE_SHIFT); - if (vlan_id) { - dev_info(&pf->pdev->dev, "Setting VLAN %d, QOS 0x%x on VF %d\n", - vlan_id, qos, vf_id); + return 0; +} - /* add new VLAN filter for each MAC */ - ret = i40e_add_vlan_all_mac(vsi, vlan_id); - if (ret) { - dev_info(&vsi->back->pdev->dev, - "add VF VLAN failed, ret=%d aq_err=%d\n", ret, - vsi->back->hw.aq.asq_last_status); - spin_unlock_bh(&vsi->mac_filter_hash_lock); - goto error_pvid; - } +static int i40e_set_pf_tpid(struct pci_dev *pdev, u16 tp_id) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + int ret; - /* remove the previously added non-VLAN MAC filters */ - i40e_rm_vlan_all_mac(vsi, I40E_VLAN_ANY); + if (!(pf->hw.flags & I40E_HW_FLAG_802_1AD_CAPABLE)) + return -EOPNOTSUPP; + + if (pf->hw.pf_id != 0) { + dev_err(&pdev->dev, + "TPID configuration only supported for PF 0\n"); + return -EOPNOTSUPP; } - spin_unlock_bh(&vsi->mac_filter_hash_lock); + pf->hw.first_tag = tp_id; + ret = i40e_aq_set_switch_config(&pf->hw, 0, 0, 0, NULL); - /* Schedule the worker thread to take care of applying changes */ - i40e_service_event_schedule(vsi->back); + return ret; +} - if (ret) { - dev_err(&pf->pdev->dev, "Unable to update VF vsi context\n"); - goto error_pvid; - } +static int i40e_get_num_queues(struct pci_dev *pdev, int vf_id, int *num_queues) +{ + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vf *vf; + int ret; + + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + return ret; + vf = &pf->vf[vf_id]; - /* The Port VLAN needs to be saved across resets the same as the - * default LAN MAC address. - */ - vf->port_vlan_id = le16_to_cpu(vsi->info.pvid); - ret = 0; + *num_queues = vf->num_queue_pairs; -error_pvid: return ret; } -#define I40E_BW_CREDIT_DIVISOR 50 /* 50Mbps per BW credit */ -#define I40E_MAX_BW_INACTIVE_ACCUM 4 /* device can accumulate 4 credits max */ -/** - * i40e_ndo_set_vf_bw - * @netdev: network interface device structure - * @vf_id: VF identifier - * @tx_rate: Tx rate - * - * configure VF Tx rate - **/ -int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, - int max_tx_rate) +static int i40e_set_num_queues(struct pci_dev *pdev, int vf_id, int num_queues) { - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_pf *pf = np->vsi->back; - struct i40e_vsi *vsi; + struct i40e_pf *pf = pci_get_drvdata(pdev); struct i40e_vf *vf; - int speed = 0; - int ret = 0; + int ret; /* validate the request */ - if (vf_id >= pf->num_alloc_vfs) { - dev_err(&pf->pdev->dev, "Invalid VF Identifier %d.\n", vf_id); - ret = -EINVAL; - goto error; - } - - if (min_tx_rate) { - dev_err(&pf->pdev->dev, "Invalid min tx rate (%d) (greater than 0) specified for VF %d.\n", - min_tx_rate, vf_id); - return -EINVAL; - } + ret = i40e_validate_vf(pf, vf_id); + if (ret) + return ret; + vf = &pf->vf[vf_id]; - vf = &(pf->vf[vf_id]); - vsi = pf->vsi[vf->lan_vsi_idx]; - if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { - dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", - vf_id); - ret = -EAGAIN; - goto error; + if (test_bit(I40E_VF_STATE_LOADED_VF_DRIVER, &vf->vf_states)) { + dev_err(&pdev->dev, + "Unable to configure %d queues, please unbind the driver for VF %d\n", + num_queues, vf_id); + return -EAGAIN; } - - switch (pf->hw.phy.link_info.link_speed) { - case I40E_LINK_SPEED_40GB: - speed = 40000; - break; - case I40E_LINK_SPEED_25GB: - speed = 25000; - break; - case I40E_LINK_SPEED_20GB: - speed = 20000; - break; - case I40E_LINK_SPEED_10GB: - speed = 10000; - break; - case I40E_LINK_SPEED_1GB: - speed = 1000; - break; - default: - break; + if (num_queues > I40E_MAX_VF_QUEUES) { + dev_err(&pdev->dev, + "Unable to configure %d VF queues, the maximum is %d\n", + num_queues, I40E_MAX_VF_QUEUES); + return -EINVAL; } - - if (max_tx_rate > speed) { - dev_err(&pf->pdev->dev, "Invalid max tx rate %d specified for VF %d.\n", - max_tx_rate, vf->vf_id); - ret = -EINVAL; - goto error; + if (num_queues - vf->num_queue_pairs > pf->queues_left) { + dev_err(&pdev->dev, + "Unable to configure %d VF queues, only %d available\n", + num_queues, vf->num_queue_pairs + pf->queues_left); + return -EINVAL; } - if ((max_tx_rate < 50) && (max_tx_rate > 0)) { - dev_warn(&pf->pdev->dev, "Setting max Tx rate to minimum usable value of 50Mbps.\n"); - max_tx_rate = 50; - } + /* Set vf->num_req_queues to the desired value and reset the VF. When + * the VSI is reallocated it will be configured with the new queue + * count. + */ + vf->num_req_queues = num_queues; + i40e_vc_notify_vf_reset(vf); + i40e_reset_vf(vf, false); - /* Tx rate credits are in values of 50Mbps, 0 is disabled*/ - ret = i40e_aq_config_vsi_bw_limit(&pf->hw, vsi->seid, - max_tx_rate / I40E_BW_CREDIT_DIVISOR, - I40E_MAX_BW_INACTIVE_ACCUM, NULL); - if (ret) { - dev_err(&pf->pdev->dev, "Unable to set max tx rate, error code %d.\n", - ret); - ret = -EIO; - goto error; - } - vf->tx_rate = max_tx_rate; -error: - return ret; + return 0; } /** - * i40e_ndo_get_vf_config - * @netdev: network interface device structure + * i40e_get_max_tx_rate + * @pdev: PCI device information struct * @vf_id: VF identifier - * @ivi: VF configuration structure + * @max_tx_rate: max transmit bandwidth rate * - * return VF configuration - **/ -int i40e_ndo_get_vf_config(struct net_device *netdev, - int vf_id, struct ifla_vf_info *ivi) + * This function returns the value of transmit bandwidth, in Mbps, + * for the specified VF, + * value 0 means rate limiting is disabled. + * + * Returns 0 on success, negative on failure + */ +static int i40e_get_max_tx_rate(struct pci_dev *pdev, int vf_id, + unsigned int *max_tx_rate) { - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_vsi *vsi = np->vsi; - struct i40e_pf *pf = vsi->back; + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; struct i40e_vf *vf; - int ret = 0; + int ret; + + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pf->pdev->dev, + "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; + } /* validate the request */ - if (vf_id >= pf->num_alloc_vfs) { - dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id); - ret = -EINVAL; + ret = i40e_validate_vf(pf, vf_id); + if (ret) goto error_param; - } - vf = &(pf->vf[vf_id]); - /* first vsi is always the LAN vsi */ + vf = &pf->vf[vf_id]; vsi = pf->vsi[vf->lan_vsi_idx]; - if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { - dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", - vf_id); - ret = -EAGAIN; + if (!vsi) { + ret = -ENOENT; goto error_param; } - ivi->vf = vf_id; - - ether_addr_copy(ivi->mac, vf->default_lan_addr.addr); - - ivi->max_tx_rate = vf->tx_rate; - ivi->min_tx_rate = 0; - ivi->vlan = le16_to_cpu(vsi->info.pvid) & I40E_VLAN_MASK; - ivi->qos = (le16_to_cpu(vsi->info.pvid) & I40E_PRIORITY_MASK) >> - I40E_VLAN_PRIORITY_SHIFT; - if (vf->link_forced == false) - ivi->linkstate = IFLA_VF_LINK_STATE_AUTO; - else if (vf->link_up == true) - ivi->linkstate = IFLA_VF_LINK_STATE_ENABLE; - else - ivi->linkstate = IFLA_VF_LINK_STATE_DISABLE; - ivi->spoofchk = vf->spoofchk; - ivi->trusted = vf->trusted; - ret = 0; + *max_tx_rate = vf->tx_rate; error_param: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); return ret; } /** - * i40e_ndo_set_vf_link_state - * @netdev: network interface device structure + * i40e_set_max_tx_rate + * @pdev: PCI device information struct * @vf_id: VF identifier - * @link: required link state + * @max_tx_rate: max transmit bandwidth rate to set * - * Set the link state of a specified VF, regardless of physical link state + * This function sets the value of max transmit bandwidth, in Mbps, + * for the specified VF, + * value 0 means rate limiting is disabled. + * + * Returns 0 on success, negative on failure **/ -int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link) +static int i40e_set_max_tx_rate(struct pci_dev *pdev, int vf_id, + unsigned int *max_tx_rate) { - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_pf *pf = np->vsi->back; - struct virtchnl_pf_event pfe; - struct i40e_hw *hw = &pf->hw; + struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_vsi *vsi; struct i40e_vf *vf; - int abs_vf_id; - int ret = 0; + int ret; - /* validate the request */ - if (vf_id >= pf->num_alloc_vfs) { - dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id); - ret = -EINVAL; - goto error_out; + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pf->pdev->dev, + "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; } - vf = &pf->vf[vf_id]; - abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id; - - pfe.event = VIRTCHNL_EVENT_LINK_CHANGE; - pfe.severity = PF_EVENT_SEVERITY_INFO; + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + return ret; - switch (link) { - case IFLA_VF_LINK_STATE_AUTO: - vf->link_forced = false; - pfe.event_data.link_event.link_status = - pf->hw.phy.link_info.link_info & I40E_AQ_LINK_UP; - pfe.event_data.link_event.link_speed = - (enum virtchnl_link_speed) - pf->hw.phy.link_info.link_speed; - break; - case IFLA_VF_LINK_STATE_ENABLE: - vf->link_forced = true; - vf->link_up = true; - pfe.event_data.link_event.link_status = true; - pfe.event_data.link_event.link_speed = I40E_LINK_SPEED_40GB; - break; - case IFLA_VF_LINK_STATE_DISABLE: - vf->link_forced = true; - vf->link_up = false; - pfe.event_data.link_event.link_status = false; - pfe.event_data.link_event.link_speed = 0; - break; - default: - ret = -EINVAL; - goto error_out; + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { + dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", + vf_id); + ret = -EAGAIN; + goto error; } - /* Notify the VF of its new link state */ - i40e_aq_send_msg_to_vf(hw, abs_vf_id, VIRTCHNL_OP_EVENT, - 0, (u8 *)&pfe, sizeof(pfe), NULL); -error_out: + ret = i40e_set_bw_limit(vsi, vsi->seid, *max_tx_rate); + if (ret) + goto error; + + vf->tx_rate = *max_tx_rate; +error: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); return ret; } /** - * i40e_ndo_set_vf_spoofchk - * @netdev: network interface device structure + * i40_get_trust_state + * @pdev: PCI device information struct * @vf_id: VF identifier - * @enable: flag to enable or disable feature + * @enable: on success, true if enabled, false if not * - * Enable or disable VF spoof checking + * Gets VF trust configure. + * + * Returns 0 on success, negative on failure **/ -int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable) +static int i40e_get_trust_state(struct pci_dev *pdev, int vf_id, bool *enable) { - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_vsi *vsi = np->vsi; - struct i40e_pf *pf = vsi->back; - struct i40e_vsi_context ctxt; - struct i40e_hw *hw = &pf->hw; + struct i40e_pf *pf = pci_get_drvdata(pdev); struct i40e_vf *vf; - int ret = 0; - - /* validate the request */ - if (vf_id >= pf->num_alloc_vfs) { - dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id); - ret = -EINVAL; - goto out; - } + int ret; - vf = &(pf->vf[vf_id]); - if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { - dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", - vf_id); - ret = -EAGAIN; - goto out; - } + ret = i40e_validate_vf(pf, vf_id); + if (ret) + return ret; + vf = &pf->vf[vf_id]; - if (enable == vf->spoofchk) - goto out; + *enable = vf->trusted; - vf->spoofchk = enable; - memset(&ctxt, 0, sizeof(ctxt)); - ctxt.seid = pf->vsi[vf->lan_vsi_idx]->seid; - ctxt.pf_num = pf->hw.pf_id; - ctxt.info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID); - if (enable) - ctxt.info.sec_flags |= (I40E_AQ_VSI_SEC_FLAG_ENABLE_VLAN_CHK | - I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK); - ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL); - if (ret) { - dev_err(&pf->pdev->dev, "Error %d updating VSI parameters\n", - ret); - ret = -EIO; - } -out: return ret; } /** - * i40e_ndo_set_vf_trust - * @netdev: network interface device structure of the pf + * i40e_set_trust_state + * @pdev: PCI device information struct * @vf_id: VF identifier - * @setting: trust setting + * @enable: enable or disable trust * - * Enable or disable VF trust setting + * Sets the VF trust configure + * + * Returns 0 on success, negative on failure **/ -int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting) +static int i40e_set_trust_state(struct pci_dev *pdev, int vf_id, bool enable) { - struct i40e_netdev_priv *np = netdev_priv(netdev); - struct i40e_pf *pf = np->vsi->back; + struct i40e_pf *pf = pci_get_drvdata(pdev); struct i40e_vf *vf; - int ret = 0; + int ret; - /* validate the request */ - if (vf_id >= pf->num_alloc_vfs) { - dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id); - return -EINVAL; + if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) { + dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n"); + return -EAGAIN; } if (pf->flags & I40E_FLAG_MFP_ENABLED) { dev_err(&pf->pdev->dev, "Trusted VF not supported in MFP mode.\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } - vf = &pf->vf[vf_id]; + /* validate the request */ + ret = i40e_validate_vf(pf, vf_id); + if (ret) + goto out; - if (!vf) - return -EINVAL; - if (setting == vf->trusted) + vf = &pf->vf[vf_id]; + /* if vf is in base mode, make it untrusted */ + if (pf->vf_base_mode_only) + enable = false; + if (enable == vf->trusted) goto out; - vf->trusted = setting; - i40e_vc_notify_vf_reset(vf); - i40e_reset_vf(vf, false); + vf->trusted = enable; + i40e_vc_reset_vf(vf, true); dev_info(&pf->pdev->dev, "VF %u is now %strusted\n", - vf_id, setting ? "" : "un"); + vf_id, enable ? "" : "un"); + +#ifdef __TC_MQPRIO_MODE_MAX + if (vf->adq_enabled) { + if (!vf->trusted) { + dev_info(&pf->pdev->dev, + "VF %u no longer Trusted, deleting all cloud filters\n", + vf_id); + i40e_del_all_cloud_filters(vf); + } + } +#endif /* __TC_MQPRIO_MODE_MAX */ + out: + clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state); return ret; } + +const struct vfd_ops i40e_vfd_ops = { + .get_trunk = i40e_get_trunk, + .set_trunk = i40e_set_trunk, + .get_vlan_mirror = i40e_get_mirror, + .set_vlan_mirror = i40e_set_mirror, + .set_allow_untagged = i40e_set_allow_untagged, + .get_allow_untagged = i40e_get_allow_untagged, + .get_loopback = i40e_get_loopback, + .set_loopback = i40e_set_loopback, + .get_vlan_strip = i40e_get_vlan_strip, + .set_vlan_strip = i40e_set_vlan_strip, + .get_rx_bytes = i40e_get_rx_bytes, + .get_rx_dropped = i40e_get_rx_dropped, + .get_rx_packets = i40e_get_rx_packets, + .get_tx_bytes = i40e_get_tx_bytes, + .get_tx_dropped = i40e_get_tx_dropped, + .get_tx_packets = i40e_get_tx_packets, + .get_tx_errors = i40e_get_tx_errors, + .get_mac = i40e_get_mac, + .set_mac = i40e_set_mac, + .get_promisc = i40e_get_promisc, + .set_promisc = i40e_set_promisc, + .get_ingress_mirror = i40e_get_ingress_mirror, + .set_ingress_mirror = i40e_set_ingress_mirror, + .get_egress_mirror = i40e_get_egress_mirror, + .set_egress_mirror = i40e_set_egress_mirror, + .get_link_state = i40e_get_link_state, + .set_link_state = i40e_set_link_state, + .get_mac_list = i40e_get_mac_list, + .add_macs_to_list = i40e_add_macs_to_list, + .rem_macs_from_list = i40e_rem_macs_from_list, + .get_vf_enable = i40e_get_vf_enable, + .set_vf_enable = i40e_set_vf_enable, + .reset_stats = i40e_reset_vf_stats, + .set_vf_bw_share = i40e_store_vf_bw_share, + .get_vf_bw_share = i40e_get_vf_bw_share, + .set_pf_qos_apply = i40e_set_pf_qos_apply, + .get_pf_ingress_mirror = i40e_get_pf_ingress_mirror, + .set_pf_ingress_mirror = i40e_set_pf_ingress_mirror, + .get_pf_egress_mirror = i40e_get_pf_egress_mirror, + .set_pf_egress_mirror = i40e_set_pf_egress_mirror, + .get_pf_tpid = i40e_get_pf_tpid, + .set_pf_tpid = i40e_set_pf_tpid, + .get_num_queues = i40e_get_num_queues, + .set_num_queues = i40e_set_num_queues, + .get_max_tx_rate = i40e_get_max_tx_rate, + .set_max_tx_rate = i40e_set_max_tx_rate, + .get_trust_state = i40e_get_trust_state, + .set_trust_state = i40e_set_trust_state, +}; +#endif /* HAVE_NDO_SET_VF_LINK_STATE */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h index 1f4b0c504..77fb5cfef 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h @@ -1,28 +1,5 @@ -/******************************************************************************* - * - * Intel Ethernet Controller XL710 Family Linux Driver - * Copyright(c) 2013 - 2015 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Contact Information: - * e1000-devel Mailing List - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - ******************************************************************************/ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ #ifndef _I40E_VIRTCHNL_PF_H_ #define _I40E_VIRTCHNL_PF_H_ @@ -36,9 +13,11 @@ #define I40E_DEFAULT_NUM_MDD_EVENTS_ALLOWED 3 #define I40E_DEFAULT_NUM_INVALID_MSGS_ALLOWED 10 -#define I40E_VLAN_PRIORITY_SHIFT 12 +#define I40E_VLAN_PRIORITY_SHIFT 13 #define I40E_VLAN_MASK 0xFFF -#define I40E_PRIORITY_MASK 0x7000 +#define I40E_PRIORITY_MASK 0xE000 + +#define I40E_MAX_VF_PROMISC_FLAGS 3 /* Various queue ctrls */ enum i40e_queue_ctrl { @@ -55,19 +34,30 @@ enum i40e_queue_ctrl { enum i40e_vf_states { I40E_VF_STATE_INIT = 0, I40E_VF_STATE_ACTIVE, - I40E_VF_STATE_IWARPENA, - I40E_VF_STATE_FCOEENA, I40E_VF_STATE_DISABLED, I40E_VF_STATE_MC_PROMISC, I40E_VF_STATE_UC_PROMISC, I40E_VF_STATE_PRE_ENABLE, + I40E_VF_STATE_LOADED_VF_DRIVER, }; /* VF capabilities */ enum i40e_vf_capabilities { I40E_VIRTCHNL_VF_CAP_PRIVILEGE = 0, I40E_VIRTCHNL_VF_CAP_L2, - I40E_VIRTCHNL_VF_CAP_IWARP, +}; + +/* In ADq, max 4 VSI's can be allocated per VF including primary VF VSI. + * These variables are used to store indices, id's and number of queues + * for each VSI including that of primary VF VSI. Each Traffic class is + * termed as channel and each channel can in-turn have 4 queues which + * means max 16 queues overall per VF. + */ +struct i40evf_channel { + u16 vsi_idx; /* index in PF struct for all channel VSIs */ + u16 vsi_id; /* VSI ID used by firmware */ + u16 num_qps; /* number of queue pairs requested by user */ + u64 max_tx_rate; /* bandwidth rate allocation for VSIs */ }; /* VF information structure */ @@ -97,6 +87,7 @@ struct i40e_vf { u16 lan_vsi_id; /* ID as used by firmware */ u8 num_queue_pairs; /* num of qps assigned to VF vsis */ + u8 num_req_queues; /* num of requested qps */ u64 num_mdd_events; /* num of mdd events detected */ /* num of continuous malformed or invalid msgs detected */ u64 num_invalid_msgs; @@ -105,39 +96,87 @@ struct i40e_vf { unsigned long vf_caps; /* vf's adv. capabilities */ unsigned long vf_states; /* vf's runtime states */ unsigned int tx_rate; /* Tx bandwidth limit in Mbps */ +#ifdef HAVE_NDO_SET_VF_LINK_STATE bool link_forced; bool link_up; /* only valid if VF link is forced */ - bool spoofchk; - u16 num_mac; +#endif + bool queues_enabled; /* true if the VF queues are enabled */ + bool mac_anti_spoof; u16 num_vlan; - - /* RDMA Client */ - struct virtchnl_iwarp_qvlist_info *qvlist_info; + DECLARE_BITMAP(mirror_vlans, VLAN_N_VID); + u16 vlan_rule_id; +#define I40E_NO_VF_MIRROR -1 +/* assuming vf ids' range is <0..max_supported> */ +#define I40E_IS_MIRROR_VLAN_ID_VALID(id) ((id) >= 0) + u16 ingress_rule_id; + int ingress_vlan; + u16 egress_rule_id; + int egress_vlan; + DECLARE_BITMAP(trunk_vlans, VLAN_N_VID); + bool allow_untagged; + bool loopback; + bool vlan_stripping; + u8 promisc_mode; + u8 bw_share; + bool bw_share_applied; /* true if config is applied to the device */ + bool pf_ctrl_disable; /* tracking bool for PF ctrl of VF enable/disable */ + + /* ADq related variables */ + bool adq_enabled; /* flag to enable adq */ + u8 num_tc; + struct i40evf_channel ch[I40E_MAX_VF_VSI]; + struct hlist_head cloud_filter_list; + u16 num_cloud_filters; }; void i40e_free_vfs(struct i40e_pf *pf); +#if defined(HAVE_SRIOV_CONFIGURE) || defined(HAVE_RHEL6_SRIOV_CONFIGURE) int i40e_pci_sriov_configure(struct pci_dev *dev, int num_vfs); +#endif int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs); int i40e_vc_process_vf_msg(struct i40e_pf *pf, s16 vf_id, u32 v_opcode, u32 v_retval, u8 *msg, u16 msglen); int i40e_vc_process_vflr_event(struct i40e_pf *pf); -void i40e_reset_vf(struct i40e_vf *vf, bool flr); -void i40e_reset_all_vfs(struct i40e_pf *pf, bool flr); +bool i40e_reset_vf(struct i40e_vf *vf, bool flr); +bool i40e_reset_all_vfs(struct i40e_pf *pf, bool flr); void i40e_vc_notify_vf_reset(struct i40e_vf *vf); /* VF configuration related iplink handlers */ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac); +#ifdef IFLA_VF_VLAN_INFO_MAX int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos, __be16 vlan_proto); +#else +int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, + int vf_id, u16 vlan_id, u8 qos); +#endif +#ifdef HAVE_NDO_SET_VF_MIN_MAX_TX_RATE int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, int max_tx_rate); +#else +int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate); +#endif +#ifdef HAVE_NDO_SET_VF_TRUST int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting); +#endif +int i40e_ndo_enable_vf(struct net_device *netdev, int vf_id, bool enable); +#ifdef IFLA_VF_MAX int i40e_ndo_get_vf_config(struct net_device *netdev, int vf_id, struct ifla_vf_info *ivi); +#ifdef HAVE_NDO_SET_VF_LINK_STATE int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link); +#endif +#ifdef HAVE_VF_SPOOFCHK_CONFIGURE int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable); +#endif +#endif void i40e_vc_notify_link_state(struct i40e_pf *pf); void i40e_vc_notify_reset(struct i40e_pf *pf); +#ifdef HAVE_VF_STATS +int i40e_get_vf_stats(struct net_device *netdev, int vf_id, + struct ifla_vf_stats *vf_stats); +#endif +extern const struct vfd_ops i40e_vfd_ops; #endif /* _I40E_VIRTCHNL_PF_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/kcompat.c b/drivers/net/ethernet/intel/i40e/kcompat.c new file mode 100644 index 000000000..eb2ba6bd1 --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/kcompat.c @@ -0,0 +1,2761 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ + +#include "i40e.h" +#include "kcompat.h" + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,8) ) || defined __VMKLNX__ +/* From lib/vsprintf.c */ +#include + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define _kc_ZEROPAD 1 /* pad with zero */ +#define _kc_SIGN 2 /* unsigned/signed long */ +#define _kc_PLUS 4 /* show plus */ +#define _kc_SPACE 8 /* space if plus */ +#define _kc_LEFT 16 /* left justified */ +#define _kc_SPECIAL 32 /* 0x */ +#define _kc_LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +static char * number(char * buf, char * end, long long num, int base, int size, int precision, int type) +{ + char c,sign,tmp[66]; + const char *digits; + const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int i; + + digits = (type & _kc_LARGE) ? large_digits : small_digits; + if (type & _kc_LEFT) + type &= ~_kc_ZEROPAD; + if (base < 2 || base > 36) + return 0; + c = (type & _kc_ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & _kc_SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & _kc_PLUS) { + sign = '+'; + size--; + } else if (type & _kc_SPACE) { + sign = ' '; + size--; + } + } + if (type & _kc_SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(_kc_ZEROPAD+_kc_LEFT))) { + while(size-->0) { + if (buf <= end) + *buf = ' '; + ++buf; + } + } + if (sign) { + if (buf <= end) + *buf = sign; + ++buf; + } + if (type & _kc_SPECIAL) { + if (base==8) { + if (buf <= end) + *buf = '0'; + ++buf; + } else if (base==16) { + if (buf <= end) + *buf = '0'; + ++buf; + if (buf <= end) + *buf = digits[33]; + ++buf; + } + } + if (!(type & _kc_LEFT)) { + while (size-- > 0) { + if (buf <= end) + *buf = c; + ++buf; + } + } + while (i < precision--) { + if (buf <= end) + *buf = '0'; + ++buf; + } + while (i-- > 0) { + if (buf <= end) + *buf = tmp[i]; + ++buf; + } + while (size-- > 0) { + if (buf <= end) + *buf = ' '; + ++buf; + } + return buf; +} + +int _kc_vsnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + int len; + unsigned long long num; + int i, base; + char *str, *end, c; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + + str = buf; + end = buf + size - 1; + + if (end < buf - 1) { + end = ((void *) -1); + size = end - buf + 1; + } + + for (; *fmt ; ++fmt) { + if (*fmt != '%') { + if (str <= end) + *str = *fmt; + ++str; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= _kc_LEFT; goto repeat; + case '+': flags |= _kc_PLUS; goto repeat; + case ' ': flags |= _kc_SPACE; goto repeat; + case '#': flags |= _kc_SPECIAL; goto repeat; + case '0': flags |= _kc_ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= _kc_LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & _kc_LEFT)) { + while (--field_width > 0) { + if (str <= end) + *str = ' '; + ++str; + } + } + c = (unsigned char) va_arg(args, int); + if (str <= end) + *str = c; + ++str; + while (--field_width > 0) { + if (str <= end) + *str = ' '; + ++str; + } + continue; + + case 's': + s = va_arg(args, char *); + if (!s) + s = ""; + + len = strnlen(s, precision); + + if (!(flags & _kc_LEFT)) { + while (len < field_width--) { + if (str <= end) + *str = ' '; + ++str; + } + } + for (i = 0; i < len; ++i) { + if (str <= end) + *str = *s; + ++str; ++s; + } + while (len < field_width--) { + if (str <= end) + *str = ' '; + ++str; + } + continue; + + case 'p': + if ('M' == *(fmt+1)) { + str = get_mac(str, end, va_arg(args, unsigned char *)); + fmt++; + } else { + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= _kc_ZEROPAD; + } + str = number(str, end, + (unsigned long) va_arg(args, void *), + 16, field_width, precision, flags); + } + continue; + + case 'n': + /* FIXME: + * What does C99 say about the overflow case here? */ + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else if (qualifier == 'Z') { + size_t * ip = va_arg(args, size_t *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + if (str <= end) + *str = '%'; + ++str; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= _kc_LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= _kc_SIGN; + case 'u': + break; + + default: + if (str <= end) + *str = '%'; + ++str; + if (*fmt) { + if (str <= end) + *str = *fmt; + ++str; + } else { + --fmt; + } + continue; + } + if (qualifier == 'L') + num = va_arg(args, long long); + else if (qualifier == 'l') { + num = va_arg(args, unsigned long); + if (flags & _kc_SIGN) + num = (signed long) num; + } else if (qualifier == 'Z') { + num = va_arg(args, size_t); + } else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & _kc_SIGN) + num = (signed short) num; + } else { + num = va_arg(args, unsigned int); + if (flags & _kc_SIGN) + num = (signed int) num; + } + str = number(str, end, num, base, + field_width, precision, flags); + } + if (str <= end) + *str = '\0'; + else if (size > 0) + /* don't write out a null byte if the buf size is zero */ + *end = '\0'; + /* the trailing null byte doesn't count towards the total + * ++str; + */ + return str-buf; +} + +int _kc_snprintf(char * buf, size_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = _kc_vsnprintf(buf,size,fmt,args); + va_end(args); + return i; +} +#endif /* < 2.4.8 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,13) ) + +/**************************************/ +/* PCI DMA MAPPING */ + +#if defined(CONFIG_HIGHMEM) + +#ifndef PCI_DRAM_OFFSET +#define PCI_DRAM_OFFSET 0 +#endif + +u64 +_kc_pci_map_page(struct pci_dev *dev, struct page *page, unsigned long offset, + size_t size, int direction) +{ + return (((u64) (page - mem_map) << PAGE_SHIFT) + offset + + PCI_DRAM_OFFSET); +} + +#else /* CONFIG_HIGHMEM */ + +u64 +_kc_pci_map_page(struct pci_dev *dev, struct page *page, unsigned long offset, + size_t size, int direction) +{ + return pci_map_single(dev, (void *)page_address(page) + offset, size, + direction); +} + +#endif /* CONFIG_HIGHMEM */ + +void +_kc_pci_unmap_page(struct pci_dev *dev, u64 dma_addr, size_t size, + int direction) +{ + return pci_unmap_single(dev, dma_addr, size, direction); +} + +#endif /* 2.4.13 => 2.4.3 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,3) ) + +/**************************************/ +/* PCI DRIVER API */ + +int +_kc_pci_set_dma_mask(struct pci_dev *dev, dma_addr_t mask) +{ + if (!pci_dma_supported(dev, mask)) + return -EIO; + dev->dma_mask = mask; + return 0; +} + +int +_kc_pci_request_regions(struct pci_dev *dev, char *res_name) +{ + int i; + + for (i = 0; i < 6; i++) { + if (pci_resource_len(dev, i) == 0) + continue; + + if (pci_resource_flags(dev, i) & IORESOURCE_IO) { + if (!request_region(pci_resource_start(dev, i), pci_resource_len(dev, i), res_name)) { + pci_release_regions(dev); + return -EBUSY; + } + } else if (pci_resource_flags(dev, i) & IORESOURCE_MEM) { + if (!request_mem_region(pci_resource_start(dev, i), pci_resource_len(dev, i), res_name)) { + pci_release_regions(dev); + return -EBUSY; + } + } + } + return 0; +} + +void +_kc_pci_release_regions(struct pci_dev *dev) +{ + int i; + + for (i = 0; i < 6; i++) { + if (pci_resource_len(dev, i) == 0) + continue; + + if (pci_resource_flags(dev, i) & IORESOURCE_IO) + release_region(pci_resource_start(dev, i), pci_resource_len(dev, i)); + + else if (pci_resource_flags(dev, i) & IORESOURCE_MEM) + release_mem_region(pci_resource_start(dev, i), pci_resource_len(dev, i)); + } +} + +/**************************************/ +/* NETWORK DRIVER API */ + +struct net_device * +_kc_alloc_etherdev(int sizeof_priv) +{ + struct net_device *dev; + int alloc_size; + + alloc_size = sizeof(*dev) + sizeof_priv + IFNAMSIZ + 31; + dev = kzalloc(alloc_size, GFP_KERNEL); + if (!dev) + return NULL; + + if (sizeof_priv) + dev->priv = (void *) (((unsigned long)(dev + 1) + 31) & ~31); + dev->name[0] = '\0'; + ether_setup(dev); + + return dev; +} + +int +_kc_is_valid_ether_addr(u8 *addr) +{ + const char zaddr[6] = { 0, }; + + return !(addr[0] & 1) && memcmp(addr, zaddr, 6); +} + +#endif /* 2.4.3 => 2.4.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,6) ) + +int +_kc_pci_set_power_state(struct pci_dev *dev, int state) +{ + return 0; +} + +int +_kc_pci_enable_wake(struct pci_dev *pdev, u32 state, int enable) +{ + return 0; +} + +#endif /* 2.4.6 => 2.4.3 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) +void _kc_skb_fill_page_desc(struct sk_buff *skb, int i, struct page *page, + int off, int size) +{ + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + frag->page = page; + frag->page_offset = off; + frag->size = size; + skb_shinfo(skb)->nr_frags = i + 1; +} + +/* + * Original Copyright: + * find_next_bit.c: fallback find next bit implementation + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +/** + * find_next_bit - find the next set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +unsigned long find_next_bit(const unsigned long *addr, unsigned long size, + unsigned long offset) +{ + const unsigned long *p = addr + BITOP_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG-1); + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset %= BITS_PER_LONG; + if (offset) { + tmp = *(p++); + tmp &= (~0UL << offset); + if (size < BITS_PER_LONG) + goto found_first; + if (tmp) + goto found_middle; + size -= BITS_PER_LONG; + result += BITS_PER_LONG; + } + while (size & ~(BITS_PER_LONG-1)) { + if ((tmp = *(p++))) + goto found_middle; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp &= (~0UL >> (BITS_PER_LONG - size)); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + ffs(tmp); +} + +size_t _kc_strlcpy(char *dest, const char *src, size_t size) +{ + size_t ret = strlen(src); + + if (size) { + size_t len = (ret >= size) ? size - 1 : ret; + memcpy(dest, src, len); + dest[len] = '\0'; + } + return ret; +} + +#ifndef do_div +#if BITS_PER_LONG == 32 +uint32_t __attribute__((weak)) _kc__div64_32(uint64_t *n, uint32_t base) +{ + uint64_t rem = *n; + uint64_t b = base; + uint64_t res, d = 1; + uint32_t high = rem >> 32; + + /* Reduce the thing a bit first */ + res = 0; + if (high >= base) { + high /= base; + res = (uint64_t) high << 32; + rem -= (uint64_t) (high*base) << 32; + } + + while ((int64_t)b > 0 && b < rem) { + b = b+b; + d = d+d; + } + + do { + if (rem >= b) { + rem -= b; + res += d; + } + b >>= 1; + d >>= 1; + } while (d); + + *n = res; + return rem; +} +#endif /* BITS_PER_LONG == 32 */ +#endif /* do_div */ +#endif /* 2.6.0 => 2.4.6 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4) ) +int _kc_scnprintf(char * buf, size_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsnprintf(buf, size, fmt, args); + va_end(args); + return (i >= size) ? (size - 1) : i; +} +#endif /* < 2.6.4 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) ) +DECLARE_BITMAP(_kcompat_node_online_map, MAX_NUMNODES) = {1}; +#endif /* < 2.6.10 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) ) +char *_kc_kstrdup(const char *s, unsigned int gfp) +{ + size_t len; + char *buf; + + if (!s) + return NULL; + + len = strlen(s) + 1; + buf = kmalloc(len, gfp); + if (buf) + memcpy(buf, s, len); + return buf; +} +#endif /* < 2.6.13 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) ) +void *_kc_kzalloc(size_t size, int flags) +{ + void *ret = kmalloc(size, flags); + if (ret) + memset(ret, 0, size); + return ret; +} +#endif /* <= 2.6.13 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ) +int _kc_skb_pad(struct sk_buff *skb, int pad) +{ + int ntail; + + /* If the skbuff is non linear tailroom is always zero.. */ + if(!skb_cloned(skb) && skb_tailroom(skb) >= pad) { + memset(skb->data+skb->len, 0, pad); + return 0; + } + + ntail = skb->data_len + pad - (skb->end - skb->tail); + if (likely(skb_cloned(skb) || ntail > 0)) { + if (pskb_expand_head(skb, 0, ntail, GFP_ATOMIC)) + goto free_skb; + } + +#ifdef MAX_SKB_FRAGS + if (skb_is_nonlinear(skb) && + !__pskb_pull_tail(skb, skb->data_len)) + goto free_skb; + +#endif + memset(skb->data + skb->len, 0, pad); + return 0; + +free_skb: + kfree_skb(skb); + return -ENOMEM; +} + +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(5,4))) +int _kc_pci_save_state(struct pci_dev *pdev) +{ + struct adapter_struct *adapter = pci_get_drvdata(pdev); + int size = PCI_CONFIG_SPACE_LEN, i; + u16 pcie_cap_offset, pcie_link_status; + +#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) + /* no ->dev for 2.4 kernels */ + WARN_ON(pdev->dev.driver_data == NULL); +#endif + pcie_cap_offset = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (pcie_cap_offset) { + if (!pci_read_config_word(pdev, + pcie_cap_offset + PCIE_LINK_STATUS, + &pcie_link_status)) + size = PCIE_CONFIG_SPACE_LEN; + } + pci_config_space_ich8lan(); +#ifdef HAVE_PCI_ERS + if (adapter->config_space == NULL) +#else + WARN_ON(adapter->config_space != NULL); +#endif + adapter->config_space = kmalloc(size, GFP_KERNEL); + if (!adapter->config_space) { + printk(KERN_ERR "Out of memory in pci_save_state\n"); + return -ENOMEM; + } + for (i = 0; i < (size / 4); i++) + pci_read_config_dword(pdev, i * 4, &adapter->config_space[i]); + return 0; +} + +void _kc_pci_restore_state(struct pci_dev *pdev) +{ + struct adapter_struct *adapter = pci_get_drvdata(pdev); + int size = PCI_CONFIG_SPACE_LEN, i; + u16 pcie_cap_offset; + u16 pcie_link_status; + + if (adapter->config_space != NULL) { + pcie_cap_offset = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (pcie_cap_offset && + !pci_read_config_word(pdev, + pcie_cap_offset + PCIE_LINK_STATUS, + &pcie_link_status)) + size = PCIE_CONFIG_SPACE_LEN; + + pci_config_space_ich8lan(); + for (i = 0; i < (size / 4); i++) + pci_write_config_dword(pdev, i * 4, adapter->config_space[i]); +#ifndef HAVE_PCI_ERS + kfree(adapter->config_space); + adapter->config_space = NULL; +#endif + } +} +#endif /* !(RHEL_RELEASE_CODE >= RHEL 5.4) */ + +#ifdef HAVE_PCI_ERS +void _kc_free_netdev(struct net_device *netdev) +{ + struct adapter_struct *adapter = netdev_priv(netdev); + + kfree(adapter->config_space); +#ifdef CONFIG_SYSFS + if (netdev->reg_state == NETREG_UNINITIALIZED) { + kfree((char *)netdev - netdev->padded); + } else { + BUG_ON(netdev->reg_state != NETREG_UNREGISTERED); + netdev->reg_state = NETREG_RELEASED; + class_device_put(&netdev->class_dev); + } +#else + kfree((char *)netdev - netdev->padded); +#endif +} +#endif + +void *_kc_kmemdup(const void *src, size_t len, unsigned gfp) +{ + void *p; + + p = kzalloc(len, gfp); + if (p) + memcpy(p, src, len); + return p; +} +#endif /* <= 2.6.19 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21) ) +struct pci_dev *_kc_netdev_to_pdev(struct net_device *netdev) +{ + return ((struct adapter_struct *)netdev_priv(netdev))->pdev; +} +#endif /* < 2.6.21 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) ) +/* hexdump code taken from lib/hexdump.c */ +static void _kc_hex_dump_to_buffer(const void *buf, size_t len, int rowsize, + int groupsize, unsigned char *linebuf, + size_t linebuflen, bool ascii) +{ + const u8 *ptr = buf; + u8 ch; + int j, lx = 0; + int ascii_column; + + if (rowsize != 16 && rowsize != 32) + rowsize = 16; + + if (!len) + goto nil; + if (len > rowsize) /* limit to one line at a time */ + len = rowsize; + if ((len % groupsize) != 0) /* no mixed size output */ + groupsize = 1; + + switch (groupsize) { + case 8: { + const u64 *ptr8 = buf; + int ngroups = len / groupsize; + + for (j = 0; j < ngroups; j++) + lx += scnprintf((char *)(linebuf + lx), linebuflen - lx, + "%s%16.16llx", j ? " " : "", + (unsigned long long)*(ptr8 + j)); + ascii_column = 17 * ngroups + 2; + break; + } + + case 4: { + const u32 *ptr4 = buf; + int ngroups = len / groupsize; + + for (j = 0; j < ngroups; j++) + lx += scnprintf((char *)(linebuf + lx), linebuflen - lx, + "%s%8.8x", j ? " " : "", *(ptr4 + j)); + ascii_column = 9 * ngroups + 2; + break; + } + + case 2: { + const u16 *ptr2 = buf; + int ngroups = len / groupsize; + + for (j = 0; j < ngroups; j++) + lx += scnprintf((char *)(linebuf + lx), linebuflen - lx, + "%s%4.4x", j ? " " : "", *(ptr2 + j)); + ascii_column = 5 * ngroups + 2; + break; + } + + default: + for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) { + ch = ptr[j]; + linebuf[lx++] = hex_asc(ch >> 4); + linebuf[lx++] = hex_asc(ch & 0x0f); + linebuf[lx++] = ' '; + } + if (j) + lx--; + + ascii_column = 3 * rowsize + 2; + break; + } + if (!ascii) + goto nil; + + while (lx < (linebuflen - 1) && lx < (ascii_column - 1)) + linebuf[lx++] = ' '; + for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) + linebuf[lx++] = (isascii(ptr[j]) && isprint(ptr[j])) ? ptr[j] + : '.'; +nil: + linebuf[lx++] = '\0'; +} + +void _kc_print_hex_dump(const char *level, + const char *prefix_str, int prefix_type, + int rowsize, int groupsize, + const void *buf, size_t len, bool ascii) +{ + const u8 *ptr = buf; + int i, linelen, remaining = len; + unsigned char linebuf[200]; + + if (rowsize != 16 && rowsize != 32) + rowsize = 16; + + for (i = 0; i < len; i += rowsize) { + linelen = min(remaining, rowsize); + remaining -= rowsize; + _kc_hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, + linebuf, sizeof(linebuf), ascii); + + switch (prefix_type) { + case DUMP_PREFIX_ADDRESS: + printk("%s%s%*p: %s\n", level, prefix_str, + (int)(2 * sizeof(void *)), ptr + i, linebuf); + break; + case DUMP_PREFIX_OFFSET: + printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf); + break; + default: + printk("%s%s%s\n", level, prefix_str, linebuf); + break; + } + } +} + +#endif /* < 2.6.22 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) ) +#ifdef NAPI +struct net_device *napi_to_poll_dev(const struct napi_struct *napi) +{ + struct adapter_q_vector *q_vector = container_of(napi, + struct adapter_q_vector, + napi); + return &q_vector->poll_dev; +} + +int __kc_adapter_clean(struct net_device *netdev, int *budget) +{ + int work_done; + int work_to_do = min(*budget, netdev->quota); + /* kcompat.h netif_napi_add puts napi struct in "fake netdev->priv" */ + struct napi_struct *napi = netdev->priv; + work_done = napi->poll(napi, work_to_do); + *budget -= work_done; + netdev->quota -= work_done; + return (work_done >= work_to_do) ? 1 : 0; +} +#endif /* NAPI */ +#endif /* <= 2.6.24 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) ) +void _kc_pci_disable_link_state(struct pci_dev *pdev, int state) +{ + struct pci_dev *parent = pdev->bus->self; + u16 link_state; + int pos; + + if (!parent) + return; + + pos = pci_find_capability(parent, PCI_CAP_ID_EXP); + if (pos) { + pci_read_config_word(parent, pos + PCI_EXP_LNKCTL, &link_state); + link_state &= ~state; + pci_write_config_word(parent, pos + PCI_EXP_LNKCTL, link_state); + } +} +#endif /* < 2.6.26 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ) +#ifdef HAVE_TX_MQ +void _kc_netif_tx_stop_all_queues(struct net_device *netdev) +{ + struct adapter_struct *adapter = netdev_priv(netdev); + int i; + + netif_stop_queue(netdev); + if (netif_is_multiqueue(netdev)) + for (i = 0; i < adapter->num_tx_queues; i++) + netif_stop_subqueue(netdev, i); +} +void _kc_netif_tx_wake_all_queues(struct net_device *netdev) +{ + struct adapter_struct *adapter = netdev_priv(netdev); + int i; + + netif_wake_queue(netdev); + if (netif_is_multiqueue(netdev)) + for (i = 0; i < adapter->num_tx_queues; i++) + netif_wake_subqueue(netdev, i); +} +void _kc_netif_tx_start_all_queues(struct net_device *netdev) +{ + struct adapter_struct *adapter = netdev_priv(netdev); + int i; + + netif_start_queue(netdev); + if (netif_is_multiqueue(netdev)) + for (i = 0; i < adapter->num_tx_queues; i++) + netif_start_subqueue(netdev, i); +} +#endif /* HAVE_TX_MQ */ + +void __kc_warn_slowpath(const char *file, int line, const char *fmt, ...) +{ + va_list args; + + printk(KERN_WARNING "------------[ cut here ]------------\n"); + printk(KERN_WARNING "WARNING: at %s:%d \n", file, line); + va_start(args, fmt); + vprintk(fmt, args); + va_end(args); + + dump_stack(); +} +#endif /* __VMKLNX__ */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ) + +int +_kc_pci_prepare_to_sleep(struct pci_dev *dev) +{ + pci_power_t target_state; + int error; + + target_state = pci_choose_state(dev, PMSG_SUSPEND); + + pci_enable_wake(dev, target_state, true); + + error = pci_set_power_state(dev, target_state); + + if (error) + pci_enable_wake(dev, target_state, false); + + return error; +} + +int +_kc_pci_wake_from_d3(struct pci_dev *dev, bool enable) +{ + int err; + + err = pci_enable_wake(dev, PCI_D3cold, enable); + if (err) + goto out; + + err = pci_enable_wake(dev, PCI_D3hot, enable); + +out: + return err; +} +#endif /* < 2.6.28 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) ) +static void __kc_pci_set_master(struct pci_dev *pdev, bool enable) +{ + u16 old_cmd, cmd; + + pci_read_config_word(pdev, PCI_COMMAND, &old_cmd); + if (enable) + cmd = old_cmd | PCI_COMMAND_MASTER; + else + cmd = old_cmd & ~PCI_COMMAND_MASTER; + if (cmd != old_cmd) { + dev_dbg(pci_dev_to_dev(pdev), "%s bus mastering\n", + enable ? "enabling" : "disabling"); + pci_write_config_word(pdev, PCI_COMMAND, cmd); + } +#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,7) ) + pdev->is_busmaster = enable; +#endif +} + +void _kc_pci_clear_master(struct pci_dev *dev) +{ + __kc_pci_set_master(dev, false); +} +#endif /* < 2.6.29 */ + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) ) +#if (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(6,0)) +int _kc_pci_num_vf(struct pci_dev __maybe_unused *dev) +{ + int num_vf = 0; +#ifdef CONFIG_PCI_IOV + struct pci_dev *vfdev; + + /* loop through all ethernet devices starting at PF dev */ + vfdev = pci_get_class(PCI_CLASS_NETWORK_ETHERNET << 8, NULL); + while (vfdev) { + if (vfdev->is_virtfn && vfdev->physfn == dev) + num_vf++; + + vfdev = pci_get_class(PCI_CLASS_NETWORK_ETHERNET << 8, vfdev); + } + +#endif + return num_vf; +} +#endif /* RHEL_RELEASE_CODE */ +#endif /* < 2.6.34 */ + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) ) +#ifdef HAVE_TX_MQ +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,0))) +#ifndef CONFIG_NETDEVICES_MULTIQUEUE +int _kc_netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq) +{ + unsigned int real_num = dev->real_num_tx_queues; + struct Qdisc *qdisc; + int i; + + if (txq < 1 || txq > dev->num_tx_queues) + return -EINVAL; + + else if (txq > real_num) + dev->real_num_tx_queues = txq; + else if (txq < real_num) { + dev->real_num_tx_queues = txq; + for (i = txq; i < dev->num_tx_queues; i++) { + qdisc = netdev_get_tx_queue(dev, i)->qdisc; + if (qdisc) { + spin_lock_bh(qdisc_lock(qdisc)); + qdisc_reset(qdisc); + spin_unlock_bh(qdisc_lock(qdisc)); + } + } + } + + return 0; +} +#endif /* CONFIG_NETDEVICES_MULTIQUEUE */ +#endif /* !(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,0)) */ +#endif /* HAVE_TX_MQ */ + +ssize_t _kc_simple_write_to_buffer(void *to, size_t available, loff_t *ppos, + const void __user *from, size_t count) +{ + loff_t pos = *ppos; + size_t res; + + if (pos < 0) + return -EINVAL; + if (pos >= available || !count) + return 0; + if (count > available - pos) + count = available - pos; + res = copy_from_user(to + pos, from, count); + if (res == count) + return -EFAULT; + count -= res; + *ppos = pos + count; + return count; +} + +#endif /* < 2.6.35 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ) +static const u32 _kc_flags_dup_features = + (ETH_FLAG_LRO | ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH); + +u32 _kc_ethtool_op_get_flags(struct net_device *dev) +{ + return dev->features & _kc_flags_dup_features; +} + +int _kc_ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported) +{ + if (data & ~supported) + return -EINVAL; + + dev->features = ((dev->features & ~_kc_flags_dup_features) | + (data & _kc_flags_dup_features)); + return 0; +} +#endif /* < 2.6.36 */ + +/******************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) ) +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(6,0))) +#ifdef HAVE_NETDEV_SELECT_QUEUE +#include +#include + +u16 ___kc_skb_tx_hash(struct net_device *dev, const struct sk_buff *skb, + u16 num_tx_queues) +{ + u32 hash; + u16 qoffset = 0; + u16 qcount = num_tx_queues; + + if (skb_rx_queue_recorded(skb)) { + hash = skb_get_rx_queue(skb); + while (unlikely(hash >= num_tx_queues)) + hash -= num_tx_queues; + return hash; + } + + if (skb->sk && skb->sk->sk_hash) + hash = skb->sk->sk_hash; + else +#ifdef NETIF_F_RXHASH + hash = (__force u16) skb->protocol ^ skb->rxhash; +#else + hash = skb->protocol; +#endif + + hash = jhash_1word(hash, _kc_hashrnd); + + return (u16) (((u64) hash * qcount) >> 32) + qoffset; +} +#endif /* HAVE_NETDEV_SELECT_QUEUE */ + +u8 _kc_netdev_get_num_tc(struct net_device *dev) +{ + struct i40e_netdev_priv *np = netdev_priv(dev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + if (pf->flags & I40E_FLAG_DCB_ENABLED) + return vsi->tc_config.numtc; + + return 0; +} + +int _kc_netdev_set_num_tc(struct net_device *dev, u8 num_tc) +{ + struct i40e_netdev_priv *np = netdev_priv(dev); + struct i40e_vsi *vsi = np->vsi; + + if (num_tc > I40E_MAX_TRAFFIC_CLASS) + return -EINVAL; + + vsi->tc_config.numtc = num_tc; + + return 0; +} + +u8 _kc_netdev_get_prio_tc_map(struct net_device *dev, u8 up) +{ + struct i40e_netdev_priv *np = netdev_priv(dev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; + struct i40e_dcbx_config *dcbcfg = &hw->local_dcbx_config; + + return dcbcfg->etscfg.prioritytable[up]; +} + +#endif /* !(RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(6,0)) */ +#endif /* < 2.6.39 */ + +/******************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0) ) +void _kc_skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, + int off, int size, unsigned int truesize) +{ + skb_fill_page_desc(skb, i, page, off, size); + skb->len += size; + skb->data_len += size; + skb->truesize += truesize; +} + +#if !(SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(11,3,0)) +int _kc_simple_open(struct inode *inode, struct file *file) +{ + if (inode->i_private) + file->private_data = inode->i_private; + + return 0; +} +#endif /* SLE_VERSION < 11,3,0 */ + +#endif /* < 3.4.0 */ + +/******************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) ) +static inline int __kc_pcie_cap_version(struct pci_dev *dev) +{ + int pos; + u16 reg16; + + pos = pci_find_capability(dev, PCI_CAP_ID_EXP); + if (!pos) + return 0; + pci_read_config_word(dev, pos + PCI_EXP_FLAGS, ®16); + return reg16 & PCI_EXP_FLAGS_VERS; +} + +static inline bool __kc_pcie_cap_has_devctl(const struct pci_dev __always_unused *dev) +{ + return true; +} + +static inline bool __kc_pcie_cap_has_lnkctl(struct pci_dev *dev) +{ + int type = pci_pcie_type(dev); + + return __kc_pcie_cap_version(dev) > 1 || + type == PCI_EXP_TYPE_ROOT_PORT || + type == PCI_EXP_TYPE_ENDPOINT || + type == PCI_EXP_TYPE_LEG_END; +} + +static inline bool __kc_pcie_cap_has_sltctl(struct pci_dev *dev) +{ + int type = pci_pcie_type(dev); + int pos; + u16 pcie_flags_reg; + + pos = pci_find_capability(dev, PCI_CAP_ID_EXP); + if (!pos) + return false; + pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &pcie_flags_reg); + + return __kc_pcie_cap_version(dev) > 1 || + type == PCI_EXP_TYPE_ROOT_PORT || + (type == PCI_EXP_TYPE_DOWNSTREAM && + pcie_flags_reg & PCI_EXP_FLAGS_SLOT); +} + +static inline bool __kc_pcie_cap_has_rtctl(struct pci_dev *dev) +{ + int type = pci_pcie_type(dev); + + return __kc_pcie_cap_version(dev) > 1 || + type == PCI_EXP_TYPE_ROOT_PORT || + type == PCI_EXP_TYPE_RC_EC; +} + +static bool __kc_pcie_capability_reg_implemented(struct pci_dev *dev, int pos) +{ + if (!pci_is_pcie(dev)) + return false; + + switch (pos) { + case PCI_EXP_FLAGS_TYPE: + return true; + case PCI_EXP_DEVCAP: + case PCI_EXP_DEVCTL: + case PCI_EXP_DEVSTA: + return __kc_pcie_cap_has_devctl(dev); + case PCI_EXP_LNKCAP: + case PCI_EXP_LNKCTL: + case PCI_EXP_LNKSTA: + return __kc_pcie_cap_has_lnkctl(dev); + case PCI_EXP_SLTCAP: + case PCI_EXP_SLTCTL: + case PCI_EXP_SLTSTA: + return __kc_pcie_cap_has_sltctl(dev); + case PCI_EXP_RTCTL: + case PCI_EXP_RTCAP: + case PCI_EXP_RTSTA: + return __kc_pcie_cap_has_rtctl(dev); + case PCI_EXP_DEVCAP2: + case PCI_EXP_DEVCTL2: + case PCI_EXP_LNKCAP2: + case PCI_EXP_LNKCTL2: + case PCI_EXP_LNKSTA2: + return __kc_pcie_cap_version(dev) > 1; + default: + return false; + } +} + +/* + * Note that these accessor functions are only for the "PCI Express + * Capability" (see PCIe spec r3.0, sec 7.8). They do not apply to the + * other "PCI Express Extended Capabilities" (AER, VC, ACS, MFVC, etc.) + */ +int __kc_pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val) +{ + int ret; + + *val = 0; + if (pos & 1) + return -EINVAL; + + if (__kc_pcie_capability_reg_implemented(dev, pos)) { + ret = pci_read_config_word(dev, pci_pcie_cap(dev) + pos, val); + /* + * Reset *val to 0 if pci_read_config_word() fails, it may + * have been written as 0xFFFF if hardware error happens + * during pci_read_config_word(). + */ + if (ret) + *val = 0; + return ret; + } + + /* + * For Functions that do not implement the Slot Capabilities, + * Slot Status, and Slot Control registers, these spaces must + * be hardwired to 0b, with the exception of the Presence Detect + * State bit in the Slot Status register of Downstream Ports, + * which must be hardwired to 1b. (PCIe Base Spec 3.0, sec 7.8) + */ + if (pci_is_pcie(dev) && pos == PCI_EXP_SLTSTA && + pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) { + *val = PCI_EXP_SLTSTA_PDS; + } + + return 0; +} + +int __kc_pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val) +{ + int ret; + + *val = 0; + if (pos & 3) + return -EINVAL; + + if (__kc_pcie_capability_reg_implemented(dev, pos)) { + ret = pci_read_config_dword(dev, pci_pcie_cap(dev) + pos, val); + /* + * Reset *val to 0 if pci_read_config_dword() fails, it may + * have been written as 0xFFFFFFFF if hardware error happens + * during pci_read_config_dword(). + */ + if (ret) + *val = 0; + return ret; + } + + if (pci_is_pcie(dev) && pos == PCI_EXP_SLTSTA && + pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) { + *val = PCI_EXP_SLTSTA_PDS; + } + + return 0; +} + +int __kc_pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val) +{ + if (pos & 1) + return -EINVAL; + + if (!__kc_pcie_capability_reg_implemented(dev, pos)) + return 0; + + return pci_write_config_word(dev, pci_pcie_cap(dev) + pos, val); +} + +int __kc_pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos, + u16 clear, u16 set) +{ + int ret; + u16 val; + + ret = __kc_pcie_capability_read_word(dev, pos, &val); + if (!ret) { + val &= ~clear; + val |= set; + ret = __kc_pcie_capability_write_word(dev, pos, val); + } + + return ret; +} + +int __kc_pcie_capability_clear_word(struct pci_dev *dev, int pos, + u16 clear) +{ + return __kc_pcie_capability_clear_and_set_word(dev, pos, clear, 0); +} +#endif /* < 3.7.0 */ + +/******************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) ) +#ifdef CONFIG_XPS +#if NR_CPUS < 64 +#define _KC_MAX_XPS_CPUS NR_CPUS +#else +#define _KC_MAX_XPS_CPUS 64 +#endif + +/* + * netdev_queue sysfs structures and functions. + */ +struct _kc_netdev_queue_attribute { + struct attribute attr; + ssize_t (*show)(struct netdev_queue *queue, + struct _kc_netdev_queue_attribute *attr, char *buf); + ssize_t (*store)(struct netdev_queue *queue, + struct _kc_netdev_queue_attribute *attr, const char *buf, size_t len); +}; + +#define to_kc_netdev_queue_attr(_attr) container_of(_attr, \ + struct _kc_netdev_queue_attribute, attr) + +int __kc_netif_set_xps_queue(struct net_device *dev, const struct cpumask *mask, + u16 index) +{ + struct netdev_queue *txq = netdev_get_tx_queue(dev, index); +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) ) + /* Redhat requires some odd extended netdev structures */ + struct netdev_tx_queue_extended *txq_ext = + netdev_extended(dev)->_tx_ext + index; + struct kobj_type *ktype = txq_ext->kobj.ktype; +#else + struct kobj_type *ktype = txq->kobj.ktype; +#endif + struct _kc_netdev_queue_attribute *xps_attr; + struct attribute *attr = NULL; + int i, len, err; +#define _KC_XPS_BUFLEN (DIV_ROUND_UP(_KC_MAX_XPS_CPUS, 32) * 9) + char buf[_KC_XPS_BUFLEN]; + + if (!ktype) + return -ENOMEM; + + /* attempt to locate the XPS attribute in the Tx queue */ + for (i = 0; (attr = ktype->default_attrs[i]); i++) { + if (!strcmp("xps_cpus", attr->name)) + break; + } + + /* if we did not find it return an error */ + if (!attr) + return -EINVAL; + + /* copy the mask into a string */ + len = bitmap_scnprintf(buf, _KC_XPS_BUFLEN, + cpumask_bits(mask), _KC_MAX_XPS_CPUS); + if (!len) + return -ENOMEM; + + xps_attr = to_kc_netdev_queue_attr(attr); + + /* Store the XPS value using the SYSFS store call */ + err = xps_attr->store(txq, xps_attr, buf, len); + + /* we only had an error on err < 0 */ + return (err < 0) ? err : 0; +} +#endif /* CONFIG_XPS */ +#ifdef HAVE_NETDEV_SELECT_QUEUE +static inline int kc_get_xps_queue(struct net_device *dev, struct sk_buff *skb) +{ +#ifdef CONFIG_XPS + struct xps_dev_maps *dev_maps; + struct xps_map *map; + int queue_index = -1; + + rcu_read_lock(); +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) ) + /* Redhat requires some odd extended netdev structures */ + dev_maps = rcu_dereference(netdev_extended(dev)->xps_maps); +#else + dev_maps = rcu_dereference(dev->xps_maps); +#endif + if (dev_maps) { + map = rcu_dereference( + dev_maps->cpu_map[raw_smp_processor_id()]); + if (map) { + if (map->len == 1) + queue_index = map->queues[0]; + else { + u32 hash; + if (skb->sk && skb->sk->sk_hash) + hash = skb->sk->sk_hash; + else + hash = (__force u16) skb->protocol ^ + skb->rxhash; + hash = jhash_1word(hash, _kc_hashrnd); + queue_index = map->queues[ + ((u64)hash * map->len) >> 32]; + } + if (unlikely(queue_index >= dev->real_num_tx_queues)) + queue_index = -1; + } + } + rcu_read_unlock(); + + return queue_index; +#else + return -1; +#endif +} + +u16 __kc_netdev_pick_tx(struct net_device *dev, struct sk_buff *skb) +{ + struct sock *sk = skb->sk; + int queue_index = sk_tx_queue_get(sk); + int new_index; + + if (queue_index >= 0 && queue_index < dev->real_num_tx_queues) { +#ifdef CONFIG_XPS + if (!skb->ooo_okay) +#endif + return queue_index; + } + + new_index = kc_get_xps_queue(dev, skb); + if (new_index < 0) + new_index = skb_tx_hash(dev, skb); + + if (queue_index != new_index && sk) { + struct dst_entry *dst = + rcu_dereference(sk->sk_dst_cache); + + if (dst && skb_dst(skb) == dst) + sk_tx_queue_set(sk, new_index); + + } + + return new_index; +} + +#endif /* HAVE_NETDEV_SELECT_QUEUE */ +#endif /* 3.9.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ) +#ifdef HAVE_FDB_OPS +#ifdef USE_CONST_DEV_UC_CHAR +int __kc_ndo_dflt_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, const unsigned char *addr, + u16 flags) +#else +int __kc_ndo_dflt_fdb_add(struct ndmsg *ndm, struct net_device *dev, + unsigned char *addr, u16 flags) +#endif +{ + int err = -EINVAL; + + /* If aging addresses are supported device will need to + * implement its own handler for this. + */ + if (ndm->ndm_state && !(ndm->ndm_state & NUD_PERMANENT)) { + pr_info("%s: FDB only supports static addresses\n", dev->name); + return err; + } + + if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) + err = dev_uc_add_excl(dev, addr); + else if (is_multicast_ether_addr(addr)) + err = dev_mc_add_excl(dev, addr); + + /* Only return duplicate errors if NLM_F_EXCL is set */ + if (err == -EEXIST && !(flags & NLM_F_EXCL)) + err = 0; + + return err; +} + +#ifdef USE_CONST_DEV_UC_CHAR +#ifdef HAVE_FDB_DEL_NLATTR +int __kc_ndo_dflt_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, const unsigned char *addr) +#else +int __kc_ndo_dflt_fdb_del(struct ndmsg *ndm, struct net_device *dev, + const unsigned char *addr) +#endif +#else +int __kc_ndo_dflt_fdb_del(struct ndmsg *ndm, struct net_device *dev, + unsigned char *addr) +#endif +{ + int err = -EINVAL; + + /* If aging addresses are supported device will need to + * implement its own handler for this. + */ + if (!(ndm->ndm_state & NUD_PERMANENT)) { + pr_info("%s: FDB only supports static addresses\n", dev->name); + return err; + } + + if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) + err = dev_uc_del(dev, addr); + else if (is_multicast_ether_addr(addr)) + err = dev_mc_del(dev, addr); + + return err; +} + +#endif /* HAVE_FDB_OPS */ +#ifdef CONFIG_PCI_IOV +int __kc_pci_vfs_assigned(struct pci_dev __maybe_unused *dev) +{ + unsigned int vfs_assigned = 0; +#ifdef HAVE_PCI_DEV_FLAGS_ASSIGNED + int pos; + struct pci_dev *vfdev; + unsigned short dev_id; + + /* only search if we are a PF */ + if (!dev->is_physfn) + return 0; + + /* find SR-IOV capability */ + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); + if (!pos) + return 0; + + /* + * determine the device ID for the VFs, the vendor ID will be the + * same as the PF so there is no need to check for that one + */ + pci_read_config_word(dev, pos + PCI_SRIOV_VF_DID, &dev_id); + + /* loop through all the VFs to see if we own any that are assigned */ + vfdev = pci_get_device(dev->vendor, dev_id, NULL); + while (vfdev) { + /* + * It is considered assigned if it is a virtual function with + * our dev as the physical function and the assigned bit is set + */ + if (vfdev->is_virtfn && (vfdev->physfn == dev) && + (vfdev->dev_flags & PCI_DEV_FLAGS_ASSIGNED)) + vfs_assigned++; + + vfdev = pci_get_device(dev->vendor, dev_id, vfdev); + } + +#endif /* HAVE_PCI_DEV_FLAGS_ASSIGNED */ + return vfs_assigned; +} + +#endif /* CONFIG_PCI_IOV */ +#endif /* 3.10.0 */ + +static const unsigned char __maybe_unused pcie_link_speed[] = { + PCI_SPEED_UNKNOWN, /* 0 */ + PCIE_SPEED_2_5GT, /* 1 */ + PCIE_SPEED_5_0GT, /* 2 */ + PCIE_SPEED_8_0GT, /* 3 */ + PCIE_SPEED_16_0GT, /* 4 */ + PCI_SPEED_UNKNOWN, /* 5 */ + PCI_SPEED_UNKNOWN, /* 6 */ + PCI_SPEED_UNKNOWN, /* 7 */ + PCI_SPEED_UNKNOWN, /* 8 */ + PCI_SPEED_UNKNOWN, /* 9 */ + PCI_SPEED_UNKNOWN, /* A */ + PCI_SPEED_UNKNOWN, /* B */ + PCI_SPEED_UNKNOWN, /* C */ + PCI_SPEED_UNKNOWN, /* D */ + PCI_SPEED_UNKNOWN, /* E */ + PCI_SPEED_UNKNOWN /* F */ +}; + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0) ) +int __kc_pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, + enum pcie_link_width *width) +{ + int ret; + + *speed = PCI_SPEED_UNKNOWN; + *width = PCIE_LNK_WIDTH_UNKNOWN; + + while (dev) { + u16 lnksta; + enum pci_bus_speed next_speed; + enum pcie_link_width next_width; + + ret = pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + if (ret) + return ret; + + next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS]; + next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> + PCI_EXP_LNKSTA_NLW_SHIFT; + + if (next_speed < *speed) + *speed = next_speed; + + if (next_width < *width) + *width = next_width; + + dev = dev->bus->self; + } + + return 0; +} + +#if (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(6,7)) +int _kc_pci_wait_for_pending_transaction(struct pci_dev *dev) +{ + int i; + u16 status; + + /* Wait for Transaction Pending bit clean */ + for (i = 0; i < 4; i++) { + if (i) + msleep((1 << (i - 1)) * 100); + + pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status); + if (!(status & PCI_EXP_DEVSTA_TRPND)) + return 1; + } + + return 0; +} +#endif /* nexthdr; + unsigned int len; + bool found; + +#define __KC_IP6_FH_F_FRAG BIT(0) +#define __KC_IP6_FH_F_AUTH BIT(1) +#define __KC_IP6_FH_F_SKIP_RH BIT(2) + + if (fragoff) + *fragoff = 0; + + if (*offset) { + struct ipv6hdr _ip6, *ip6; + + ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); + if (!ip6 || (ip6->version != 6)) { + printk(KERN_ERR "IPv6 header not found\n"); + return -EBADMSG; + } + start = *offset + sizeof(struct ipv6hdr); + nexthdr = ip6->nexthdr; + } + len = skb->len - start; + + do { + struct ipv6_opt_hdr _hdr, *hp; + unsigned int hdrlen; + found = (nexthdr == target); + + if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { + if (target < 0 || found) + break; + return -ENOENT; + } + + hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); + if (!hp) + return -EBADMSG; + + if (nexthdr == NEXTHDR_ROUTING) { + struct ipv6_rt_hdr _rh, *rh; + + rh = skb_header_pointer(skb, start, sizeof(_rh), + &_rh); + if (!rh) + return -EBADMSG; + + if (flags && (*flags & __KC_IP6_FH_F_SKIP_RH) && + rh->segments_left == 0) + found = false; + } + + if (nexthdr == NEXTHDR_FRAGMENT) { + unsigned short _frag_off; + __be16 *fp; + + if (flags) /* Indicate that this is a fragment */ + *flags |= __KC_IP6_FH_F_FRAG; + fp = skb_header_pointer(skb, + start+offsetof(struct frag_hdr, + frag_off), + sizeof(_frag_off), + &_frag_off); + if (!fp) + return -EBADMSG; + + _frag_off = ntohs(*fp) & ~0x7; + if (_frag_off) { + if (target < 0 && + ((!ipv6_ext_hdr(hp->nexthdr)) || + hp->nexthdr == NEXTHDR_NONE)) { + if (fragoff) + *fragoff = _frag_off; + return hp->nexthdr; + } + return -ENOENT; + } + hdrlen = 8; + } else if (nexthdr == NEXTHDR_AUTH) { + if (flags && (*flags & __KC_IP6_FH_F_AUTH) && (target < 0)) + break; + hdrlen = (hp->hdrlen + 2) << 2; + } else + hdrlen = ipv6_optlen(hp); + + if (!found) { + nexthdr = hp->nexthdr; + len -= hdrlen; + start += hdrlen; + } + } while (!found); + + *offset = start; + return nexthdr; +} + +int __kc_pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, + int minvec, int maxvec) +{ + int nvec = maxvec; + int rc; + + if (maxvec < minvec) + return -ERANGE; + + do { + rc = pci_enable_msix(dev, entries, nvec); + if (rc < 0) { + return rc; + } else if (rc > 0) { + if (rc < minvec) + return -ENOSPC; + nvec = rc; + } + } while (rc); + + return nvec; +} +#endif /* 3.14.0 */ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0)) +char *_kc_devm_kstrdup(struct device *dev, const char *s, gfp_t gfp) +{ + size_t size; + char *buf; + + if (!s) + return NULL; + + size = strlen(s) + 1; + buf = devm_kzalloc(dev, size, gfp); + if (buf) + memcpy(buf, s, size); + return buf; +} + +void __kc_netdev_rss_key_fill(void *buffer, size_t len) +{ + /* Set of random keys generated using kernel random number generator */ + static const u8 seed[NETDEV_RSS_KEY_LEN] = {0xE6, 0xFA, 0x35, 0x62, + 0x95, 0x12, 0x3E, 0xA3, 0xFB, 0x46, 0xC1, 0x5F, + 0xB1, 0x43, 0x82, 0x5B, 0x6A, 0x49, 0x50, 0x95, + 0xCD, 0xAB, 0xD8, 0x11, 0x8F, 0xC5, 0xBD, 0xBC, + 0x6A, 0x4A, 0xB2, 0xD4, 0x1F, 0xFE, 0xBC, 0x41, + 0xBF, 0xAC, 0xB2, 0x9A, 0x8F, 0x70, 0xE9, 0x2A, + 0xD7, 0xB2, 0x80, 0xB6, 0x5B, 0xAA, 0x9D, 0x20}; + + BUG_ON(len > NETDEV_RSS_KEY_LEN); + memcpy(buffer, seed, len); +} +#endif /* 3.15.0 */ + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) ) +#ifdef HAVE_SET_RX_MODE +#ifdef NETDEV_HW_ADDR_T_UNICAST +int __kc_hw_addr_sync_dev(struct netdev_hw_addr_list *list, + struct net_device *dev, + int (*sync)(struct net_device *, const unsigned char *), + int (*unsync)(struct net_device *, const unsigned char *)) +{ + struct netdev_hw_addr *ha, *tmp; + int err; + + /* first go through and flush out any stale entries */ + list_for_each_entry_safe(ha, tmp, &list->list, list) { +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ) + if (!ha->synced || ha->refcount != 1) +#else + if (!ha->sync_cnt || ha->refcount != 1) +#endif + continue; + + if (unsync && unsync(dev, ha->addr)) + continue; + + list_del_rcu(&ha->list); + kfree_rcu(ha, rcu_head); + list->count--; + } + + /* go through and sync new entries to the list */ + list_for_each_entry_safe(ha, tmp, &list->list, list) { +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ) + if (ha->synced) +#else + if (ha->sync_cnt) +#endif + continue; + + err = sync(dev, ha->addr); + if (err) + return err; +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ) + ha->synced = true; +#else + ha->sync_cnt++; +#endif + ha->refcount++; + } + + return 0; +} + +void __kc_hw_addr_unsync_dev(struct netdev_hw_addr_list *list, + struct net_device *dev, + int (*unsync)(struct net_device *, const unsigned char *)) +{ + struct netdev_hw_addr *ha, *tmp; + + list_for_each_entry_safe(ha, tmp, &list->list, list) { +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ) + if (!ha->synced) +#else + if (!ha->sync_cnt) +#endif + continue; + + if (unsync && unsync(dev, ha->addr)) + continue; + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ) + ha->synced = false; +#else + ha->sync_cnt--; +#endif + if (--ha->refcount) + continue; + + list_del_rcu(&ha->list); + kfree_rcu(ha, rcu_head); + list->count--; + } +} + +#endif /* NETDEV_HW_ADDR_T_UNICAST */ +#ifndef NETDEV_HW_ADDR_T_MULTICAST +int __kc_dev_addr_sync_dev(struct dev_addr_list **list, int *count, + struct net_device *dev, + int (*sync)(struct net_device *, const unsigned char *), + int (*unsync)(struct net_device *, const unsigned char *)) +{ + struct dev_addr_list *da, **next = list; + int err; + + /* first go through and flush out any stale entries */ + while ((da = *next) != NULL) { + if (da->da_synced && da->da_users == 1) { + if (!unsync || !unsync(dev, da->da_addr)) { + *next = da->next; + kfree(da); + (*count)--; + continue; + } + } + next = &da->next; + } + + /* go through and sync new entries to the list */ + for (da = *list; da != NULL; da = da->next) { + if (da->da_synced) + continue; + + err = sync(dev, da->da_addr); + if (err) + return err; + + da->da_synced++; + da->da_users++; + } + + return 0; +} + +void __kc_dev_addr_unsync_dev(struct dev_addr_list **list, int *count, + struct net_device *dev, + int (*unsync)(struct net_device *, const unsigned char *)) +{ + struct dev_addr_list *da; + + while ((da = *list) != NULL) { + if (da->da_synced) { + if (!unsync || !unsync(dev, da->da_addr)) { + da->da_synced--; + if (--da->da_users == 0) { + *list = da->next; + kfree(da); + (*count)--; + continue; + } + } + } + list = &da->next; + } +} +#endif /* NETDEV_HW_ADDR_T_MULTICAST */ +#endif /* HAVE_SET_RX_MODE */ +void *__kc_devm_kmemdup(struct device *dev, const void *src, size_t len, + gfp_t gfp) +{ + void *p; + + p = devm_kzalloc(dev, len, gfp); + if (p) + memcpy(p, src, len); + + return p; +} +#endif /* 3.16.0 */ + +/******************************************************************************/ +#if ((LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,5))) +#endif /* <3.17.0 && RHEL_RELEASE_CODE < RHEL7.5 */ + +/******************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) ) +#ifndef NO_PTP_SUPPORT +static void __kc_sock_efree(struct sk_buff *skb) +{ + sock_put(skb->sk); +} + +struct sk_buff *__kc_skb_clone_sk(struct sk_buff *skb) +{ + struct sock *sk = skb->sk; + struct sk_buff *clone; + + if (!sk || !atomic_inc_not_zero(&sk->sk_refcnt)) + return NULL; + + clone = skb_clone(skb, GFP_ATOMIC); + if (!clone) { + sock_put(sk); + return NULL; + } + + clone->sk = sk; + clone->destructor = __kc_sock_efree; + + return clone; +} + +void __kc_skb_complete_tx_timestamp(struct sk_buff *skb, + struct skb_shared_hwtstamps *hwtstamps) +{ + struct sock_exterr_skb *serr; + struct sock *sk = skb->sk; + int err; + + sock_hold(sk); + + *skb_hwtstamps(skb) = *hwtstamps; + + serr = SKB_EXT_ERR(skb); + memset(serr, 0, sizeof(*serr)); + serr->ee.ee_errno = ENOMSG; + serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING; + + err = sock_queue_err_skb(sk, skb); + if (err) + kfree_skb(skb); + + sock_put(sk); +} +#endif + +/* include headers needed for get_headlen function */ +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +#include +#endif +#ifdef HAVE_SCTP +#include +#endif + +u32 __kc_eth_get_headlen(const struct net_device __always_unused *dev, + unsigned char *data, unsigned int max_len) +{ + union { + unsigned char *network; + /* l2 headers */ + struct ethhdr *eth; + struct vlan_hdr *vlan; + /* l3 headers */ + struct iphdr *ipv4; + struct ipv6hdr *ipv6; + } hdr; + __be16 proto; + u8 nexthdr = 0; /* default to not TCP */ + u8 hlen; + + /* this should never happen, but better safe than sorry */ + if (max_len < ETH_HLEN) + return max_len; + + /* initialize network frame pointer */ + hdr.network = data; + + /* set first protocol and move network header forward */ + proto = hdr.eth->h_proto; + hdr.network += ETH_HLEN; + +again: + switch (proto) { + /* handle any vlan tag if present */ + case __constant_htons(ETH_P_8021AD): + case __constant_htons(ETH_P_8021Q): + if ((hdr.network - data) > (max_len - VLAN_HLEN)) + return max_len; + + proto = hdr.vlan->h_vlan_encapsulated_proto; + hdr.network += VLAN_HLEN; + goto again; + /* handle L3 protocols */ + case __constant_htons(ETH_P_IP): + if ((hdr.network - data) > (max_len - sizeof(struct iphdr))) + return max_len; + + /* access ihl as a u8 to avoid unaligned access on ia64 */ + hlen = (hdr.network[0] & 0x0F) << 2; + + /* verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct iphdr)) + return hdr.network - data; + + /* record next protocol if header is present */ + if (!(hdr.ipv4->frag_off & htons(IP_OFFSET))) + nexthdr = hdr.ipv4->protocol; + + hdr.network += hlen; + break; +#ifdef NETIF_F_TSO6 + case __constant_htons(ETH_P_IPV6): + if ((hdr.network - data) > (max_len - sizeof(struct ipv6hdr))) + return max_len; + + /* record next protocol */ + nexthdr = hdr.ipv6->nexthdr; + hdr.network += sizeof(struct ipv6hdr); + break; +#endif /* NETIF_F_TSO6 */ +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) + case __constant_htons(ETH_P_FCOE): + hdr.network += FCOE_HEADER_LEN; + break; +#endif + default: + return hdr.network - data; + } + + /* finally sort out L4 */ + switch (nexthdr) { + case IPPROTO_TCP: + if ((hdr.network - data) > (max_len - sizeof(struct tcphdr))) + return max_len; + + /* access doff as a u8 to avoid unaligned access on ia64 */ + hdr.network += max_t(u8, sizeof(struct tcphdr), + (hdr.network[12] & 0xF0) >> 2); + + break; + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + hdr.network += sizeof(struct udphdr); + break; +#ifdef HAVE_SCTP + case IPPROTO_SCTP: + hdr.network += sizeof(struct sctphdr); + break; +#endif + } + + /* + * If everything has gone correctly hdr.network should be the + * data section of the packet and will be the end of the header. + * If not then it probably represents the end of the last recognized + * header. + */ + return min_t(unsigned int, hdr.network - data, max_len); +} + +#endif /* < 3.18.0 */ + +/******************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) ) +#ifdef HAVE_NET_GET_RANDOM_ONCE +static u8 __kc_netdev_rss_key[NETDEV_RSS_KEY_LEN]; + +void __kc_netdev_rss_key_fill(void *buffer, size_t len) +{ + BUG_ON(len > sizeof(__kc_netdev_rss_key)); + net_get_random_once(__kc_netdev_rss_key, sizeof(__kc_netdev_rss_key)); + memcpy(buffer, __kc_netdev_rss_key, len); +} +#endif + +int _kc_bitmap_print_to_pagebuf(bool list, char *buf, + const unsigned long *maskp, + int nmaskbits) +{ + ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf - 2; + int n = 0; + + if (len > 1) { + n = list ? bitmap_scnlistprintf(buf, len, maskp, nmaskbits) : + bitmap_scnprintf(buf, len, maskp, nmaskbits); + buf[n++] = '\n'; + buf[n] = '\0'; + } + return n; +} +#endif + +/******************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0) ) +#if !((RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(6,8) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)) && \ + (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2)) && \ + (SLE_VERSION_CODE > SLE_VERSION(12,1,0))) +unsigned int _kc_cpumask_local_spread(unsigned int i, int node) +{ + int cpu; + + /* Wrap: we always want a cpu. */ + i %= num_online_cpus(); + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ) + /* Kernels prior to 2.6.28 do not have for_each_cpu or + * cpumask_of_node, so just use for_each_online_cpu() + */ + for_each_online_cpu(cpu) + if (i-- == 0) + return cpu; + + return 0; +#else + if (node == -1) { + for_each_cpu(cpu, cpu_online_mask) + if (i-- == 0) + return cpu; + } else { + /* NUMA first. */ + for_each_cpu_and(cpu, cpumask_of_node(node), cpu_online_mask) + if (i-- == 0) + return cpu; + + for_each_cpu(cpu, cpu_online_mask) { + /* Skip NUMA nodes, done above. */ + if (cpumask_test_cpu(cpu, cpumask_of_node(node))) + continue; + + if (i-- == 0) + return cpu; + } + } +#endif /* KERNEL_VERSION >= 2.6.28 */ + BUG(); +} +#endif +#endif + +/******************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0) ) +#if (!(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,3))) +#ifdef CONFIG_SPARC +#include +#include +#endif +int _kc_eth_platform_get_mac_address(struct device *dev __maybe_unused, + u8 *mac_addr __maybe_unused) +{ +#if (((LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)) && defined(CONFIG_OF) && \ + !defined(HAVE_STRUCT_DEVICE_OF_NODE) || !defined(CONFIG_OF)) && \ + !defined(CONFIG_SPARC)) + return -ENODEV; +#else + const unsigned char *addr; + struct device_node *dp; + + if (dev_is_pci(dev)) + dp = pci_device_to_OF_node(to_pci_dev(dev)); + else +#if defined(HAVE_STRUCT_DEVICE_OF_NODE) && defined(CONFIG_OF) + dp = dev->of_node; +#else + dp = NULL; +#endif + + addr = NULL; + if (dp) + addr = of_get_mac_address(dp); +#ifdef CONFIG_SPARC + /* Kernel hasn't implemented arch_get_platform_mac_address, but we + * should handle the SPARC case here since it was supported + * originally. This is replaced by arch_get_platform_mac_address() + * upstream. + */ + if (!addr) + addr = idprom->id_ethaddr; +#endif + if (!addr) + return -ENODEV; + + ether_addr_copy(mac_addr, addr); + return 0; +#endif +} +#endif /* !(RHEL_RELEASE >= 7.3) */ +#endif /* < 4.5.0 */ + +/*****************************************************************************/ +#if ((LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)) || \ + (SLE_VERSION_CODE && (SLE_VERSION_CODE <= SLE_VERSION(12,3,0))) || \ + (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE <= RHEL_RELEASE_VERSION(7,5)))) +const char *_kc_phy_speed_to_str(int speed) +{ + switch (speed) { + case SPEED_10: + return "10Mbps"; + case SPEED_100: + return "100Mbps"; + case SPEED_1000: + return "1Gbps"; + case SPEED_2500: + return "2.5Gbps"; + case SPEED_5000: + return "5Gbps"; + case SPEED_10000: + return "10Gbps"; + case SPEED_14000: + return "14Gbps"; + case SPEED_20000: + return "20Gbps"; + case SPEED_25000: + return "25Gbps"; + case SPEED_40000: + return "40Gbps"; + case SPEED_50000: + return "50Gbps"; + case SPEED_56000: + return "56Gbps"; +#ifdef SPEED_100000 + case SPEED_100000: + return "100Gbps"; +#endif + case SPEED_UNKNOWN: + return "Unknown"; + default: + return "Unsupported (update phy-core.c)"; + } +} +#endif /* (LINUX < 4.14.0) || (SLES <= 12.3.0) || (RHEL <= 7.5) */ + +/******************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0) ) +void _kc_ethtool_intersect_link_masks(struct ethtool_link_ksettings *dst, + struct ethtool_link_ksettings *src) +{ + unsigned int size = BITS_TO_LONGS(__ETHTOOL_LINK_MODE_MASK_NBITS); + unsigned int idx = 0; + + for (; idx < size; idx++) { + dst->link_modes.supported[idx] &= + src->link_modes.supported[idx]; + dst->link_modes.advertising[idx] &= + src->link_modes.advertising[idx]; + } +} +#endif /* 4.15.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0)) +/* PCIe link information */ +#define PCIE_SPEED2STR(speed) \ + ((speed) == PCIE_SPEED_16_0GT ? "16 GT/s" : \ + (speed) == PCIE_SPEED_8_0GT ? "8 GT/s" : \ + (speed) == PCIE_SPEED_5_0GT ? "5 GT/s" : \ + (speed) == PCIE_SPEED_2_5GT ? "2.5 GT/s" : \ + "Unknown speed") + +/* PCIe speed to Mb/s reduced by encoding overhead */ +#define PCIE_SPEED2MBS_ENC(speed) \ + ((speed) == PCIE_SPEED_16_0GT ? 16000*128/130 : \ + (speed) == PCIE_SPEED_8_0GT ? 8000*128/130 : \ + (speed) == PCIE_SPEED_5_0GT ? 5000*8/10 : \ + (speed) == PCIE_SPEED_2_5GT ? 2500*8/10 : \ + 0) + +static u32 +_kc_pcie_bandwidth_available(struct pci_dev *dev, + struct pci_dev **limiting_dev, + enum pci_bus_speed *speed, + enum pcie_link_width *width) +{ + u16 lnksta; + enum pci_bus_speed next_speed; + enum pcie_link_width next_width; + u32 bw, next_bw; + + if (speed) + *speed = PCI_SPEED_UNKNOWN; + if (width) + *width = PCIE_LNK_WIDTH_UNKNOWN; + + bw = 0; + + while (dev) { + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + + next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS]; + next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> + PCI_EXP_LNKSTA_NLW_SHIFT; + + next_bw = next_width * PCIE_SPEED2MBS_ENC(next_speed); + + /* Check if current device limits the total bandwidth */ + if (!bw || next_bw <= bw) { + bw = next_bw; + + if (limiting_dev) + *limiting_dev = dev; + if (speed) + *speed = next_speed; + if (width) + *width = next_width; + } + + dev = pci_upstream_bridge(dev); + } + + return bw; +} + +static enum pci_bus_speed _kc_pcie_get_speed_cap(struct pci_dev *dev) +{ + u32 lnkcap2, lnkcap; + + /* + * PCIe r4.0 sec 7.5.3.18 recommends using the Supported Link + * Speeds Vector in Link Capabilities 2 when supported, falling + * back to Max Link Speed in Link Capabilities otherwise. + */ + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP2, &lnkcap2); + if (lnkcap2) { /* PCIe r3.0-compliant */ + if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_16_0GB) + return PCIE_SPEED_16_0GT; + else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) + return PCIE_SPEED_8_0GT; + else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB) + return PCIE_SPEED_5_0GT; + else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB) + return PCIE_SPEED_2_5GT; + return PCI_SPEED_UNKNOWN; + } + + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); + if (lnkcap) { + if (lnkcap & PCI_EXP_LNKCAP_SLS_16_0GB) + return PCIE_SPEED_16_0GT; + else if (lnkcap & PCI_EXP_LNKCAP_SLS_8_0GB) + return PCIE_SPEED_8_0GT; + else if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB) + return PCIE_SPEED_5_0GT; + else if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB) + return PCIE_SPEED_2_5GT; + } + + return PCI_SPEED_UNKNOWN; +} + +static enum pcie_link_width _kc_pcie_get_width_cap(struct pci_dev *dev) +{ + u32 lnkcap; + + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); + if (lnkcap) + return (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4; + + return PCIE_LNK_WIDTH_UNKNOWN; +} + +static u32 +_kc_pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed, + enum pcie_link_width *width) +{ + *speed = _kc_pcie_get_speed_cap(dev); + *width = _kc_pcie_get_width_cap(dev); + + if (*speed == PCI_SPEED_UNKNOWN || *width == PCIE_LNK_WIDTH_UNKNOWN) + return 0; + + return *width * PCIE_SPEED2MBS_ENC(*speed); +} + +void _kc_pcie_print_link_status(struct pci_dev *dev) { + enum pcie_link_width width, width_cap; + enum pci_bus_speed speed, speed_cap; + struct pci_dev *limiting_dev = NULL; + u32 bw_avail, bw_cap; + + bw_cap = _kc_pcie_bandwidth_capable(dev, &speed_cap, &width_cap); + bw_avail = _kc_pcie_bandwidth_available(dev, &limiting_dev, &speed, + &width); + + if (bw_avail >= bw_cap) + pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth (%s x%d link)\n", + bw_cap / 1000, bw_cap % 1000, + PCIE_SPEED2STR(speed_cap), width_cap); + else + pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth, limited by %s x%d link at %s (capable of %u.%03u Gb/s with %s x%d link)\n", + bw_avail / 1000, bw_avail % 1000, + PCIE_SPEED2STR(speed), width, + limiting_dev ? pci_name(limiting_dev) : "", + bw_cap / 1000, bw_cap % 1000, + PCIE_SPEED2STR(speed_cap), width_cap); +} +#endif /* 4.17.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,1,0)) +#if (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,1))) +#define HAVE_NDO_FDB_ADD_EXTACK +#else /* !RHEL || RHEL < 8.1 */ +#ifdef HAVE_TC_SETUP_CLSFLOWER +#define FLOW_DISSECTOR_MATCH(__rule, __type, __out) \ + const struct flow_match *__m = &(__rule)->match; \ + struct flow_dissector *__d = (__m)->dissector; \ + \ + (__out)->key = skb_flow_dissector_target(__d, __type, (__m)->key); \ + (__out)->mask = skb_flow_dissector_target(__d, __type, (__m)->mask); \ + +void flow_rule_match_basic(const struct flow_rule *rule, + struct flow_match_basic *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_BASIC, out); +} + +void flow_rule_match_control(const struct flow_rule *rule, + struct flow_match_control *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CONTROL, out); +} + +void flow_rule_match_eth_addrs(const struct flow_rule *rule, + struct flow_match_eth_addrs *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS, out); +} + +#ifdef HAVE_TC_FLOWER_ENC +void flow_rule_match_enc_keyid(const struct flow_rule *rule, + struct flow_match_enc_keyid *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_KEYID, out); +} + +void flow_rule_match_enc_ports(const struct flow_rule *rule, + struct flow_match_ports *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_PORTS, out); +} + +void flow_rule_match_enc_control(const struct flow_rule *rule, + struct flow_match_control *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_CONTROL, out); +} + +void flow_rule_match_enc_ipv4_addrs(const struct flow_rule *rule, + struct flow_match_ipv4_addrs *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, out); +} + +void flow_rule_match_enc_ipv6_addrs(const struct flow_rule *rule, + struct flow_match_ipv6_addrs *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, out); +} +#endif + +#ifndef HAVE_TC_FLOWER_VLAN_IN_TAGS +void flow_rule_match_vlan(const struct flow_rule *rule, + struct flow_match_vlan *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_VLAN, out); +} +#endif + +void flow_rule_match_ipv4_addrs(const struct flow_rule *rule, + struct flow_match_ipv4_addrs *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS, out); +} + +void flow_rule_match_ipv6_addrs(const struct flow_rule *rule, + struct flow_match_ipv6_addrs *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS, out); +} + +void flow_rule_match_ports(const struct flow_rule *rule, + struct flow_match_ports *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_PORTS, out); +} +#endif /* HAVE_TC_SETUP_CLSFLOWER */ +#endif /* !RHEL || RHEL < 8.1 */ +#endif /* 5.1.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,3,0)) +#if (!(RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,2)))) +#ifdef HAVE_TC_CB_AND_SETUP_QDISC_MQPRIO +int _kc_flow_block_cb_setup_simple(struct flow_block_offload *f, + struct list_head __always_unused *driver_list, + tc_setup_cb_t *cb, + void *cb_ident, void *cb_priv, + bool ingress_only) +{ + if (ingress_only && + f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS) + return -EOPNOTSUPP; + + /* Note: Upstream has driver_block_list, but older kernels do not */ + switch (f->command) { + case TC_BLOCK_BIND: +#ifdef HAVE_TCF_BLOCK_CB_REGISTER_EXTACK + return tcf_block_cb_register(f->block, cb, cb_ident, cb_priv, + f->extack); +#else + return tcf_block_cb_register(f->block, cb, cb_ident, cb_priv); +#endif + case TC_BLOCK_UNBIND: + tcf_block_cb_unregister(f->block, cb, cb_ident); + return 0; + default: + return -EOPNOTSUPP; + } +} +#endif /* HAVE_TC_CB_AND_SETUP_QDISC_MQPRIO */ +#endif /* !RHEL >= 8.2 */ +#endif /* 5.3.0 */ diff --git a/drivers/net/ethernet/intel/i40e/kcompat.h b/drivers/net/ethernet/intel/i40e/kcompat.h new file mode 100644 index 000000000..a5cee4859 --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/kcompat.h @@ -0,0 +1,6838 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ + +#ifndef _KCOMPAT_H_ +#define _KCOMPAT_H_ + +#ifndef LINUX_VERSION_CODE +#include +#else +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GCC_VERSION +#define GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) +#endif /* GCC_VERSION */ + +/* Backport macros for controlling GCC diagnostics */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,18,0) ) + +/* Compilers before gcc-4.6 do not understand "#pragma GCC diagnostic push" */ +#if GCC_VERSION >= 40600 +#define __diag_str1(s) #s +#define __diag_str(s) __diag_str1(s) +#define __diag(s) _Pragma(__diag_str(GCC diagnostic s)) +#else +#define __diag(s) +#endif /* GCC_VERSION >= 4.6 */ +#define __diag_push() __diag(push) +#define __diag_pop() __diag(pop) +#endif /* LINUX_VERSION < 4.18.0 */ + +#ifndef NSEC_PER_MSEC +#define NSEC_PER_MSEC 1000000L +#endif +#include +/* UTS_RELEASE is in a different header starting in kernel 2.6.18 */ +#ifndef UTS_RELEASE +/* utsrelease.h changed locations in 2.6.33 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) ) +#include +#else +#include +#endif +#endif + +/* NAPI enable/disable flags here */ +#define NAPI + +#define adapter_struct i40e_pf +#define adapter_q_vector i40e_q_vector + +/* and finally set defines so that the code sees the changes */ +#ifdef NAPI +#ifndef CONFIG_I40E_NAPI +#define CONFIG_I40E_NAPI +#endif +#else +#undef CONFIG_I40E_NAPI +#endif /* NAPI */ + +/* Dynamic LTR and deeper C-State support disable/enable */ + +/* packet split disable/enable */ +#ifdef DISABLE_PACKET_SPLIT +#ifndef CONFIG_I40E_DISABLE_PACKET_SPLIT +#define CONFIG_I40E_DISABLE_PACKET_SPLIT +#endif +#endif /* DISABLE_PACKET_SPLIT */ + +/* MSI compatibility code for all kernels and drivers */ +#ifdef DISABLE_PCI_MSI +#undef CONFIG_PCI_MSI +#endif +#ifndef CONFIG_PCI_MSI +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) ) +struct msix_entry { + u16 vector; /* kernel uses to write allocated vector */ + u16 entry; /* driver uses to specify entry, OS writes */ +}; +#endif +#undef pci_enable_msi +#define pci_enable_msi(a) -ENOTSUPP +#undef pci_disable_msi +#define pci_disable_msi(a) do {} while (0) +#undef pci_enable_msix +#define pci_enable_msix(a, b, c) -ENOTSUPP +#undef pci_disable_msix +#define pci_disable_msix(a) do {} while (0) +#define msi_remove_pci_irq_vectors(a) do {} while (0) +#endif /* CONFIG_PCI_MSI */ +#ifdef DISABLE_PM +#undef CONFIG_PM +#endif + +#ifdef DISABLE_NET_POLL_CONTROLLER +#undef CONFIG_NET_POLL_CONTROLLER +#endif + +#ifndef PMSG_SUSPEND +#define PMSG_SUSPEND 3 +#endif + +/* generic boolean compatibility */ +#undef TRUE +#undef FALSE +#define TRUE true +#define FALSE false +#ifdef GCC_VERSION +#if ( GCC_VERSION < 3000 ) +#define _Bool char +#endif +#else +#define _Bool char +#endif + +#ifndef BIT +#define BIT(nr) (1UL << (nr)) +#endif + +#undef __always_unused +#define __always_unused __attribute__((__unused__)) + +#undef __maybe_unused +#define __maybe_unused __attribute__((__unused__)) + +/* kernels less than 2.4.14 don't have this */ +#ifndef ETH_P_8021Q +#define ETH_P_8021Q 0x8100 +#endif + +#ifndef module_param +#define module_param(v,t,p) MODULE_PARM(v, "i"); +#endif + +#ifndef DMA_64BIT_MASK +#define DMA_64BIT_MASK 0xffffffffffffffffULL +#endif + +#ifndef DMA_32BIT_MASK +#define DMA_32BIT_MASK 0x00000000ffffffffULL +#endif + +#ifndef PCI_CAP_ID_EXP +#define PCI_CAP_ID_EXP 0x10 +#endif + +#ifndef uninitialized_var +#define uninitialized_var(x) x = x +#endif + +#ifndef PCIE_LINK_STATE_L0S +#define PCIE_LINK_STATE_L0S 1 +#endif +#ifndef PCIE_LINK_STATE_L1 +#define PCIE_LINK_STATE_L1 2 +#endif + +#ifndef SET_NETDEV_DEV +#define SET_NETDEV_DEV(net, pdev) +#endif + +#if !defined(HAVE_FREE_NETDEV) && ( LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0) ) +#define free_netdev(x) kfree(x) +#endif + +#ifdef HAVE_POLL_CONTROLLER +#define CONFIG_NET_POLL_CONTROLLER +#endif + +#ifndef SKB_DATAREF_SHIFT +/* if we do not have the infrastructure to detect if skb_header is cloned + just return false in all cases */ +#define skb_header_cloned(x) 0 +#endif + +#ifndef NETIF_F_GSO +#define gso_size tso_size +#define gso_segs tso_segs +#endif + +#ifndef NETIF_F_GRO +#define vlan_gro_receive(_napi, _vlgrp, _vlan, _skb) \ + vlan_hwaccel_receive_skb(_skb, _vlgrp, _vlan) +#define napi_gro_receive(_napi, _skb) netif_receive_skb(_skb) +#endif + +#ifndef NETIF_F_SCTP_CSUM +#define NETIF_F_SCTP_CSUM 0 +#endif + +#ifndef NETIF_F_LRO +#define NETIF_F_LRO BIT(15) +#endif + +#ifndef NETIF_F_NTUPLE +#define NETIF_F_NTUPLE BIT(27) +#endif + +#ifndef NETIF_F_ALL_FCOE +#define NETIF_F_ALL_FCOE (NETIF_F_FCOE_CRC | NETIF_F_FCOE_MTU | \ + NETIF_F_FSO) +#endif + +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif + +#ifndef IPPROTO_UDPLITE +#define IPPROTO_UDPLITE 136 +#endif + +#ifndef CHECKSUM_PARTIAL +#define CHECKSUM_PARTIAL CHECKSUM_HW +#define CHECKSUM_COMPLETE CHECKSUM_HW +#endif + +#ifndef __read_mostly +#define __read_mostly +#endif + +#ifndef MII_RESV1 +#define MII_RESV1 0x17 /* Reserved... */ +#endif + +#ifndef unlikely +#define unlikely(_x) _x +#define likely(_x) _x +#endif + +#ifndef WARN_ON +#define WARN_ON(x) +#endif + +#ifndef PCI_DEVICE +#define PCI_DEVICE(vend,dev) \ + .vendor = (vend), .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID +#endif + +#ifndef node_online +#define node_online(node) ((node) == 0) +#endif + +#ifndef cpu_online +#define cpu_online(cpuid) test_bit((cpuid), &cpu_online_map) +#endif + +#ifndef _LINUX_RANDOM_H +#include +#endif + +#ifndef BITS_PER_TYPE +#define BITS_PER_TYPE(type) (sizeof(type) * BITS_PER_BYTE) +#endif + +#ifndef BITS_TO_LONGS +#define BITS_TO_LONGS(bits) (((bits)+BITS_PER_LONG-1)/BITS_PER_LONG) +#endif + +#ifndef DECLARE_BITMAP +#define DECLARE_BITMAP(name,bits) long name[BITS_TO_LONGS(bits)] +#endif + +#ifndef VLAN_HLEN +#define VLAN_HLEN 4 +#endif + +#ifndef VLAN_ETH_HLEN +#define VLAN_ETH_HLEN 18 +#endif + +#ifndef VLAN_ETH_FRAME_LEN +#define VLAN_ETH_FRAME_LEN 1518 +#endif + +#ifndef DCA_GET_TAG_TWO_ARGS +#define dca3_get_tag(a,b) dca_get_tag(b) +#endif + +#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +#if defined(__i386__) || defined(__x86_64__) +#define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +#endif +#endif + +/* taken from 2.6.24 definition in linux/kernel.h */ +#ifndef IS_ALIGNED +#define IS_ALIGNED(x,a) (((x) % ((typeof(x))(a))) == 0) +#endif + +#ifdef IS_ENABLED +#undef IS_ENABLED +#undef __ARG_PLACEHOLDER_1 +#undef config_enabled +#undef _config_enabled +#undef __config_enabled +#undef ___config_enabled +#endif + +#define __ARG_PLACEHOLDER_1 0, +#define config_enabled(cfg) _config_enabled(cfg) +#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value) +#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0) +#define ___config_enabled(__ignored, val, ...) val + +#define IS_ENABLED(option) \ + (config_enabled(option) || config_enabled(option##_MODULE)) + +#if !defined(NETIF_F_HW_VLAN_TX) && !defined(NETIF_F_HW_VLAN_CTAG_TX) +struct _kc_vlan_ethhdr { + unsigned char h_dest[ETH_ALEN]; + unsigned char h_source[ETH_ALEN]; + __be16 h_vlan_proto; + __be16 h_vlan_TCI; + __be16 h_vlan_encapsulated_proto; +}; +#define vlan_ethhdr _kc_vlan_ethhdr +struct _kc_vlan_hdr { + __be16 h_vlan_TCI; + __be16 h_vlan_encapsulated_proto; +}; +#define vlan_hdr _kc_vlan_hdr +#define vlan_tx_tag_present(_skb) 0 +#define vlan_tx_tag_get(_skb) 0 +#endif /* NETIF_F_HW_VLAN_TX && NETIF_F_HW_VLAN_CTAG_TX */ + +#ifndef VLAN_PRIO_SHIFT +#define VLAN_PRIO_SHIFT 13 +#endif + +#ifndef PCI_EXP_LNKSTA_CLS_2_5GB +#define PCI_EXP_LNKSTA_CLS_2_5GB 0x0001 +#endif + +#ifndef PCI_EXP_LNKSTA_CLS_5_0GB +#define PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 +#endif + +#ifndef PCI_EXP_LNKSTA_CLS_8_0GB +#define PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 +#endif + +#ifndef PCI_EXP_LNKSTA_NLW_X1 +#define PCI_EXP_LNKSTA_NLW_X1 0x0010 +#endif + +#ifndef PCI_EXP_LNKSTA_NLW_X2 +#define PCI_EXP_LNKSTA_NLW_X2 0x0020 +#endif + +#ifndef PCI_EXP_LNKSTA_NLW_X4 +#define PCI_EXP_LNKSTA_NLW_X4 0x0040 +#endif + +#ifndef PCI_EXP_LNKSTA_NLW_X8 +#define PCI_EXP_LNKSTA_NLW_X8 0x0080 +#endif + +#ifndef __GFP_COLD +#define __GFP_COLD 0 +#endif + +#ifndef __GFP_COMP +#define __GFP_COMP 0 +#endif + +#ifndef IP_OFFSET +#define IP_OFFSET 0x1FFF /* "Fragment Offset" part */ +#endif + +/*****************************************************************************/ +/* Installations with ethtool version without eeprom, adapter id, or statistics + * support */ + +#ifndef ETH_GSTRING_LEN +#define ETH_GSTRING_LEN 32 +#endif + +#ifndef ETHTOOL_GSTATS +#define ETHTOOL_GSTATS 0x1d +#undef ethtool_drvinfo +#define ethtool_drvinfo k_ethtool_drvinfo +struct k_ethtool_drvinfo { + u32 cmd; + char driver[32]; + char version[32]; + char fw_version[32]; + char bus_info[32]; + char reserved1[32]; + char reserved2[16]; + u32 n_stats; + u32 testinfo_len; + u32 eedump_len; + u32 regdump_len; +}; + +struct ethtool_stats { + u32 cmd; + u32 n_stats; + u64 data[0]; +}; +#endif /* ETHTOOL_GSTATS */ + +#ifndef ETHTOOL_PHYS_ID +#define ETHTOOL_PHYS_ID 0x1c +#endif /* ETHTOOL_PHYS_ID */ + +#ifndef ETHTOOL_GSTRINGS +#define ETHTOOL_GSTRINGS 0x1b +enum ethtool_stringset { + ETH_SS_TEST = 0, + ETH_SS_STATS, +}; +struct ethtool_gstrings { + u32 cmd; /* ETHTOOL_GSTRINGS */ + u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/ + u32 len; /* number of strings in the string set */ + u8 data[0]; +}; +#endif /* ETHTOOL_GSTRINGS */ + +#ifndef ETHTOOL_TEST +#define ETHTOOL_TEST 0x1a +enum ethtool_test_flags { + ETH_TEST_FL_OFFLINE = BIT(0), + ETH_TEST_FL_FAILED = BIT(1), +}; +struct ethtool_test { + u32 cmd; + u32 flags; + u32 reserved; + u32 len; + u64 data[0]; +}; +#endif /* ETHTOOL_TEST */ + +#ifndef ETHTOOL_GEEPROM +#define ETHTOOL_GEEPROM 0xb +#undef ETHTOOL_GREGS +struct ethtool_eeprom { + u32 cmd; + u32 magic; + u32 offset; + u32 len; + u8 data[0]; +}; + +struct ethtool_value { + u32 cmd; + u32 data; +}; +#endif /* ETHTOOL_GEEPROM */ + +#ifndef ETHTOOL_GLINK +#define ETHTOOL_GLINK 0xa +#endif /* ETHTOOL_GLINK */ + +#ifndef ETHTOOL_GWOL +#define ETHTOOL_GWOL 0x5 +#define ETHTOOL_SWOL 0x6 +#define SOPASS_MAX 6 +struct ethtool_wolinfo { + u32 cmd; + u32 supported; + u32 wolopts; + u8 sopass[SOPASS_MAX]; /* SecureOn(tm) password */ +}; +#endif /* ETHTOOL_GWOL */ + +#ifndef ETHTOOL_GREGS +#define ETHTOOL_GREGS 0x00000004 /* Get NIC registers */ +#define ethtool_regs _kc_ethtool_regs +/* for passing big chunks of data */ +struct _kc_ethtool_regs { + u32 cmd; + u32 version; /* driver-specific, indicates different chips/revs */ + u32 len; /* bytes */ + u8 data[0]; +}; +#endif /* ETHTOOL_GREGS */ + +#ifndef ETHTOOL_GMSGLVL +#define ETHTOOL_GMSGLVL 0x00000007 /* Get driver message level */ +#endif +#ifndef ETHTOOL_SMSGLVL +#define ETHTOOL_SMSGLVL 0x00000008 /* Set driver msg level, priv. */ +#endif +#ifndef ETHTOOL_NWAY_RST +#define ETHTOOL_NWAY_RST 0x00000009 /* Restart autonegotiation, priv */ +#endif +#ifndef ETHTOOL_GLINK +#define ETHTOOL_GLINK 0x0000000a /* Get link status */ +#endif +#ifndef ETHTOOL_GEEPROM +#define ETHTOOL_GEEPROM 0x0000000b /* Get EEPROM data */ +#endif +#ifndef ETHTOOL_SEEPROM +#define ETHTOOL_SEEPROM 0x0000000c /* Set EEPROM data */ +#endif +#ifndef ETHTOOL_GCOALESCE +#define ETHTOOL_GCOALESCE 0x0000000e /* Get coalesce config */ +/* for configuring coalescing parameters of chip */ +#define ethtool_coalesce _kc_ethtool_coalesce +struct _kc_ethtool_coalesce { + u32 cmd; /* ETHTOOL_{G,S}COALESCE */ + + /* How many usecs to delay an RX interrupt after + * a packet arrives. If 0, only rx_max_coalesced_frames + * is used. + */ + u32 rx_coalesce_usecs; + + /* How many packets to delay an RX interrupt after + * a packet arrives. If 0, only rx_coalesce_usecs is + * used. It is illegal to set both usecs and max frames + * to zero as this would cause RX interrupts to never be + * generated. + */ + u32 rx_max_coalesced_frames; + + /* Same as above two parameters, except that these values + * apply while an IRQ is being serviced by the host. Not + * all cards support this feature and the values are ignored + * in that case. + */ + u32 rx_coalesce_usecs_irq; + u32 rx_max_coalesced_frames_irq; + + /* How many usecs to delay a TX interrupt after + * a packet is sent. If 0, only tx_max_coalesced_frames + * is used. + */ + u32 tx_coalesce_usecs; + + /* How many packets to delay a TX interrupt after + * a packet is sent. If 0, only tx_coalesce_usecs is + * used. It is illegal to set both usecs and max frames + * to zero as this would cause TX interrupts to never be + * generated. + */ + u32 tx_max_coalesced_frames; + + /* Same as above two parameters, except that these values + * apply while an IRQ is being serviced by the host. Not + * all cards support this feature and the values are ignored + * in that case. + */ + u32 tx_coalesce_usecs_irq; + u32 tx_max_coalesced_frames_irq; + + /* How many usecs to delay in-memory statistics + * block updates. Some drivers do not have an in-memory + * statistic block, and in such cases this value is ignored. + * This value must not be zero. + */ + u32 stats_block_coalesce_usecs; + + /* Adaptive RX/TX coalescing is an algorithm implemented by + * some drivers to improve latency under low packet rates and + * improve throughput under high packet rates. Some drivers + * only implement one of RX or TX adaptive coalescing. Anything + * not implemented by the driver causes these values to be + * silently ignored. + */ + u32 use_adaptive_rx_coalesce; + u32 use_adaptive_tx_coalesce; + + /* When the packet rate (measured in packets per second) + * is below pkt_rate_low, the {rx,tx}_*_low parameters are + * used. + */ + u32 pkt_rate_low; + u32 rx_coalesce_usecs_low; + u32 rx_max_coalesced_frames_low; + u32 tx_coalesce_usecs_low; + u32 tx_max_coalesced_frames_low; + + /* When the packet rate is below pkt_rate_high but above + * pkt_rate_low (both measured in packets per second) the + * normal {rx,tx}_* coalescing parameters are used. + */ + + /* When the packet rate is (measured in packets per second) + * is above pkt_rate_high, the {rx,tx}_*_high parameters are + * used. + */ + u32 pkt_rate_high; + u32 rx_coalesce_usecs_high; + u32 rx_max_coalesced_frames_high; + u32 tx_coalesce_usecs_high; + u32 tx_max_coalesced_frames_high; + + /* How often to do adaptive coalescing packet rate sampling, + * measured in seconds. Must not be zero. + */ + u32 rate_sample_interval; +}; +#endif /* ETHTOOL_GCOALESCE */ + +#ifndef ETHTOOL_SCOALESCE +#define ETHTOOL_SCOALESCE 0x0000000f /* Set coalesce config. */ +#endif +#ifndef ETHTOOL_GRINGPARAM +#define ETHTOOL_GRINGPARAM 0x00000010 /* Get ring parameters */ +/* for configuring RX/TX ring parameters */ +#define ethtool_ringparam _kc_ethtool_ringparam +struct _kc_ethtool_ringparam { + u32 cmd; /* ETHTOOL_{G,S}RINGPARAM */ + + /* Read only attributes. These indicate the maximum number + * of pending RX/TX ring entries the driver will allow the + * user to set. + */ + u32 rx_max_pending; + u32 rx_mini_max_pending; + u32 rx_jumbo_max_pending; + u32 tx_max_pending; + + /* Values changeable by the user. The valid values are + * in the range 1 to the "*_max_pending" counterpart above. + */ + u32 rx_pending; + u32 rx_mini_pending; + u32 rx_jumbo_pending; + u32 tx_pending; +}; +#endif /* ETHTOOL_GRINGPARAM */ + +#ifndef ETHTOOL_SRINGPARAM +#define ETHTOOL_SRINGPARAM 0x00000011 /* Set ring parameters, priv. */ +#endif +#ifndef ETHTOOL_GPAUSEPARAM +#define ETHTOOL_GPAUSEPARAM 0x00000012 /* Get pause parameters */ +/* for configuring link flow control parameters */ +#define ethtool_pauseparam _kc_ethtool_pauseparam +struct _kc_ethtool_pauseparam { + u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */ + + /* If the link is being auto-negotiated (via ethtool_cmd.autoneg + * being true) the user may set 'autoneg' here non-zero to have the + * pause parameters be auto-negotiated too. In such a case, the + * {rx,tx}_pause values below determine what capabilities are + * advertised. + * + * If 'autoneg' is zero or the link is not being auto-negotiated, + * then {rx,tx}_pause force the driver to use/not-use pause + * flow control. + */ + u32 autoneg; + u32 rx_pause; + u32 tx_pause; +}; +#endif /* ETHTOOL_GPAUSEPARAM */ + +#ifndef ETHTOOL_SPAUSEPARAM +#define ETHTOOL_SPAUSEPARAM 0x00000013 /* Set pause parameters. */ +#endif +#ifndef ETHTOOL_GRXCSUM +#define ETHTOOL_GRXCSUM 0x00000014 /* Get RX hw csum enable (ethtool_value) */ +#endif +#ifndef ETHTOOL_SRXCSUM +#define ETHTOOL_SRXCSUM 0x00000015 /* Set RX hw csum enable (ethtool_value) */ +#endif +#ifndef ETHTOOL_GTXCSUM +#define ETHTOOL_GTXCSUM 0x00000016 /* Get TX hw csum enable (ethtool_value) */ +#endif +#ifndef ETHTOOL_STXCSUM +#define ETHTOOL_STXCSUM 0x00000017 /* Set TX hw csum enable (ethtool_value) */ +#endif +#ifndef ETHTOOL_GSG +#define ETHTOOL_GSG 0x00000018 /* Get scatter-gather enable + * (ethtool_value) */ +#endif +#ifndef ETHTOOL_SSG +#define ETHTOOL_SSG 0x00000019 /* Set scatter-gather enable + * (ethtool_value). */ +#endif +#ifndef ETHTOOL_TEST +#define ETHTOOL_TEST 0x0000001a /* execute NIC self-test, priv. */ +#endif +#ifndef ETHTOOL_GSTRINGS +#define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ +#endif +#ifndef ETHTOOL_PHYS_ID +#define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ +#endif +#ifndef ETHTOOL_GSTATS +#define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */ +#endif +#ifndef ETHTOOL_GTSO +#define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */ +#endif +#ifndef ETHTOOL_STSO +#define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */ +#endif + +#ifndef ETHTOOL_BUSINFO_LEN +#define ETHTOOL_BUSINFO_LEN 32 +#endif + +#ifndef WAKE_FILTER +#define WAKE_FILTER BIT(7) +#endif + +#ifndef SPEED_2500 +#define SPEED_2500 2500 +#endif +#ifndef SPEED_5000 +#define SPEED_5000 5000 +#endif +#ifndef SPEED_14000 +#define SPEED_14000 14000 +#endif +#ifndef SPEED_25000 +#define SPEED_25000 25000 +#endif +#ifndef SPEED_50000 +#define SPEED_50000 50000 +#endif +#ifndef SPEED_56000 +#define SPEED_56000 56000 +#endif +#ifndef SPEED_100000 +#define SPEED_100000 100000 +#endif + +#ifndef RHEL_RELEASE_VERSION +#define RHEL_RELEASE_VERSION(a,b) (((a) << 8) + (b)) +#endif +#ifndef AX_RELEASE_VERSION +#define AX_RELEASE_VERSION(a,b) (((a) << 8) + (b)) +#endif + +/* #ifndef AX_RELEASE_CODE */ +#define AX_RELEASE_CODE 0 +/* #endif */ + +#if (AX_RELEASE_CODE && AX_RELEASE_CODE == AX_RELEASE_VERSION(3,0)) +#define RHEL_RELEASE_CODE RHEL_RELEASE_VERSION(5,0) +#elif (AX_RELEASE_CODE && AX_RELEASE_CODE == AX_RELEASE_VERSION(3,1)) +#define RHEL_RELEASE_CODE RHEL_RELEASE_VERSION(5,1) +#elif (AX_RELEASE_CODE && AX_RELEASE_CODE == AX_RELEASE_VERSION(3,2)) +#define RHEL_RELEASE_CODE RHEL_RELEASE_VERSION(5,3) +#endif + +/* #ifndef RHEL_RELEASE_CODE */ +/* NOTE: RHEL_RELEASE_* introduced in RHEL4.5 */ +#define RHEL_RELEASE_CODE 0 +/* #endif */ + +/* RHEL 7 didn't backport the parameter change in + * create_singlethread_workqueue. + * If/when RH corrects this we will want to tighten up the version check. + */ +#if (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,0)) +#undef create_singlethread_workqueue +#define create_singlethread_workqueue(name) \ + alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, name) +#endif + +/* Ubuntu Release ABI is the 4th digit of their kernel version. You can find + * it in /usr/src/linux/$(uname -r)/include/generated/utsrelease.h for new + * enough versions of Ubuntu. Otherwise you can simply see it in the output of + * uname as the 4th digit of the kernel. The UTS_UBUNTU_RELEASE_ABI is not in + * the linux-source package, but in the linux-headers package. It begins to + * appear in later releases of 14.04 and 14.10. + * + * Ex: + * + * $uname -r + * 3.13.0-45-generic + * ABI is 45 + * + * + * $uname -r + * 3.16.0-23-generic + * ABI is 23 + */ +#ifndef UTS_UBUNTU_RELEASE_ABI +#define UTS_UBUNTU_RELEASE_ABI 0 +#define UBUNTU_VERSION_CODE 0 +#else +/* Ubuntu does not provide actual release version macro, so we use the kernel + * version plus the ABI to generate a unique version code specific to Ubuntu. + * In addition, we mask the lower 8 bits of LINUX_VERSION_CODE in order to + * ignore differences in sublevel which are not important since we have the + * ABI value. Otherwise, it becomes impossible to correlate ABI to version for + * ordering checks. + */ +#define UBUNTU_VERSION_CODE (((~0xFF & LINUX_VERSION_CODE) << 8) + \ + UTS_UBUNTU_RELEASE_ABI) + +#if UTS_UBUNTU_RELEASE_ABI > 255 +#error UTS_UBUNTU_RELEASE_ABI is too large... +#endif /* UTS_UBUNTU_RELEASE_ABI > 255 */ + +#if ( LINUX_VERSION_CODE <= KERNEL_VERSION(3,0,0) ) +/* Our version code scheme does not make sense for non 3.x or newer kernels, + * and we have no support in kcompat for this scenario. Thus, treat this as a + * non-Ubuntu kernel. Possibly might be better to error here. + */ +#define UTS_UBUNTU_RELEASE_ABI 0 +#define UBUNTU_VERSION_CODE 0 +#endif + +#endif + +/* Note that the 3rd digit is always zero, and will be ignored. This is + * because Ubuntu kernels are based on x.y.0-ABI values, and while their linux + * version codes are 3 digit, this 3rd digit is superseded by the ABI value. + */ +#define UBUNTU_VERSION(a,b,c,d) ((KERNEL_VERSION(a,b,0) << 8) + (d)) + +/* SuSE version macros are the same as Linux kernel version macro */ +#ifndef SLE_VERSION +#define SLE_VERSION(a,b,c) KERNEL_VERSION(a,b,c) +#endif +#define SLE_LOCALVERSION(a,b,c) KERNEL_VERSION(a,b,c) +#ifdef CONFIG_SUSE_KERNEL +#if ( LINUX_VERSION_CODE == KERNEL_VERSION(2,6,27) ) +/* SLES11 GA is 2.6.27 based */ +#define SLE_VERSION_CODE SLE_VERSION(11,0,0) +#elif ( LINUX_VERSION_CODE == KERNEL_VERSION(2,6,32) ) +/* SLES11 SP1 is 2.6.32 based */ +#define SLE_VERSION_CODE SLE_VERSION(11,1,0) +#elif ( LINUX_VERSION_CODE == KERNEL_VERSION(3,0,13) ) +/* SLES11 SP2 GA is 3.0.13-0.27 */ +#define SLE_VERSION_CODE SLE_VERSION(11,2,0) +#elif ((LINUX_VERSION_CODE == KERNEL_VERSION(3,0,76))) +/* SLES11 SP3 GA is 3.0.76-0.11 */ +#define SLE_VERSION_CODE SLE_VERSION(11,3,0) +#elif (LINUX_VERSION_CODE == KERNEL_VERSION(3,0,101)) + #if (SLE_LOCALVERSION_CODE < SLE_LOCALVERSION(0,8,0)) + /* some SLES11sp2 update kernels up to 3.0.101-0.7.x */ + #define SLE_VERSION_CODE SLE_VERSION(11,2,0) + #elif (SLE_LOCALVERSION_CODE < SLE_LOCALVERSION(63,0,0)) + /* most SLES11sp3 update kernels */ + #define SLE_VERSION_CODE SLE_VERSION(11,3,0) + #else + /* SLES11 SP4 GA (3.0.101-63) and update kernels 3.0.101-63+ */ + #define SLE_VERSION_CODE SLE_VERSION(11,4,0) + #endif +#elif (LINUX_VERSION_CODE == KERNEL_VERSION(3,12,28)) +/* SLES12 GA is 3.12.28-4 + * kernel updates 3.12.xx-<33 through 52>[.yy] */ +#define SLE_VERSION_CODE SLE_VERSION(12,0,0) +#elif (LINUX_VERSION_CODE == KERNEL_VERSION(3,12,49)) +/* SLES12 SP1 GA is 3.12.49-11 + * updates 3.12.xx-60.yy where xx={51..} */ +#define SLE_VERSION_CODE SLE_VERSION(12,1,0) +#elif ((LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,21) && \ + (LINUX_VERSION_CODE <= KERNEL_VERSION(4,4,59))) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,74) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0) && \ + SLE_LOCALVERSION_CODE >= KERNEL_VERSION(92,0,0) && \ + SLE_LOCALVERSION_CODE < KERNEL_VERSION(93,0,0))) +/* SLES12 SP2 GA is 4.4.21-69. + * SLES12 SP2 updates before SLES12 SP3 are: 4.4.{21,38,49,59} + * SLES12 SP2 updates after SLES12 SP3 are: 4.4.{74,90,103,114,120} + * but they all use a SLE_LOCALVERSION_CODE matching 92.nn.y */ +#define SLE_VERSION_CODE SLE_VERSION(12,2,0) +#elif ((LINUX_VERSION_CODE == KERNEL_VERSION(4,4,73) || \ + LINUX_VERSION_CODE == KERNEL_VERSION(4,4,82) || \ + LINUX_VERSION_CODE == KERNEL_VERSION(4,4,92)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(4,4,103) && \ + (SLE_LOCALVERSION_CODE == KERNEL_VERSION(6,33,0) || \ + SLE_LOCALVERSION_CODE == KERNEL_VERSION(6,38,0))) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,114) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0) && \ + SLE_LOCALVERSION_CODE >= KERNEL_VERSION(94,0,0) && \ + SLE_LOCALVERSION_CODE < KERNEL_VERSION(95,0,0)) ) +/* SLES12 SP3 GM is 4.4.73-5 and update kernels are 4.4.82-6.3. + * SLES12 SP3 updates not conflicting with SP2 are: 4.4.{82,92} + * SLES12 SP3 updates conflicting with SP2 are: + * - 4.4.103-6.33.1, 4.4.103-6.38.1 + * - 4.4.{114,120}-94.nn.y */ +#define SLE_VERSION_CODE SLE_VERSION(12,3,0) +#elif (LINUX_VERSION_CODE == KERNEL_VERSION(4,12,14) && \ + (SLE_LOCALVERSION_CODE == KERNEL_VERSION(94,41,0) || \ + (SLE_LOCALVERSION_CODE >= KERNEL_VERSION(95,0,0) && \ + SLE_LOCALVERSION_CODE < KERNEL_VERSION(96,0,0)))) +/* SLES12 SP4 GM is 4.12.14-94.41 and update kernel is 4.12.14-95.x. */ +#define SLE_VERSION_CODE SLE_VERSION(12,4,0) +#elif (LINUX_VERSION_CODE == KERNEL_VERSION(4,12,14) && \ + (SLE_LOCALVERSION_CODE == KERNEL_VERSION(23,0,0) || \ + SLE_LOCALVERSION_CODE == KERNEL_VERSION(2,0,0) || \ + SLE_LOCALVERSION_CODE == KERNEL_VERSION(136,0,0) || \ + (SLE_LOCALVERSION_CODE >= KERNEL_VERSION(25,0,0) && \ + SLE_LOCALVERSION_CODE < KERNEL_VERSION(26,0,0)) || \ + (SLE_LOCALVERSION_CODE >= KERNEL_VERSION(150,0,0) && \ + SLE_LOCALVERSION_CODE < KERNEL_VERSION(151,0,0)))) +/* SLES15 Beta1 is 4.12.14-2 + * SLES15 GM is 4.12.14-23 and update kernel is 4.12.14-{25,136}, + * and 4.12.14-150.14. + */ +#define SLE_VERSION_CODE SLE_VERSION(15,0,0) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4,12,14) && \ + SLE_LOCALVERSION_CODE >= KERNEL_VERSION(25,23,0)) +/* SLES15 SP1 Beta1 is 4.12.14-25.23 */ +#define SLE_VERSION_CODE SLE_VERSION(15,1,0) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,13)) +/* SLES15 SP2 Beta1 is 5.3.13 */ +#define SLE_VERSION_CODE SLE_VERSION(15,2,0) + +/* new SLES kernels must be added here with >= based on kernel + * the idea is to order from newest to oldest and just catch all + * of them using the >= + */ +#endif /* LINUX_VERSION_CODE == KERNEL_VERSION(x,y,z) */ +#endif /* CONFIG_SUSE_KERNEL */ +#ifndef SLE_VERSION_CODE +#define SLE_VERSION_CODE 0 +#endif /* SLE_VERSION_CODE */ +#ifndef SLE_LOCALVERSION_CODE +#define SLE_LOCALVERSION_CODE 0 +#endif /* SLE_LOCALVERSION_CODE */ + +#ifdef __KLOCWORK__ +/* The following are not compiled into the binary driver; they are here + * only to tune Klocwork scans to workaround false-positive issues. + */ +#ifdef ARRAY_SIZE +#undef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#define memcpy(dest, src, len) memcpy_s(dest, len, src, len) +#define memset(dest, ch, len) memset_s(dest, len, ch, len) + +static inline int _kc_test_and_clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + unsigned long old; + unsigned long flags = 0; + + _atomic_spin_lock_irqsave(p, flags); + old = *p; + *p = old & ~mask; + _atomic_spin_unlock_irqrestore(p, flags); + + return (old & mask) != 0; +} +#define test_and_clear_bit(nr, addr) _kc_test_and_clear_bit(nr, addr) + +static inline int _kc_test_and_set_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + unsigned long old; + unsigned long flags = 0; + + _atomic_spin_lock_irqsave(p, flags); + old = *p; + *p = old | mask; + _atomic_spin_unlock_irqrestore(p, flags); + + return (old & mask) != 0; +} +#define test_and_set_bit(nr, addr) _kc_test_and_set_bit(nr, addr) + +#ifdef CONFIG_DYNAMIC_DEBUG +#undef dev_dbg +#define dev_dbg(dev, format, arg...) dev_printk(KERN_DEBUG, dev, format, ##arg) +#undef pr_debug +#define pr_debug(format, arg...) printk(KERN_DEBUG format, ##arg) +#endif /* CONFIG_DYNAMIC_DEBUG */ + +#undef hlist_for_each_entry_safe +#define hlist_for_each_entry_safe(pos, n, head, member) \ + for (n = NULL, pos = hlist_entry_safe((head)->first, typeof(*(pos)), \ + member); \ + pos; \ + pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) + +#ifdef uninitialized_var +#undef uninitialized_var +#define uninitialized_var(x) x = *(&(x)) +#endif +#endif /* __KLOCWORK__ */ + +#include "kcompat_vfd.h" +struct vfd_objects *create_vfd_sysfs(struct pci_dev *pdev, int num_alloc_vfs); +void destroy_vfd_sysfs(struct pci_dev *pdev, struct vfd_objects *vfd_obj); + +/* Older versions of GCC will trigger -Wformat-nonliteral warnings for const + * char * strings. Unfortunately, the implementation of do_trace_printk does + * this, in order to add a storage attribute to the memory. This was fixed in + * GCC 5.1, but we still use older distributions built with GCC 4.x. + * + * The string pointer is only passed as a const char * to the __trace_bprintk + * function. Since that function has the __printf attribute, it will trigger + * the warnings. We can't remove the attribute, so instead we'll use the + * __diag macro to disable -Wformat-nonliteral around the call to + * __trace_bprintk. + */ +#if GCC_VERSION < 50100 +#define __trace_bprintk(ip, fmt, args...) ({ \ + int err; \ + __diag_push(); \ + __diag(ignored "-Wformat-nonliteral"); \ + err = __trace_bprintk(ip, fmt, ##args); \ + __diag_pop(); \ + err; \ +}) +#endif /* GCC_VERSION < 5.1.0 */ + +/* Newer kernels removed */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ) +#define HAVE_PCI_ASPM_H +#endif + +/*****************************************************************************/ +/* 2.4.3 => 2.4.0 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,3) ) + +/**************************************/ +/* PCI DRIVER API */ + +#ifndef pci_set_dma_mask +#define pci_set_dma_mask _kc_pci_set_dma_mask +int _kc_pci_set_dma_mask(struct pci_dev *dev, dma_addr_t mask); +#endif + +#ifndef pci_request_regions +#define pci_request_regions _kc_pci_request_regions +int _kc_pci_request_regions(struct pci_dev *pdev, char *res_name); +#endif + +#ifndef pci_release_regions +#define pci_release_regions _kc_pci_release_regions +void _kc_pci_release_regions(struct pci_dev *pdev); +#endif + +/**************************************/ +/* NETWORK DRIVER API */ + +#ifndef alloc_etherdev +#define alloc_etherdev _kc_alloc_etherdev +struct net_device * _kc_alloc_etherdev(int sizeof_priv); +#endif + +#ifndef is_valid_ether_addr +#define is_valid_ether_addr _kc_is_valid_ether_addr +int _kc_is_valid_ether_addr(u8 *addr); +#endif + +/**************************************/ +/* MISCELLANEOUS */ + +#ifndef INIT_TQUEUE +#define INIT_TQUEUE(_tq, _routine, _data) \ + do { \ + INIT_LIST_HEAD(&(_tq)->list); \ + (_tq)->sync = 0; \ + (_tq)->routine = _routine; \ + (_tq)->data = _data; \ + } while (0) +#endif + +#endif /* 2.4.3 => 2.4.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5) ) +/* Generic MII registers. */ +#define MII_BMCR 0x00 /* Basic mode control register */ +#define MII_BMSR 0x01 /* Basic mode status register */ +#define MII_PHYSID1 0x02 /* PHYS ID 1 */ +#define MII_PHYSID2 0x03 /* PHYS ID 2 */ +#define MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define MII_LPA 0x05 /* Link partner ability reg */ +#define MII_EXPANSION 0x06 /* Expansion register */ +/* Basic mode control register. */ +#define BMCR_FULLDPLX 0x0100 /* Full duplex */ +#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ +/* Basic mode status register. */ +#define BMSR_ERCAP 0x0001 /* Ext-reg capability */ +#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */ +#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */ +#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */ +#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */ +#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */ +/* Advertisement control register. */ +#define ADVERTISE_CSMA 0x0001 /* Only selector supported */ +#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \ + ADVERTISE_100HALF | ADVERTISE_100FULL) +/* Expansion register for auto-negotiation. */ +#define EXPANSION_ENABLENPAGE 0x0004 /* This enables npage words */ +#endif + +/*****************************************************************************/ +/* 2.4.6 => 2.4.3 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,6) ) + +#ifndef pci_set_power_state +#define pci_set_power_state _kc_pci_set_power_state +int _kc_pci_set_power_state(struct pci_dev *dev, int state); +#endif + +#ifndef pci_enable_wake +#define pci_enable_wake _kc_pci_enable_wake +int _kc_pci_enable_wake(struct pci_dev *pdev, u32 state, int enable); +#endif + +#ifndef pci_disable_device +#define pci_disable_device _kc_pci_disable_device +void _kc_pci_disable_device(struct pci_dev *pdev); +#endif + +/* PCI PM entry point syntax changed, so don't support suspend/resume */ +#undef CONFIG_PM + +#endif /* 2.4.6 => 2.4.3 */ + +#ifndef HAVE_PCI_SET_MWI +#define pci_set_mwi(X) pci_write_config_word(X, \ + PCI_COMMAND, adapter->hw.bus.pci_cmd_word | \ + PCI_COMMAND_INVALIDATE); +#define pci_clear_mwi(X) pci_write_config_word(X, \ + PCI_COMMAND, adapter->hw.bus.pci_cmd_word & \ + ~PCI_COMMAND_INVALIDATE); +#endif + +/*****************************************************************************/ +/* 2.4.10 => 2.4.9 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) ) + +/**************************************/ +/* MODULE API */ + +#ifndef MODULE_LICENSE + #define MODULE_LICENSE(X) +#endif + +/**************************************/ +/* OTHER */ + +#undef min +#define min(x,y) ({ \ + const typeof(x) _x = (x); \ + const typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) + +#undef max +#define max(x,y) ({ \ + const typeof(x) _x = (x); \ + const typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; }) + +#define min_t(type,x,y) ({ \ + type _x = (x); \ + type _y = (y); \ + _x < _y ? _x : _y; }) + +#define max_t(type,x,y) ({ \ + type _x = (x); \ + type _y = (y); \ + _x > _y ? _x : _y; }) + +#ifndef list_for_each_safe +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) +#endif + +#ifndef ____cacheline_aligned_in_smp +#ifdef CONFIG_SMP +#define ____cacheline_aligned_in_smp ____cacheline_aligned +#else +#define ____cacheline_aligned_in_smp +#endif /* CONFIG_SMP */ +#endif + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,8) ) +int _kc_snprintf(char * buf, size_t size, const char *fmt, ...); +#define snprintf(buf, size, fmt, args...) _kc_snprintf(buf, size, fmt, ##args) +int _kc_vsnprintf(char *buf, size_t size, const char *fmt, va_list args); +#define vsnprintf(buf, size, fmt, args) _kc_vsnprintf(buf, size, fmt, args) +#else /* 2.4.8 => 2.4.9 */ +int snprintf(char * buf, size_t size, const char *fmt, ...); +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args); +#endif +#endif /* 2.4.10 -> 2.4.6 */ + + +/*****************************************************************************/ +/* 2.4.12 => 2.4.10 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,12) ) +#ifndef HAVE_NETIF_MSG +#define HAVE_NETIF_MSG 1 +enum { + NETIF_MSG_DRV = 0x0001, + NETIF_MSG_PROBE = 0x0002, + NETIF_MSG_LINK = 0x0004, + NETIF_MSG_TIMER = 0x0008, + NETIF_MSG_IFDOWN = 0x0010, + NETIF_MSG_IFUP = 0x0020, + NETIF_MSG_RX_ERR = 0x0040, + NETIF_MSG_TX_ERR = 0x0080, + NETIF_MSG_TX_QUEUED = 0x0100, + NETIF_MSG_INTR = 0x0200, + NETIF_MSG_TX_DONE = 0x0400, + NETIF_MSG_RX_STATUS = 0x0800, + NETIF_MSG_PKTDATA = 0x1000, + NETIF_MSG_HW = 0x2000, + NETIF_MSG_WOL = 0x4000, +}; + +#define netif_msg_drv(p) ((p)->msg_enable & NETIF_MSG_DRV) +#define netif_msg_probe(p) ((p)->msg_enable & NETIF_MSG_PROBE) +#define netif_msg_link(p) ((p)->msg_enable & NETIF_MSG_LINK) +#define netif_msg_timer(p) ((p)->msg_enable & NETIF_MSG_TIMER) +#define netif_msg_ifdown(p) ((p)->msg_enable & NETIF_MSG_IFDOWN) +#define netif_msg_ifup(p) ((p)->msg_enable & NETIF_MSG_IFUP) +#define netif_msg_rx_err(p) ((p)->msg_enable & NETIF_MSG_RX_ERR) +#define netif_msg_tx_err(p) ((p)->msg_enable & NETIF_MSG_TX_ERR) +#define netif_msg_tx_queued(p) ((p)->msg_enable & NETIF_MSG_TX_QUEUED) +#define netif_msg_intr(p) ((p)->msg_enable & NETIF_MSG_INTR) +#define netif_msg_tx_done(p) ((p)->msg_enable & NETIF_MSG_TX_DONE) +#define netif_msg_rx_status(p) ((p)->msg_enable & NETIF_MSG_RX_STATUS) +#define netif_msg_pktdata(p) ((p)->msg_enable & NETIF_MSG_PKTDATA) +#endif /* !HAVE_NETIF_MSG */ +#endif /* 2.4.12 => 2.4.10 */ + +/*****************************************************************************/ +/* 2.4.13 => 2.4.12 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,13) ) + +/**************************************/ +/* PCI DMA MAPPING */ + +#ifndef virt_to_page + #define virt_to_page(v) (mem_map + (virt_to_phys(v) >> PAGE_SHIFT)) +#endif + +#ifndef pci_map_page +#define pci_map_page _kc_pci_map_page +u64 _kc_pci_map_page(struct pci_dev *dev, struct page *page, unsigned long offset, size_t size, int direction); +#endif + +#ifndef pci_unmap_page +#define pci_unmap_page _kc_pci_unmap_page +void _kc_pci_unmap_page(struct pci_dev *dev, u64 dma_addr, size_t size, int direction); +#endif + +/* pci_set_dma_mask takes dma_addr_t, which is only 32-bits prior to 2.4.13 */ + +#undef DMA_32BIT_MASK +#define DMA_32BIT_MASK 0xffffffff +#undef DMA_64BIT_MASK +#define DMA_64BIT_MASK 0xffffffff + +/**************************************/ +/* OTHER */ + +#ifndef cpu_relax +#define cpu_relax() rep_nop() +#endif + +struct vlan_ethhdr { + unsigned char h_dest[ETH_ALEN]; + unsigned char h_source[ETH_ALEN]; + unsigned short h_vlan_proto; + unsigned short h_vlan_TCI; + unsigned short h_vlan_encapsulated_proto; +}; +#endif /* 2.4.13 => 2.4.12 */ + +/*****************************************************************************/ +/* 2.4.17 => 2.4.12 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,17) ) + +#ifndef __devexit_p + #define __devexit_p(x) &(x) +#endif + +#endif /* 2.4.17 => 2.4.13 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18) ) +#define NETIF_MSG_HW 0x2000 +#define NETIF_MSG_WOL 0x4000 + +#ifndef netif_msg_hw +#define netif_msg_hw(p) ((p)->msg_enable & NETIF_MSG_HW) +#endif +#ifndef netif_msg_wol +#define netif_msg_wol(p) ((p)->msg_enable & NETIF_MSG_WOL) +#endif +#endif /* 2.4.18 */ + +/*****************************************************************************/ + +/*****************************************************************************/ +/* 2.4.20 => 2.4.19 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20) ) + +/* we won't support NAPI on less than 2.4.20 */ +#ifdef NAPI +#undef NAPI +#endif + +#endif /* 2.4.20 => 2.4.19 */ + +/*****************************************************************************/ +/* 2.4.22 => 2.4.17 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,22) ) +#define pci_name(x) ((x)->slot_name) + +#ifndef SUPPORTED_10000baseT_Full +#define SUPPORTED_10000baseT_Full BIT(12) +#endif +#ifndef ADVERTISED_10000baseT_Full +#define ADVERTISED_10000baseT_Full BIT(12) +#endif +#endif + +/*****************************************************************************/ +/*****************************************************************************/ +/* 2.4.23 => 2.4.22 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23) ) +/*****************************************************************************/ +#ifdef NAPI +#ifndef netif_poll_disable +#define netif_poll_disable(x) _kc_netif_poll_disable(x) +static inline void _kc_netif_poll_disable(struct net_device *netdev) +{ + while (test_and_set_bit(__LINK_STATE_RX_SCHED, &netdev->state)) { + /* No hurry */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } +} +#endif +#ifndef netif_poll_enable +#define netif_poll_enable(x) _kc_netif_poll_enable(x) +static inline void _kc_netif_poll_enable(struct net_device *netdev) +{ + clear_bit(__LINK_STATE_RX_SCHED, &netdev->state); +} +#endif +#endif /* NAPI */ +#ifndef netif_tx_disable +#define netif_tx_disable(x) _kc_netif_tx_disable(x) +static inline void _kc_netif_tx_disable(struct net_device *dev) +{ + spin_lock_bh(&dev->xmit_lock); + netif_stop_queue(dev); + spin_unlock_bh(&dev->xmit_lock); +} +#endif +#else /* 2.4.23 => 2.4.22 */ +#define HAVE_SCTP +#endif /* 2.4.23 => 2.4.22 */ + +/*****************************************************************************/ +/* 2.6.4 => 2.6.0 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,25) || \ + ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4) ) ) +#define ETHTOOL_OPS_COMPAT +#endif /* 2.6.4 => 2.6.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27) ) +#define __user +#endif /* < 2.4.27 */ + +/*****************************************************************************/ +/* 2.5.71 => 2.4.x */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,5,71) ) +#define sk_protocol protocol +#define pci_get_device pci_find_device +#endif /* 2.5.70 => 2.4.x */ + +/*****************************************************************************/ +/* < 2.4.27 or 2.6.0 <= 2.6.5 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27) || \ + ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5) ) ) + +#ifndef netif_msg_init +#define netif_msg_init _kc_netif_msg_init +static inline u32 _kc_netif_msg_init(int debug_value, int default_msg_enable_bits) +{ + /* use default */ + if (debug_value < 0 || debug_value >= (sizeof(u32) * 8)) + return default_msg_enable_bits; + if (debug_value == 0) /* no output */ + return 0; + /* set low N bits */ + return (1 << debug_value) -1; +} +#endif + +#endif /* < 2.4.27 or 2.6.0 <= 2.6.5 */ +/*****************************************************************************/ +#if (( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27) ) || \ + (( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) && \ + ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,3) ))) +#define netdev_priv(x) x->priv +#endif + +/*****************************************************************************/ +/* <= 2.5.0 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) ) +#include +#undef pci_register_driver +#define pci_register_driver pci_module_init + +/* + * Most of the dma compat code is copied/modifed from the 2.4.37 + * /include/linux/libata-compat.h header file + */ +/* These definitions mirror those in pci.h, so they can be used + * interchangeably with their PCI_ counterparts */ +enum dma_data_direction { + DMA_BIDIRECTIONAL = 0, + DMA_TO_DEVICE = 1, + DMA_FROM_DEVICE = 2, + DMA_NONE = 3, +}; + +struct device { + struct pci_dev pdev; +}; + +static inline struct pci_dev *to_pci_dev (struct device *dev) +{ + return (struct pci_dev *) dev; +} +static inline struct device *pci_dev_to_dev(struct pci_dev *pdev) +{ + return (struct device *) pdev; +} +#define pdev_printk(lvl, pdev, fmt, args...) \ + printk("%s %s: " fmt, lvl, pci_name(pdev), ## args) +#define dev_err(dev, fmt, args...) \ + pdev_printk(KERN_ERR, to_pci_dev(dev), fmt, ## args) +#define dev_info(dev, fmt, args...) \ + pdev_printk(KERN_INFO, to_pci_dev(dev), fmt, ## args) +#define dev_warn(dev, fmt, args...) \ + pdev_printk(KERN_WARNING, to_pci_dev(dev), fmt, ## args) +#define dev_notice(dev, fmt, args...) \ + pdev_printk(KERN_NOTICE, to_pci_dev(dev), fmt, ## args) +#define dev_dbg(dev, fmt, args...) \ + pdev_printk(KERN_DEBUG, to_pci_dev(dev), fmt, ## args) + +/* NOTE: dangerous! we ignore the 'gfp' argument */ +#define dma_alloc_coherent(dev,sz,dma,gfp) \ + pci_alloc_consistent(to_pci_dev(dev),(sz),(dma)) +#define dma_free_coherent(dev,sz,addr,dma_addr) \ + pci_free_consistent(to_pci_dev(dev),(sz),(addr),(dma_addr)) + +#define dma_map_page(dev,a,b,c,d) \ + pci_map_page(to_pci_dev(dev),(a),(b),(c),(d)) +#define dma_unmap_page(dev,a,b,c) \ + pci_unmap_page(to_pci_dev(dev),(a),(b),(c)) + +#define dma_map_single(dev,a,b,c) \ + pci_map_single(to_pci_dev(dev),(a),(b),(c)) +#define dma_unmap_single(dev,a,b,c) \ + pci_unmap_single(to_pci_dev(dev),(a),(b),(c)) + +#define dma_map_sg(dev, sg, nents, dir) \ + pci_map_sg(to_pci_dev(dev), (sg), (nents), (dir) +#define dma_unmap_sg(dev, sg, nents, dir) \ + pci_unmap_sg(to_pci_dev(dev), (sg), (nents), (dir) + +#define dma_sync_single(dev,a,b,c) \ + pci_dma_sync_single(to_pci_dev(dev),(a),(b),(c)) + +/* for range just sync everything, that's all the pci API can do */ +#define dma_sync_single_range(dev,addr,off,sz,dir) \ + pci_dma_sync_single(to_pci_dev(dev),(addr),(off)+(sz),(dir)) + +#define dma_set_mask(dev,mask) \ + pci_set_dma_mask(to_pci_dev(dev),(mask)) + +/* hlist_* code - double linked lists */ +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = NULL; + n->pprev = NULL; +} + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +static inline void INIT_HLIST_NODE(struct hlist_node *h) +{ + h->next = NULL; + h->pprev = NULL; +} + +#ifndef might_sleep +#define might_sleep() +#endif +#else +static inline struct device *pci_dev_to_dev(struct pci_dev *pdev) +{ + return &pdev->dev; +} +#endif /* <= 2.5.0 */ + +/*****************************************************************************/ +/* 2.5.28 => 2.4.23 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,5,28) ) + +#include +#define work_struct tq_struct +#undef INIT_WORK +#define INIT_WORK(a,b) INIT_TQUEUE(a,(void (*)(void *))b,a) +#undef container_of +#define container_of list_entry +#define schedule_work schedule_task +#define flush_scheduled_work flush_scheduled_tasks +#define cancel_work_sync(x) flush_scheduled_work() + +#endif /* 2.5.28 => 2.4.17 */ + +/*****************************************************************************/ +/* 2.6.0 => 2.5.28 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) +#ifndef read_barrier_depends +#define read_barrier_depends() rmb() +#endif + +#ifndef rcu_head +struct __kc_callback_head { + struct __kc_callback_head *next; + void (*func)(struct callback_head *head); +}; +#define rcu_head __kc_callback_head +#endif + +#undef get_cpu +#define get_cpu() smp_processor_id() +#undef put_cpu +#define put_cpu() do { } while(0) +#define MODULE_INFO(version, _version) +#ifndef CONFIG_E1000_DISABLE_PACKET_SPLIT +#define CONFIG_E1000_DISABLE_PACKET_SPLIT 1 +#endif +#ifndef CONFIG_IGB_DISABLE_PACKET_SPLIT +#define CONFIG_IGB_DISABLE_PACKET_SPLIT 1 +#endif +#ifndef CONFIG_IGC_DISABLE_PACKET_SPLIT +#define CONFIG_IGC_DISABLE_PACKET_SPLIT 1 +#endif + +#define dma_set_coherent_mask(dev,mask) 1 + +#undef dev_put +#define dev_put(dev) __dev_put(dev) + +#ifndef skb_fill_page_desc +#define skb_fill_page_desc _kc_skb_fill_page_desc +void _kc_skb_fill_page_desc(struct sk_buff *skb, int i, struct page *page, int off, int size); +#endif + +#undef ALIGN +#define ALIGN(x,a) (((x)+(a)-1)&~((a)-1)) + +#ifndef page_count +#define page_count(p) atomic_read(&(p)->count) +#endif + +#ifdef MAX_NUMNODES +#undef MAX_NUMNODES +#endif +#define MAX_NUMNODES 1 + +/* find_first_bit and find_next bit are not defined for most + * 2.4 kernels (except for the redhat 2.4.21 kernels + */ +#include +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) +#undef find_next_bit +#define find_next_bit _kc_find_next_bit +unsigned long _kc_find_next_bit(const unsigned long *addr, unsigned long size, + unsigned long offset); +#define find_first_bit(addr, size) find_next_bit((addr), (size), 0) + +#ifndef netdev_name +static inline const char *_kc_netdev_name(const struct net_device *dev) +{ + if (strchr(dev->name, '%')) + return "(unregistered net_device)"; + return dev->name; +} +#define netdev_name(netdev) _kc_netdev_name(netdev) +#endif /* netdev_name */ + +#ifndef strlcpy +#define strlcpy _kc_strlcpy +size_t _kc_strlcpy(char *dest, const char *src, size_t size); +#endif /* strlcpy */ + +#ifndef do_div +#if BITS_PER_LONG == 64 +# define do_div(n,base) ({ \ + uint32_t __base = (base); \ + uint32_t __rem; \ + __rem = ((uint64_t)(n)) % __base; \ + (n) = ((uint64_t)(n)) / __base; \ + __rem; \ + }) +#elif BITS_PER_LONG == 32 +uint32_t _kc__div64_32(uint64_t *dividend, uint32_t divisor); +# define do_div(n,base) ({ \ + uint32_t __base = (base); \ + uint32_t __rem; \ + if (likely(((n) >> 32) == 0)) { \ + __rem = (uint32_t)(n) % __base; \ + (n) = (uint32_t)(n) / __base; \ + } else \ + __rem = _kc__div64_32(&(n), __base); \ + __rem; \ + }) +#else /* BITS_PER_LONG == ?? */ +# error do_div() does not yet support the C64 +#endif /* BITS_PER_LONG */ +#endif /* do_div */ + +#ifndef NSEC_PER_SEC +#define NSEC_PER_SEC 1000000000L +#endif + +#undef HAVE_I2C_SUPPORT +#else /* 2.6.0 */ + +#endif /* 2.6.0 => 2.5.28 */ +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,3) ) +#define dma_pool pci_pool +#define dma_pool_destroy pci_pool_destroy +#define dma_pool_alloc pci_pool_alloc +#define dma_pool_free pci_pool_free + +#define dma_pool_create(name,dev,size,align,allocation) \ + pci_pool_create((name),to_pci_dev(dev),(size),(align),(allocation)) +#endif /* < 2.6.3 */ + +/*****************************************************************************/ +/* 2.6.4 => 2.6.0 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4) ) +#define MODULE_VERSION(_version) MODULE_INFO(version, _version) +#endif /* 2.6.4 => 2.6.0 */ + +/*****************************************************************************/ +/* 2.6.5 => 2.6.0 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5) ) +#define dma_sync_single_for_cpu dma_sync_single +#define dma_sync_single_for_device dma_sync_single +#define dma_sync_single_range_for_cpu dma_sync_single_range +#define dma_sync_single_range_for_device dma_sync_single_range +#ifndef pci_dma_mapping_error +#define pci_dma_mapping_error _kc_pci_dma_mapping_error +static inline int _kc_pci_dma_mapping_error(dma_addr_t dma_addr) +{ + return dma_addr == 0; +} +#endif +#endif /* 2.6.5 => 2.6.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4) ) +int _kc_scnprintf(char * buf, size_t size, const char *fmt, ...); +#define scnprintf(buf, size, fmt, args...) _kc_scnprintf(buf, size, fmt, ##args) +#endif /* < 2.6.4 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,6) ) +/* taken from 2.6 include/linux/bitmap.h */ +#undef bitmap_zero +#define bitmap_zero _kc_bitmap_zero +static inline void _kc_bitmap_zero(unsigned long *dst, int nbits) +{ + if (nbits <= BITS_PER_LONG) + *dst = 0UL; + else { + int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); + memset(dst, 0, len); + } +} +#define page_to_nid(x) 0 + +#endif /* < 2.6.6 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7) ) +#undef if_mii +#define if_mii _kc_if_mii +static inline struct mii_ioctl_data *_kc_if_mii(struct ifreq *rq) +{ + return (struct mii_ioctl_data *) &rq->ifr_ifru; +} + +#ifndef __force +#define __force +#endif +#endif /* < 2.6.7 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) ) +#ifndef PCI_EXP_DEVCTL +#define PCI_EXP_DEVCTL 8 +#endif +#ifndef PCI_EXP_DEVCTL_CERE +#define PCI_EXP_DEVCTL_CERE 0x0001 +#endif +#define PCI_EXP_FLAGS 2 /* Capabilities register */ +#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */ +#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ +#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ +#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ +#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ +#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ +#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ +#define PCI_EXP_DEVCAP 4 /* Device capabilities */ +#define PCI_EXP_DEVSTA 10 /* Device Status */ +#define msleep(x) do { set_current_state(TASK_UNINTERRUPTIBLE); \ + schedule_timeout((x * HZ)/1000 + 2); \ + } while (0) + +#endif /* < 2.6.8 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)) +#include +#define __iomem + +#ifndef kcalloc +#define kcalloc(n, size, flags) _kc_kzalloc(((n) * (size)), flags) +void *_kc_kzalloc(size_t size, int flags); +#endif +#define MSEC_PER_SEC 1000L +static inline unsigned int _kc_jiffies_to_msecs(const unsigned long j) +{ +#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ) + return (MSEC_PER_SEC / HZ) * j; +#elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC) + return (j + (HZ / MSEC_PER_SEC) - 1)/(HZ / MSEC_PER_SEC); +#else + return (j * MSEC_PER_SEC) / HZ; +#endif +} +static inline unsigned long _kc_msecs_to_jiffies(const unsigned int m) +{ + if (m > _kc_jiffies_to_msecs(MAX_JIFFY_OFFSET)) + return MAX_JIFFY_OFFSET; +#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ) + return (m + (MSEC_PER_SEC / HZ) - 1) / (MSEC_PER_SEC / HZ); +#elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC) + return m * (HZ / MSEC_PER_SEC); +#else + return (m * HZ + MSEC_PER_SEC - 1) / MSEC_PER_SEC; +#endif +} + +#define msleep_interruptible _kc_msleep_interruptible +static inline unsigned long _kc_msleep_interruptible(unsigned int msecs) +{ + unsigned long timeout = _kc_msecs_to_jiffies(msecs) + 1; + + while (timeout && !signal_pending(current)) { + __set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(timeout); + } + return _kc_jiffies_to_msecs(timeout); +} + +/* Basic mode control register. */ +#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */ + +#ifndef __le16 +#define __le16 u16 +#endif +#ifndef __le32 +#define __le32 u32 +#endif +#ifndef __le64 +#define __le64 u64 +#endif +#ifndef __be16 +#define __be16 u16 +#endif +#ifndef __be32 +#define __be32 u32 +#endif +#ifndef __be64 +#define __be64 u64 +#endif + +static inline struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb) +{ + return (struct vlan_ethhdr *)skb->mac.raw; +} + +/* Wake-On-Lan options. */ +#define WAKE_PHY BIT(0) +#define WAKE_UCAST BIT(1) +#define WAKE_MCAST BIT(2) +#define WAKE_BCAST BIT(3) +#define WAKE_ARP BIT(4) +#define WAKE_MAGIC BIT(5) +#define WAKE_MAGICSECURE BIT(6) /* only meaningful if WAKE_MAGIC */ + +#define skb_header_pointer _kc_skb_header_pointer +static inline void *_kc_skb_header_pointer(const struct sk_buff *skb, + int offset, int len, void *buffer) +{ + int hlen = skb_headlen(skb); + + if (hlen - offset >= len) + return skb->data + offset; + +#ifdef MAX_SKB_FRAGS + if (skb_copy_bits(skb, offset, buffer, len) < 0) + return NULL; + + return buffer; +#else + return NULL; +#endif + +#ifndef NETDEV_TX_OK +#define NETDEV_TX_OK 0 +#endif +#ifndef NETDEV_TX_BUSY +#define NETDEV_TX_BUSY 1 +#endif +#ifndef NETDEV_TX_LOCKED +#define NETDEV_TX_LOCKED -1 +#endif +} + +#ifndef __bitwise +#define __bitwise +#endif +#endif /* < 2.6.9 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) ) +#ifdef module_param_array_named +#undef module_param_array_named +#define module_param_array_named(name, array, type, nump, perm) \ + static struct kparam_array __param_arr_##name \ + = { ARRAY_SIZE(array), nump, param_set_##type, param_get_##type, \ + sizeof(array[0]), array }; \ + module_param_call(name, param_array_set, param_array_get, \ + &__param_arr_##name, perm) +#endif /* module_param_array_named */ +/* + * num_online is broken for all < 2.6.10 kernels. This is needed to support + * Node module parameter of ixgbe. + */ +#undef num_online_nodes +#define num_online_nodes(n) 1 +extern DECLARE_BITMAP(_kcompat_node_online_map, MAX_NUMNODES); +#undef node_online_map +#define node_online_map _kcompat_node_online_map +#define pci_get_class pci_find_class +#endif /* < 2.6.10 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) ) +#define PCI_D0 0 +#define PCI_D1 1 +#define PCI_D2 2 +#define PCI_D3hot 3 +#define PCI_D3cold 4 +typedef int pci_power_t; +#define pci_choose_state(pdev,state) state +#define PMSG_SUSPEND 3 +#define PCI_EXP_LNKCTL 16 + +#undef NETIF_F_LLTX + +#ifndef ARCH_HAS_PREFETCH +#define prefetch(X) +#endif + +#ifndef NET_IP_ALIGN +#define NET_IP_ALIGN 2 +#endif + +#define KC_USEC_PER_SEC 1000000L +#define usecs_to_jiffies _kc_usecs_to_jiffies +static inline unsigned int _kc_jiffies_to_usecs(const unsigned long j) +{ +#if HZ <= KC_USEC_PER_SEC && !(KC_USEC_PER_SEC % HZ) + return (KC_USEC_PER_SEC / HZ) * j; +#elif HZ > KC_USEC_PER_SEC && !(HZ % KC_USEC_PER_SEC) + return (j + (HZ / KC_USEC_PER_SEC) - 1)/(HZ / KC_USEC_PER_SEC); +#else + return (j * KC_USEC_PER_SEC) / HZ; +#endif +} +static inline unsigned long _kc_usecs_to_jiffies(const unsigned int m) +{ + if (m > _kc_jiffies_to_usecs(MAX_JIFFY_OFFSET)) + return MAX_JIFFY_OFFSET; +#if HZ <= KC_USEC_PER_SEC && !(KC_USEC_PER_SEC % HZ) + return (m + (KC_USEC_PER_SEC / HZ) - 1) / (KC_USEC_PER_SEC / HZ); +#elif HZ > KC_USEC_PER_SEC && !(HZ % KC_USEC_PER_SEC) + return m * (HZ / KC_USEC_PER_SEC); +#else + return (m * HZ + KC_USEC_PER_SEC - 1) / KC_USEC_PER_SEC; +#endif +} + +#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ +#define PCI_EXP_LNKSTA 18 /* Link Status */ +#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ +#define PCI_EXP_SLTCTL 24 /* Slot Control */ +#define PCI_EXP_SLTSTA 26 /* Slot Status */ +#define PCI_EXP_RTCTL 28 /* Root Control */ +#define PCI_EXP_RTCAP 30 /* Root Capabilities */ +#define PCI_EXP_RTSTA 32 /* Root Status */ +#endif /* < 2.6.11 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12) ) +#include +#define USE_REBOOT_NOTIFIER + +/* Generic MII registers. */ +#define MII_CTRL1000 0x09 /* 1000BASE-T control */ +#define MII_STAT1000 0x0a /* 1000BASE-T status */ +/* Advertisement control register. */ +#define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */ +#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymmetric pause */ +/* Link partner ability register. */ +#define LPA_PAUSE_CAP 0x0400 /* Can pause */ +#define LPA_PAUSE_ASYM 0x0800 /* Can pause asymetrically */ +/* 1000BASE-T Control register */ +#define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ +#define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ +/* 1000BASE-T Status register */ +#define LPA_1000LOCALRXOK 0x2000 /* Link partner local receiver status */ +#define LPA_1000REMRXOK 0x1000 /* Link partner remote receiver status */ + +#ifndef is_zero_ether_addr +#define is_zero_ether_addr _kc_is_zero_ether_addr +static inline int _kc_is_zero_ether_addr(const u8 *addr) +{ + return !(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]); +} +#endif /* is_zero_ether_addr */ +#ifndef is_multicast_ether_addr +#define is_multicast_ether_addr _kc_is_multicast_ether_addr +static inline int _kc_is_multicast_ether_addr(const u8 *addr) +{ + return addr[0] & 0x01; +} +#endif /* is_multicast_ether_addr */ +#endif /* < 2.6.12 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) ) +#ifndef kstrdup +#define kstrdup _kc_kstrdup +char *_kc_kstrdup(const char *s, unsigned int gfp); +#endif +#endif /* < 2.6.13 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) ) +#define pm_message_t u32 +#ifndef kzalloc +#define kzalloc _kc_kzalloc +void *_kc_kzalloc(size_t size, int flags); +#endif + +/* Generic MII registers. */ +#define MII_ESTATUS 0x0f /* Extended Status */ +/* Basic mode status register. */ +#define BMSR_ESTATEN 0x0100 /* Extended Status in R15 */ +/* Extended status register. */ +#define ESTATUS_1000_TFULL 0x2000 /* Can do 1000BT Full */ +#define ESTATUS_1000_THALF 0x1000 /* Can do 1000BT Half */ + +#define SUPPORTED_Pause BIT(13) +#define SUPPORTED_Asym_Pause BIT(14) +#define ADVERTISED_Pause BIT(13) +#define ADVERTISED_Asym_Pause BIT(14) + +#if (!(RHEL_RELEASE_CODE && \ + (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(4,3)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,0)))) +#if ((LINUX_VERSION_CODE == KERNEL_VERSION(2,6,9)) && !defined(gfp_t)) +#define gfp_t unsigned +#else +typedef unsigned gfp_t; +#endif +#endif /* !RHEL4.3->RHEL5.0 */ + +#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9) ) +#ifdef CONFIG_X86_64 +#define dma_sync_single_range_for_cpu(dev, addr, off, sz, dir) \ + dma_sync_single_for_cpu((dev), (addr), (off) + (sz), (dir)) +#define dma_sync_single_range_for_device(dev, addr, off, sz, dir) \ + dma_sync_single_for_device((dev), (addr), (off) + (sz), (dir)) +#endif +#endif +#endif /* < 2.6.14 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) ) +#ifndef kfree_rcu +/* this is placed here due to a lack of rcu_barrier in previous kernels */ +#define kfree_rcu(_ptr, _offset) kfree(_ptr) +#endif /* kfree_rcu */ +#ifndef vmalloc_node +#define vmalloc_node(a,b) vmalloc(a) +#endif /* vmalloc_node*/ + +#define setup_timer(_timer, _function, _data) \ +do { \ + (_timer)->function = _function; \ + (_timer)->data = _data; \ + init_timer(_timer); \ +} while (0) +#ifndef device_can_wakeup +#define device_can_wakeup(dev) (1) +#endif +#ifndef device_set_wakeup_enable +#define device_set_wakeup_enable(dev, val) do{}while(0) +#endif +#ifndef device_init_wakeup +#define device_init_wakeup(dev,val) do {} while (0) +#endif +static inline unsigned _kc_compare_ether_addr(const u8 *addr1, const u8 *addr2) +{ + const u16 *a = (const u16 *) addr1; + const u16 *b = (const u16 *) addr2; + + return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) != 0; +} +#undef compare_ether_addr +#define compare_ether_addr(addr1, addr2) _kc_compare_ether_addr(addr1, addr2) +#endif /* < 2.6.15 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) ) +#undef DEFINE_MUTEX +#define DEFINE_MUTEX(x) DECLARE_MUTEX(x) +#define mutex_lock(x) down_interruptible(x) +#define mutex_unlock(x) up(x) + +#ifndef ____cacheline_internodealigned_in_smp +#ifdef CONFIG_SMP +#define ____cacheline_internodealigned_in_smp ____cacheline_aligned_in_smp +#else +#define ____cacheline_internodealigned_in_smp +#endif /* CONFIG_SMP */ +#endif /* ____cacheline_internodealigned_in_smp */ +#undef HAVE_PCI_ERS +#else /* 2.6.16 and above */ +#undef HAVE_PCI_ERS +#define HAVE_PCI_ERS +#if ( SLE_VERSION_CODE && SLE_VERSION_CODE == SLE_VERSION(10,4,0) ) +#ifdef device_can_wakeup +#undef device_can_wakeup +#endif /* device_can_wakeup */ +#define device_can_wakeup(dev) 1 +#endif /* SLE_VERSION(10,4,0) */ +#endif /* < 2.6.16 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) ) +#ifndef dev_notice +#define dev_notice(dev, fmt, args...) \ + dev_printk(KERN_NOTICE, dev, fmt, ## args) +#endif + +#ifndef first_online_node +#define first_online_node 0 +#endif +#ifndef NET_SKB_PAD +#define NET_SKB_PAD 16 +#endif +#endif /* < 2.6.17 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) ) + +#ifndef IRQ_HANDLED +#define irqreturn_t void +#define IRQ_HANDLED +#define IRQ_NONE +#endif + +#ifndef IRQF_PROBE_SHARED +#ifdef SA_PROBEIRQ +#define IRQF_PROBE_SHARED SA_PROBEIRQ +#else +#define IRQF_PROBE_SHARED 0 +#endif +#endif + +#ifndef IRQF_SHARED +#define IRQF_SHARED SA_SHIRQ +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#ifndef skb_is_gso +#ifdef NETIF_F_TSO +#define skb_is_gso _kc_skb_is_gso +static inline int _kc_skb_is_gso(const struct sk_buff *skb) +{ + return skb_shinfo(skb)->gso_size; +} +#else +#define skb_is_gso(a) 0 +#endif +#endif + +#ifndef resource_size_t +#define resource_size_t unsigned long +#endif + +#ifdef skb_pad +#undef skb_pad +#endif +#define skb_pad(x,y) _kc_skb_pad(x, y) +int _kc_skb_pad(struct sk_buff *skb, int pad); +#ifdef skb_padto +#undef skb_padto +#endif +#define skb_padto(x,y) _kc_skb_padto(x, y) +static inline int _kc_skb_padto(struct sk_buff *skb, unsigned int len) +{ + unsigned int size = skb->len; + if(likely(size >= len)) + return 0; + return _kc_skb_pad(skb, len - size); +} + +#ifndef DECLARE_PCI_UNMAP_ADDR +#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) \ + dma_addr_t ADDR_NAME +#define DECLARE_PCI_UNMAP_LEN(LEN_NAME) \ + u32 LEN_NAME +#define pci_unmap_addr(PTR, ADDR_NAME) \ + ((PTR)->ADDR_NAME) +#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL) \ + (((PTR)->ADDR_NAME) = (VAL)) +#define pci_unmap_len(PTR, LEN_NAME) \ + ((PTR)->LEN_NAME) +#define pci_unmap_len_set(PTR, LEN_NAME, VAL) \ + (((PTR)->LEN_NAME) = (VAL)) +#endif /* DECLARE_PCI_UNMAP_ADDR */ +#endif /* < 2.6.18 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ) +enum pcie_link_width { + PCIE_LNK_WIDTH_RESRV = 0x00, + PCIE_LNK_X1 = 0x01, + PCIE_LNK_X2 = 0x02, + PCIE_LNK_X4 = 0x04, + PCIE_LNK_X8 = 0x08, + PCIE_LNK_X12 = 0x0C, + PCIE_LNK_X16 = 0x10, + PCIE_LNK_X32 = 0x20, + PCIE_LNK_WIDTH_UNKNOWN = 0xFF, +}; + +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(5,0))) +#define i_private u.generic_ip +#endif /* >= RHEL 5.0 */ + +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#endif +#ifndef __ALIGN_MASK +#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) +#endif +#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) ) +#if (!((RHEL_RELEASE_CODE && \ + ((RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(4,4) && \ + RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,0)) || \ + (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(5,0)))))) +typedef irqreturn_t (*irq_handler_t)(int, void*, struct pt_regs *); +#endif +#if (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(6,0)) +#undef CONFIG_INET_LRO +#undef CONFIG_INET_LRO_MODULE +#endif +typedef irqreturn_t (*new_handler_t)(int, void*); +static inline irqreturn_t _kc_request_irq(unsigned int irq, new_handler_t handler, unsigned long flags, const char *devname, void *dev_id) +#else /* 2.4.x */ +typedef void (*irq_handler_t)(int, void*, struct pt_regs *); +typedef void (*new_handler_t)(int, void*); +static inline int _kc_request_irq(unsigned int irq, new_handler_t handler, unsigned long flags, const char *devname, void *dev_id) +#endif /* >= 2.5.x */ +{ + irq_handler_t new_handler = (irq_handler_t) handler; + return request_irq(irq, new_handler, flags, devname, dev_id); +} + +#undef request_irq +#define request_irq(irq, handler, flags, devname, dev_id) _kc_request_irq((irq), (handler), (flags), (devname), (dev_id)) + +#define irq_handler_t new_handler_t + +#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) ) +#ifndef skb_checksum_help +static inline int __kc_skb_checksum_help(struct sk_buff *skb) +{ + return skb_checksum_help(skb, 0); +} +#define skb_checksum_help(skb) __kc_skb_checksum_help((skb)) +#endif +#endif /* < 2.6.19 && >= 2.6.11 */ + +/* pci_restore_state and pci_save_state handles MSI/PCIE from 2.6.19 */ +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(5,4))) +#define PCIE_CONFIG_SPACE_LEN 256 +#define PCI_CONFIG_SPACE_LEN 64 +#define PCIE_LINK_STATUS 0x12 +#define pci_config_space_ich8lan() do {} while(0) +#undef pci_save_state +int _kc_pci_save_state(struct pci_dev *); +#define pci_save_state(pdev) _kc_pci_save_state(pdev) +#undef pci_restore_state +void _kc_pci_restore_state(struct pci_dev *); +#define pci_restore_state(pdev) _kc_pci_restore_state(pdev) +#endif /* !(RHEL_RELEASE_CODE >= RHEL 5.4) */ + +#ifdef HAVE_PCI_ERS +#undef free_netdev +void _kc_free_netdev(struct net_device *); +#define free_netdev(netdev) _kc_free_netdev(netdev) +#endif +static inline int pci_enable_pcie_error_reporting(struct pci_dev __always_unused *dev) +{ + return 0; +} +#define pci_disable_pcie_error_reporting(dev) do {} while (0) +#define pci_cleanup_aer_uncorrect_error_status(dev) do {} while (0) + +void *_kc_kmemdup(const void *src, size_t len, unsigned gfp); +#define kmemdup(src, len, gfp) _kc_kmemdup(src, len, gfp) +#ifndef bool +#define bool _Bool +#define true 1 +#define false 0 +#endif +#else /* 2.6.19 */ +#include +#include + +#define NEW_SKB_CSUM_HELP +#endif /* < 2.6.19 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ) +#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28) ) +#undef INIT_WORK +#define INIT_WORK(_work, _func) \ +do { \ + INIT_LIST_HEAD(&(_work)->entry); \ + (_work)->pending = 0; \ + (_work)->func = (void (*)(void *))_func; \ + (_work)->data = _work; \ + init_timer(&(_work)->timer); \ +} while (0) +#endif + +#ifndef PCI_VDEVICE +#define PCI_VDEVICE(ven, dev) \ + PCI_VENDOR_ID_##ven, (dev), \ + PCI_ANY_ID, PCI_ANY_ID, 0, 0 +#endif + +#ifndef PCI_VENDOR_ID_INTEL +#define PCI_VENDOR_ID_INTEL 0x8086 +#endif + +#ifndef round_jiffies +#define round_jiffies(x) x +#endif + +#define csum_offset csum + +#define HAVE_EARLY_VMALLOC_NODE +#define dev_to_node(dev) -1 +#undef set_dev_node +/* remove compiler warning with b=b, for unused variable */ +#define set_dev_node(a, b) do { (b) = (b); } while(0) + +#if (!(RHEL_RELEASE_CODE && \ + (((RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(4,7)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,0))) || \ + (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(5,6)))) && \ + !(SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(10,2,0))) +typedef __u16 __bitwise __sum16; +typedef __u32 __bitwise __wsum; +#endif + +#if (!(RHEL_RELEASE_CODE && \ + (((RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(4,7)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,0))) || \ + (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(5,4)))) && \ + !(SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(10,2,0))) +static inline __wsum csum_unfold(__sum16 n) +{ + return (__force __wsum)n; +} +#endif + +#else /* < 2.6.20 */ +#define HAVE_DEVICE_NUMA_NODE +#endif /* < 2.6.20 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21) ) +#define to_net_dev(class) container_of(class, struct net_device, class_dev) +#define NETDEV_CLASS_DEV +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(5,5))) +#define vlan_group_get_device(vg, id) (vg->vlan_devices[id]) +#define vlan_group_set_device(vg, id, dev) \ + do { \ + if (vg) vg->vlan_devices[id] = dev; \ + } while (0) +#endif /* !(RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(5,5)) */ +#define pci_channel_offline(pdev) (pdev->error_state && \ + pdev->error_state != pci_channel_io_normal) +#define pci_request_selected_regions(pdev, bars, name) \ + pci_request_regions(pdev, name) +#define pci_release_selected_regions(pdev, bars) pci_release_regions(pdev); + +#ifndef __aligned +#define __aligned(x) __attribute__((aligned(x))) +#endif + +struct pci_dev *_kc_netdev_to_pdev(struct net_device *netdev); +#define netdev_to_dev(netdev) \ + pci_dev_to_dev(_kc_netdev_to_pdev(netdev)) +#define devm_kzalloc(dev, size, flags) kzalloc(size, flags) +#define devm_kfree(dev, p) kfree(p) +#else /* 2.6.21 */ +static inline struct device *netdev_to_dev(struct net_device *netdev) +{ + return &netdev->dev; +} + +#endif /* < 2.6.21 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) ) +#define tcp_hdr(skb) (skb->h.th) +#define tcp_hdrlen(skb) (skb->h.th->doff << 2) +#define skb_transport_offset(skb) (skb->h.raw - skb->data) +#define skb_transport_header(skb) (skb->h.raw) +#define ipv6_hdr(skb) (skb->nh.ipv6h) +#define ip_hdr(skb) (skb->nh.iph) +#define skb_network_offset(skb) (skb->nh.raw - skb->data) +#define skb_network_header(skb) (skb->nh.raw) +#define skb_tail_pointer(skb) skb->tail +#define skb_reset_tail_pointer(skb) \ + do { \ + skb->tail = skb->data; \ + } while (0) +#define skb_set_tail_pointer(skb, offset) \ + do { \ + skb->tail = skb->data + offset; \ + } while (0) +#define skb_copy_to_linear_data(skb, from, len) \ + memcpy(skb->data, from, len) +#define skb_copy_to_linear_data_offset(skb, offset, from, len) \ + memcpy(skb->data + offset, from, len) +#define skb_network_header_len(skb) (skb->h.raw - skb->nh.raw) +#define pci_register_driver pci_module_init +#define skb_mac_header(skb) skb->mac.raw + +#ifdef NETIF_F_MULTI_QUEUE +#ifndef alloc_etherdev_mq +#define alloc_etherdev_mq(_a, _b) alloc_etherdev(_a) +#endif +#endif /* NETIF_F_MULTI_QUEUE */ + +#ifndef ETH_FCS_LEN +#define ETH_FCS_LEN 4 +#endif +#define cancel_work_sync(x) flush_scheduled_work() +#ifndef udp_hdr +#define udp_hdr _udp_hdr +static inline struct udphdr *_udp_hdr(const struct sk_buff *skb) +{ + return (struct udphdr *)skb_transport_header(skb); +} +#endif + +#ifdef cpu_to_be16 +#undef cpu_to_be16 +#endif +#define cpu_to_be16(x) __constant_htons(x) + +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(5,1))) +enum { + DUMP_PREFIX_NONE, + DUMP_PREFIX_ADDRESS, + DUMP_PREFIX_OFFSET +}; +#endif /* !(RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(5,1)) */ +#ifndef hex_asc +#define hex_asc(x) "0123456789abcdef"[x] +#endif +#include +void _kc_print_hex_dump(const char *level, const char *prefix_str, + int prefix_type, int rowsize, int groupsize, + const void *buf, size_t len, bool ascii); +#define print_hex_dump(lvl, s, t, r, g, b, l, a) \ + _kc_print_hex_dump(lvl, s, t, r, g, b, l, a) +#ifndef ADVERTISED_2500baseX_Full +#define ADVERTISED_2500baseX_Full BIT(15) +#endif +#ifndef SUPPORTED_2500baseX_Full +#define SUPPORTED_2500baseX_Full BIT(15) +#endif + +#ifndef ETH_P_PAUSE +#define ETH_P_PAUSE 0x8808 +#endif + +static inline int compound_order(struct page *page) +{ + return 0; +} + +#define __must_be_array(a) 0 + +#ifndef SKB_WITH_OVERHEAD +#define SKB_WITH_OVERHEAD(X) \ + ((X) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) +#endif +#else /* 2.6.22 */ +#define ETH_TYPE_TRANS_SETS_DEV +#define HAVE_NETDEV_STATS_IN_NETDEV +#endif /* < 2.6.22 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22) ) +#endif /* > 2.6.22 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) ) +#define netif_subqueue_stopped(_a, _b) 0 +#ifndef PTR_ALIGN +#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) +#endif + +#ifndef CONFIG_PM_SLEEP +#define CONFIG_PM_SLEEP CONFIG_PM +#endif + +#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13) ) +#define HAVE_ETHTOOL_GET_PERM_ADDR +#endif /* 2.6.14 through 2.6.22 */ + +static inline int __kc_skb_cow_head(struct sk_buff *skb, unsigned int headroom) +{ + int delta = 0; + + if (headroom > (skb->data - skb->head)) + delta = headroom - (skb->data - skb->head); + + if (delta || skb_header_cloned(skb)) + return pskb_expand_head(skb, ALIGN(delta, NET_SKB_PAD), 0, + GFP_ATOMIC); + return 0; +} +#define skb_cow_head(s, h) __kc_skb_cow_head((s), (h)) +#endif /* < 2.6.23 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) ) +#ifndef ETH_FLAG_LRO +#define ETH_FLAG_LRO NETIF_F_LRO +#endif + +#ifndef ACCESS_ONCE +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) +#endif + +/* if GRO is supported then the napi struct must already exist */ +#ifndef NETIF_F_GRO +/* NAPI API changes in 2.6.24 break everything */ +struct napi_struct { + /* used to look up the real NAPI polling routine */ + int (*poll)(struct napi_struct *, int); + struct net_device *dev; + int weight; +}; +#endif + +#ifdef NAPI +int __kc_adapter_clean(struct net_device *, int *); +/* The following definitions are multi-queue aware, and thus we have a driver + * define list which determines which drivers support multiple queues, and + * thus need these stronger defines. If a driver does not support multi-queue + * functionality, you don't need to add it to this list. + */ +struct net_device *napi_to_poll_dev(const struct napi_struct *napi); + +static inline void __kc_mq_netif_napi_add(struct net_device *dev, struct napi_struct *napi, + int (*poll)(struct napi_struct *, int), int weight) +{ + struct net_device *poll_dev = napi_to_poll_dev(napi); + poll_dev->poll = __kc_adapter_clean; + poll_dev->priv = napi; + poll_dev->weight = weight; + set_bit(__LINK_STATE_RX_SCHED, &poll_dev->state); + set_bit(__LINK_STATE_START, &poll_dev->state); + dev_hold(poll_dev); + napi->poll = poll; + napi->weight = weight; + napi->dev = dev; +} +#define netif_napi_add __kc_mq_netif_napi_add + +static inline void __kc_mq_netif_napi_del(struct napi_struct *napi) +{ + struct net_device *poll_dev = napi_to_poll_dev(napi); + WARN_ON(!test_bit(__LINK_STATE_RX_SCHED, &poll_dev->state)); + dev_put(poll_dev); + memset(poll_dev, 0, sizeof(struct net_device)); +} + +#define netif_napi_del __kc_mq_netif_napi_del + +static inline bool __kc_mq_napi_schedule_prep(struct napi_struct *napi) +{ + return netif_running(napi->dev) && + netif_rx_schedule_prep(napi_to_poll_dev(napi)); +} +#define napi_schedule_prep __kc_mq_napi_schedule_prep + +static inline void __kc_mq_napi_schedule(struct napi_struct *napi) +{ + if (napi_schedule_prep(napi)) + __netif_rx_schedule(napi_to_poll_dev(napi)); +} +#define napi_schedule __kc_mq_napi_schedule + +#define napi_enable(_napi) netif_poll_enable(napi_to_poll_dev(_napi)) +#define napi_disable(_napi) netif_poll_disable(napi_to_poll_dev(_napi)) +#ifdef CONFIG_SMP +static inline void napi_synchronize(const struct napi_struct *n) +{ + struct net_device *dev = napi_to_poll_dev(n); + + while (test_bit(__LINK_STATE_RX_SCHED, &dev->state)) { + /* No hurry. */ + msleep(1); + } +} +#else +#define napi_synchronize(n) barrier() +#endif /* CONFIG_SMP */ +#define __napi_schedule(_napi) __netif_rx_schedule(napi_to_poll_dev(_napi)) +static inline void _kc_napi_complete(struct napi_struct *napi) +{ +#ifdef NETIF_F_GRO + napi_gro_flush(napi); +#endif + netif_rx_complete(napi_to_poll_dev(napi)); +} +#define napi_complete _kc_napi_complete +#else /* NAPI */ + +/* The following definitions are only used if we don't support NAPI at all. */ + +static inline __kc_netif_napi_add(struct net_device *dev, struct napi_struct *napi, + int (*poll)(struct napi_struct *, int), int weight) +{ + dev->poll = poll; + dev->weight = weight; + napi->poll = poll; + napi->weight = weight; + napi->dev = dev; +} +#define netif_napi_del(_a) do {} while (0) +#endif /* NAPI */ + +#undef dev_get_by_name +#define dev_get_by_name(_a, _b) dev_get_by_name(_b) +#define __netif_subqueue_stopped(_a, _b) netif_subqueue_stopped(_a, _b) +#ifndef DMA_BIT_MASK +#define DMA_BIT_MASK(n) (((n) == 64) ? DMA_64BIT_MASK : ((1ULL<<(n))-1)) +#endif + +#ifdef NETIF_F_TSO6 +#define skb_is_gso_v6 _kc_skb_is_gso_v6 +static inline int _kc_skb_is_gso_v6(const struct sk_buff *skb) +{ + return skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6; +} +#endif /* NETIF_F_TSO6 */ + +#ifndef KERN_CONT +#define KERN_CONT "" +#endif +#ifndef pr_err +#define pr_err(fmt, arg...) \ + printk(KERN_ERR fmt, ##arg) +#endif + +#ifndef rounddown_pow_of_two +#define rounddown_pow_of_two(n) \ + __builtin_constant_p(n) ? ( \ + (n == 1) ? 0 : \ + (1UL << ilog2(n))) : \ + (1UL << (fls_long(n) - 1)) +#endif + +#else /* < 2.6.24 */ +#define HAVE_ETHTOOL_GET_SSET_COUNT +#define HAVE_NETDEV_NAPI_LIST +#endif /* < 2.6.24 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,6,24) ) +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) ) +#define INCLUDE_PM_QOS_PARAMS_H +#include +#else /* >= 3.2.0 */ +#include +#endif /* else >= 3.2.0 */ +#endif /* > 2.6.24 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) ) +#define PM_QOS_CPU_DMA_LATENCY 1 + +#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18) ) +#include +#define PM_QOS_DEFAULT_VALUE INFINITE_LATENCY +#define pm_qos_add_requirement(pm_qos_class, name, value) \ + set_acceptable_latency(name, value) +#define pm_qos_remove_requirement(pm_qos_class, name) \ + remove_acceptable_latency(name) +#define pm_qos_update_requirement(pm_qos_class, name, value) \ + modify_acceptable_latency(name, value) +#else +#define PM_QOS_DEFAULT_VALUE -1 +#define pm_qos_add_requirement(pm_qos_class, name, value) +#define pm_qos_remove_requirement(pm_qos_class, name) +#define pm_qos_update_requirement(pm_qos_class, name, value) { \ + if (value != PM_QOS_DEFAULT_VALUE) { \ + printk(KERN_WARNING "%s: unable to set PM QoS requirement\n", \ + pci_name(adapter->pdev)); \ + } \ +} + +#endif /* > 2.6.18 */ + +#define pci_enable_device_mem(pdev) pci_enable_device(pdev) + +#ifndef DEFINE_PCI_DEVICE_TABLE +#define DEFINE_PCI_DEVICE_TABLE(_table) struct pci_device_id _table[] +#endif /* DEFINE_PCI_DEVICE_TABLE */ + +#ifndef strict_strtol +#define strict_strtol(s, b, r) _kc_strict_strtol(s, b, r) +static inline int _kc_strict_strtol(const char *buf, unsigned int base, long *res) +{ + /* adapted from strict_strtoul() in 2.6.25 */ + char *tail; + long val; + size_t len; + + *res = 0; + len = strlen(buf); + if (!len) + return -EINVAL; + val = simple_strtol(buf, &tail, base); + if (tail == buf) + return -EINVAL; + if ((*tail == '\0') || + ((len == (size_t)(tail - buf) + 1) && (*tail == '\n'))) { + *res = val; + return 0; + } + + return -EINVAL; +} +#endif + +#else /* < 2.6.25 */ + +#endif /* < 2.6.25 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) ) +#ifndef clamp_t +#define clamp_t(type, val, min, max) ({ \ + type __val = (val); \ + type __min = (min); \ + type __max = (max); \ + __val = __val < __min ? __min : __val; \ + __val > __max ? __max : __val; }) +#endif /* clamp_t */ +#undef kzalloc_node +#define kzalloc_node(_size, _flags, _node) kzalloc(_size, _flags) + +void _kc_pci_disable_link_state(struct pci_dev *dev, int state); +#define pci_disable_link_state(p, s) _kc_pci_disable_link_state(p, s) +#else /* < 2.6.26 */ +#define NETDEV_CAN_SET_GSO_MAX_SIZE +#ifdef HAVE_PCI_ASPM_H +#include +#endif +#define HAVE_NETDEV_VLAN_FEATURES +#ifndef PCI_EXP_LNKCAP_ASPMS +#define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ +#endif /* PCI_EXP_LNKCAP_ASPMS */ +#endif /* < 2.6.26 */ +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) ) +static inline void _kc_ethtool_cmd_speed_set(struct ethtool_cmd *ep, + __u32 speed) +{ + ep->speed = (__u16)speed; + /* ep->speed_hi = (__u16)(speed >> 16); */ +} +#define ethtool_cmd_speed_set _kc_ethtool_cmd_speed_set + +static inline __u32 _kc_ethtool_cmd_speed(struct ethtool_cmd *ep) +{ + /* no speed_hi before 2.6.27, and probably no need for it yet */ + return (__u32)ep->speed; +} +#define ethtool_cmd_speed _kc_ethtool_cmd_speed + +#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) ) +#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) && defined(CONFIG_PM)) +#define ANCIENT_PM 1 +#elif ((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) && \ + defined(CONFIG_PM_SLEEP)) +#define NEWER_PM 1 +#endif +#if defined(ANCIENT_PM) || defined(NEWER_PM) +#undef device_set_wakeup_enable +#define device_set_wakeup_enable(dev, val) \ + do { \ + u16 pmc = 0; \ + int pm = pci_find_capability(adapter->pdev, PCI_CAP_ID_PM); \ + if (pm) { \ + pci_read_config_word(adapter->pdev, pm + PCI_PM_PMC, \ + &pmc); \ + } \ + (dev)->power.can_wakeup = !!(pmc >> 11); \ + (dev)->power.should_wakeup = (val && (pmc >> 11)); \ + } while (0) +#endif /* 2.6.15-2.6.22 and CONFIG_PM or 2.6.23-2.6.25 and CONFIG_PM_SLEEP */ +#endif /* 2.6.15 through 2.6.27 */ +#ifndef netif_napi_del +#define netif_napi_del(_a) do {} while (0) +#ifdef NAPI +#ifdef CONFIG_NETPOLL +#undef netif_napi_del +#define netif_napi_del(_a) list_del(&(_a)->dev_list); +#endif +#endif +#endif /* netif_napi_del */ +#ifdef dma_mapping_error +#undef dma_mapping_error +#endif +#define dma_mapping_error(dev, dma_addr) pci_dma_mapping_error(dma_addr) + +#ifdef CONFIG_NETDEVICES_MULTIQUEUE +#define HAVE_TX_MQ +#endif + +#ifndef DMA_ATTR_WEAK_ORDERING +#define DMA_ATTR_WEAK_ORDERING 0 +#endif + +#ifdef HAVE_TX_MQ +void _kc_netif_tx_stop_all_queues(struct net_device *); +void _kc_netif_tx_wake_all_queues(struct net_device *); +void _kc_netif_tx_start_all_queues(struct net_device *); +#define netif_tx_stop_all_queues(a) _kc_netif_tx_stop_all_queues(a) +#define netif_tx_wake_all_queues(a) _kc_netif_tx_wake_all_queues(a) +#define netif_tx_start_all_queues(a) _kc_netif_tx_start_all_queues(a) +#undef netif_stop_subqueue +#define netif_stop_subqueue(_ndev,_qi) do { \ + if (netif_is_multiqueue((_ndev))) \ + netif_stop_subqueue((_ndev), (_qi)); \ + else \ + netif_stop_queue((_ndev)); \ + } while (0) +#undef netif_start_subqueue +#define netif_start_subqueue(_ndev,_qi) do { \ + if (netif_is_multiqueue((_ndev))) \ + netif_start_subqueue((_ndev), (_qi)); \ + else \ + netif_start_queue((_ndev)); \ + } while (0) +#else /* HAVE_TX_MQ */ +#define netif_tx_stop_all_queues(a) netif_stop_queue(a) +#define netif_tx_wake_all_queues(a) netif_wake_queue(a) +#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) ) +#define netif_tx_start_all_queues(a) netif_start_queue(a) +#else +#define netif_tx_start_all_queues(a) do {} while (0) +#endif +#define netif_stop_subqueue(_ndev,_qi) netif_stop_queue((_ndev)) +#define netif_start_subqueue(_ndev,_qi) netif_start_queue((_ndev)) +#endif /* HAVE_TX_MQ */ +#ifndef NETIF_F_MULTI_QUEUE +#define NETIF_F_MULTI_QUEUE 0 +#define netif_is_multiqueue(a) 0 +#define netif_wake_subqueue(a, b) +#endif /* NETIF_F_MULTI_QUEUE */ + +#ifndef __WARN_printf +void __kc_warn_slowpath(const char *file, const int line, + const char *fmt, ...) __attribute__((format(printf, 3, 4))); +#define __WARN_printf(arg...) __kc_warn_slowpath(__FILE__, __LINE__, arg) +#endif /* __WARN_printf */ + +#ifndef WARN +#define WARN(condition, format...) ({ \ + int __ret_warn_on = !!(condition); \ + if (unlikely(__ret_warn_on)) \ + __WARN_printf(format); \ + unlikely(__ret_warn_on); \ +}) +#endif /* WARN */ +#undef HAVE_IXGBE_DEBUG_FS +#undef HAVE_IGB_DEBUG_FS +#define qdisc_reset_all_tx(a) +#else /* < 2.6.27 */ +#include +#define ethtool_cmd_speed_set _kc_ethtool_cmd_speed_set +static inline void _kc_ethtool_cmd_speed_set(struct ethtool_cmd *ep, + __u32 speed) +{ + ep->speed = (__u16)(speed & 0xFFFF); + ep->speed_hi = (__u16)(speed >> 16); +} +#define HAVE_TX_MQ +#define HAVE_NETDEV_SELECT_QUEUE +#ifdef CONFIG_DEBUG_FS +#define HAVE_IXGBE_DEBUG_FS +#define HAVE_IGB_DEBUG_FS +#endif /* CONFIG_DEBUG_FS */ +#endif /* < 2.6.27 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ) +#define pci_ioremap_bar(pdev, bar) ioremap(pci_resource_start(pdev, bar), \ + pci_resource_len(pdev, bar)) +#define pci_wake_from_d3 _kc_pci_wake_from_d3 +#define pci_prepare_to_sleep _kc_pci_prepare_to_sleep +int _kc_pci_wake_from_d3(struct pci_dev *dev, bool enable); +int _kc_pci_prepare_to_sleep(struct pci_dev *dev); +#define netdev_alloc_page(a) alloc_page(GFP_ATOMIC) +#ifndef __skb_queue_head_init +static inline void __kc_skb_queue_head_init(struct sk_buff_head *list) +{ + list->prev = list->next = (struct sk_buff *)list; + list->qlen = 0; +} +#define __skb_queue_head_init(_q) __kc_skb_queue_head_init(_q) +#endif + +#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ +#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ + +#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ +#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ + +#endif /* < 2.6.28 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) ) +#ifndef swap +#define swap(a, b) \ + do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) +#endif +#define pci_request_selected_regions_exclusive(pdev, bars, name) \ + pci_request_selected_regions(pdev, bars, name) +#ifndef CONFIG_NR_CPUS +#define CONFIG_NR_CPUS 1 +#endif /* CONFIG_NR_CPUS */ +#ifndef pcie_aspm_enabled +#define pcie_aspm_enabled() (1) +#endif /* pcie_aspm_enabled */ + +#define PCI_EXP_SLTSTA_PDS 0x0040 /* Presence Detect State */ + +#ifndef PCI_EXP_LNKSTA_CLS +#define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */ +#endif +#ifndef PCI_EXP_LNKSTA_NLW +#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */ +#endif + +#ifndef pci_clear_master +void _kc_pci_clear_master(struct pci_dev *dev); +#define pci_clear_master(dev) _kc_pci_clear_master(dev) +#endif + +#ifndef PCI_EXP_LNKCTL_ASPMC +#define PCI_EXP_LNKCTL_ASPMC 0x0003 /* ASPM Control */ +#endif + +#ifndef PCI_EXP_LNKCAP_MLW +#define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ +#endif + +#else /* < 2.6.29 */ +#ifndef HAVE_NET_DEVICE_OPS +#define HAVE_NET_DEVICE_OPS +#endif +#ifdef CONFIG_DCB +#define HAVE_PFC_MODE_ENABLE +#endif /* CONFIG_DCB */ +#endif /* < 2.6.29 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) ) +#define NO_PTP_SUPPORT +#define skb_rx_queue_recorded(a) false +#define skb_get_rx_queue(a) 0 +#define skb_record_rx_queue(a, b) do {} while (0) +#define skb_tx_hash(n, s) ___kc_skb_tx_hash((n), (s), (n)->real_num_tx_queues) +#ifndef CONFIG_PCI_IOV +#undef pci_enable_sriov +#define pci_enable_sriov(a, b) -ENOTSUPP +#undef pci_disable_sriov +#define pci_disable_sriov(a) do {} while (0) +#endif /* CONFIG_PCI_IOV */ +#ifndef pr_cont +#define pr_cont(fmt, ...) \ + printk(KERN_CONT fmt, ##__VA_ARGS__) +#endif /* pr_cont */ +static inline void _kc_synchronize_irq(unsigned int a) +{ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,5,28) ) + synchronize_irq(); +#else /* < 2.5.28 */ + synchronize_irq(a); +#endif /* < 2.5.28 */ +} +#undef synchronize_irq +#define synchronize_irq(a) _kc_synchronize_irq(a) + +#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ + +#ifdef nr_cpus_node +#undef nr_cpus_node +#define nr_cpus_node(node) cpumask_weight(cpumask_of_node(node)) +#endif + +#if (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(5,5)) +#define HAVE_PCI_DEV_IS_VIRTFN_BIT +#endif /* RHEL >= 5.5 */ + +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(5,5))) +static inline bool pci_is_root_bus(struct pci_bus *pbus) +{ + return !(pbus->parent); +} +#endif + +#else /* < 2.6.30 */ +#define HAVE_ASPM_QUIRKS +#define HAVE_PCI_DEV_IS_VIRTFN_BIT +#endif /* < 2.6.30 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) ) +#define ETH_P_1588 0x88F7 +#define ETH_P_FIP 0x8914 +#ifndef netdev_uc_count +#define netdev_uc_count(dev) ((dev)->uc_count) +#endif +#ifndef netdev_for_each_uc_addr +#define netdev_for_each_uc_addr(uclist, dev) \ + for (uclist = dev->uc_list; uclist; uclist = uclist->next) +#endif +#ifndef PORT_OTHER +#define PORT_OTHER 0xff +#endif +#ifndef MDIO_PHY_ID_PRTAD +#define MDIO_PHY_ID_PRTAD 0x03e0 +#endif +#ifndef MDIO_PHY_ID_DEVAD +#define MDIO_PHY_ID_DEVAD 0x001f +#endif +#ifndef skb_dst +#define skb_dst(s) ((s)->dst) +#endif + +#ifndef SUPPORTED_1000baseKX_Full +#define SUPPORTED_1000baseKX_Full BIT(17) +#endif +#ifndef SUPPORTED_10000baseKX4_Full +#define SUPPORTED_10000baseKX4_Full BIT(18) +#endif +#ifndef SUPPORTED_10000baseKR_Full +#define SUPPORTED_10000baseKR_Full BIT(19) +#endif + +#ifndef ADVERTISED_1000baseKX_Full +#define ADVERTISED_1000baseKX_Full BIT(17) +#endif +#ifndef ADVERTISED_10000baseKX4_Full +#define ADVERTISED_10000baseKX4_Full BIT(18) +#endif +#ifndef ADVERTISED_10000baseKR_Full +#define ADVERTISED_10000baseKR_Full BIT(19) +#endif + +static inline unsigned long dev_trans_start(struct net_device *dev) +{ + return dev->trans_start; +} +#else /* < 2.6.31 */ +#ifndef HAVE_NETDEV_STORAGE_ADDRESS +#define HAVE_NETDEV_STORAGE_ADDRESS +#endif +#ifndef HAVE_NETDEV_HW_ADDR +#define HAVE_NETDEV_HW_ADDR +#endif +#ifndef HAVE_TRANS_START_IN_QUEUE +#define HAVE_TRANS_START_IN_QUEUE +#endif +#ifndef HAVE_INCLUDE_LINUX_MDIO_H +#define HAVE_INCLUDE_LINUX_MDIO_H +#endif +#include +#endif /* < 2.6.31 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) ) +#undef netdev_tx_t +#define netdev_tx_t int +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +#ifndef NETIF_F_FCOE_MTU +#define NETIF_F_FCOE_MTU BIT(26) +#endif +#endif /* CONFIG_FCOE || CONFIG_FCOE_MODULE */ + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) +static inline int _kc_pm_runtime_get_sync() +{ + return 1; +} +#define pm_runtime_get_sync(dev) _kc_pm_runtime_get_sync() +#else /* 2.6.0 => 2.6.32 */ +static inline int _kc_pm_runtime_get_sync(struct device __always_unused *dev) +{ + return 1; +} +#ifndef pm_runtime_get_sync +#define pm_runtime_get_sync(dev) _kc_pm_runtime_get_sync(dev) +#endif +#endif /* 2.6.0 => 2.6.32 */ +#ifndef pm_runtime_put +#define pm_runtime_put(dev) do {} while (0) +#endif +#ifndef pm_runtime_put_sync +#define pm_runtime_put_sync(dev) do {} while (0) +#endif +#ifndef pm_runtime_resume +#define pm_runtime_resume(dev) do {} while (0) +#endif +#ifndef pm_schedule_suspend +#define pm_schedule_suspend(dev, t) do {} while (0) +#endif +#ifndef pm_runtime_set_suspended +#define pm_runtime_set_suspended(dev) do {} while (0) +#endif +#ifndef pm_runtime_disable +#define pm_runtime_disable(dev) do {} while (0) +#endif +#ifndef pm_runtime_put_noidle +#define pm_runtime_put_noidle(dev) do {} while (0) +#endif +#ifndef pm_runtime_set_active +#define pm_runtime_set_active(dev) do {} while (0) +#endif +#ifndef pm_runtime_enable +#define pm_runtime_enable(dev) do {} while (0) +#endif +#ifndef pm_runtime_get_noresume +#define pm_runtime_get_noresume(dev) do {} while (0) +#endif +#else /* < 2.6.32 */ +#if (RHEL_RELEASE_CODE && \ + (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,2)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0))) +#define HAVE_RHEL6_NET_DEVICE_EXTENDED +#endif /* RHEL >= 6.2 && RHEL < 7.0 */ +#if (RHEL_RELEASE_CODE && \ + (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,6)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0))) +#define HAVE_RHEL6_NET_DEVICE_OPS_EXT +#define HAVE_NDO_SET_FEATURES +#endif /* RHEL >= 6.6 && RHEL < 7.0 */ +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +#ifndef HAVE_NETDEV_OPS_FCOE_ENABLE +#define HAVE_NETDEV_OPS_FCOE_ENABLE +#endif +#endif /* CONFIG_FCOE || CONFIG_FCOE_MODULE */ +#ifdef CONFIG_DCB +#ifndef HAVE_DCBNL_OPS_GETAPP +#define HAVE_DCBNL_OPS_GETAPP +#endif +#endif /* CONFIG_DCB */ +#include +/* IOV bad DMA target work arounds require at least this kernel rev support */ +#define HAVE_PCIE_TYPE +#endif /* < 2.6.32 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) ) +#ifndef pci_pcie_cap +#define pci_pcie_cap(pdev) pci_find_capability(pdev, PCI_CAP_ID_EXP) +#endif +#ifndef IPV4_FLOW +#define IPV4_FLOW 0x10 +#endif /* IPV4_FLOW */ +#ifndef IPV6_FLOW +#define IPV6_FLOW 0x11 +#endif /* IPV6_FLOW */ +/* Features back-ported to RHEL6 or SLES11 SP1 after 2.6.32 */ +#if ( (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,0)) || \ + (SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(11,1,0)) ) +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +#ifndef HAVE_NETDEV_OPS_FCOE_GETWWN +#define HAVE_NETDEV_OPS_FCOE_GETWWN +#endif +#endif /* CONFIG_FCOE || CONFIG_FCOE_MODULE */ +#endif /* RHEL6 or SLES11 SP1 */ +#ifndef __percpu +#define __percpu +#endif /* __percpu */ + +#ifndef PORT_DA +#define PORT_DA PORT_OTHER +#endif /* PORT_DA */ +#ifndef PORT_NONE +#define PORT_NONE PORT_OTHER +#endif + +#if ((RHEL_RELEASE_CODE && \ + (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,3)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)))) +#if !defined(CONFIG_X86_32) && !defined(CONFIG_NEED_DMA_MAP_STATE) +#undef DEFINE_DMA_UNMAP_ADDR +#define DEFINE_DMA_UNMAP_ADDR(ADDR_NAME) dma_addr_t ADDR_NAME +#undef DEFINE_DMA_UNMAP_LEN +#define DEFINE_DMA_UNMAP_LEN(LEN_NAME) __u32 LEN_NAME +#undef dma_unmap_addr +#define dma_unmap_addr(PTR, ADDR_NAME) ((PTR)->ADDR_NAME) +#undef dma_unmap_addr_set +#define dma_unmap_addr_set(PTR, ADDR_NAME, VAL) (((PTR)->ADDR_NAME) = (VAL)) +#undef dma_unmap_len +#define dma_unmap_len(PTR, LEN_NAME) ((PTR)->LEN_NAME) +#undef dma_unmap_len_set +#define dma_unmap_len_set(PTR, LEN_NAME, VAL) (((PTR)->LEN_NAME) = (VAL)) +#endif /* CONFIG_X86_64 && !CONFIG_NEED_DMA_MAP_STATE */ +#endif /* RHEL_RELEASE_CODE */ + +#if (!(RHEL_RELEASE_CODE && \ + (((RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(5,8)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(6,0))) || \ + ((RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,1)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)))))) +static inline bool pci_is_pcie(struct pci_dev *dev) +{ + return !!pci_pcie_cap(dev); +} +#endif /* RHEL_RELEASE_CODE */ + +#if (!(RHEL_RELEASE_CODE && \ + (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,2)))) +#define sk_tx_queue_get(_sk) (-1) +#define sk_tx_queue_set(_sk, _tx_queue) do {} while(0) +#endif /* !(RHEL >= 6.2) */ + +#if (RHEL_RELEASE_CODE && \ + (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,4)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0))) +#define HAVE_RHEL6_ETHTOOL_OPS_EXT_STRUCT +#define HAVE_ETHTOOL_GRXFHINDIR_SIZE +#define HAVE_ETHTOOL_SET_PHYS_ID +#define HAVE_ETHTOOL_GET_TS_INFO +#if (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(6,5)) +#define HAVE_ETHTOOL_GSRSSH +#define HAVE_RHEL6_SRIOV_CONFIGURE +#define HAVE_RXFH_NONCONST +#endif /* RHEL > 6.5 */ +#endif /* RHEL >= 6.4 && RHEL < 7.0 */ + +#else /* < 2.6.33 */ +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +#ifndef HAVE_NETDEV_OPS_FCOE_GETWWN +#define HAVE_NETDEV_OPS_FCOE_GETWWN +#endif +#endif /* CONFIG_FCOE || CONFIG_FCOE_MODULE */ +#endif /* < 2.6.33 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) ) +#if (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(6,0)) +#ifndef pci_num_vf +#define pci_num_vf(pdev) _kc_pci_num_vf(pdev) +int _kc_pci_num_vf(struct pci_dev *dev); +#endif +#endif /* RHEL_RELEASE_CODE */ + +#ifndef dev_is_pci +#define dev_is_pci(d) ((d)->bus == &pci_bus_type) +#endif + +#ifndef ETH_FLAG_NTUPLE +#define ETH_FLAG_NTUPLE NETIF_F_NTUPLE +#endif + +#ifndef netdev_mc_count +#define netdev_mc_count(dev) ((dev)->mc_count) +#endif +#ifndef netdev_mc_empty +#define netdev_mc_empty(dev) (netdev_mc_count(dev) == 0) +#endif +#ifndef netdev_for_each_mc_addr +#define netdev_for_each_mc_addr(mclist, dev) \ + for (mclist = dev->mc_list; mclist; mclist = mclist->next) +#endif +#ifndef netdev_uc_count +#define netdev_uc_count(dev) ((dev)->uc.count) +#endif +#ifndef netdev_uc_empty +#define netdev_uc_empty(dev) (netdev_uc_count(dev) == 0) +#endif +#ifndef netdev_for_each_uc_addr +#define netdev_for_each_uc_addr(ha, dev) \ + list_for_each_entry(ha, &dev->uc.list, list) +#endif +#ifndef dma_set_coherent_mask +#define dma_set_coherent_mask(dev,mask) \ + pci_set_consistent_dma_mask(to_pci_dev(dev),(mask)) +#endif +#ifndef pci_dev_run_wake +#define pci_dev_run_wake(pdev) (0) +#endif + +/* netdev logging taken from include/linux/netdevice.h */ +#ifndef netdev_name +static inline const char *_kc_netdev_name(const struct net_device *dev) +{ + if (dev->reg_state != NETREG_REGISTERED) + return "(unregistered net_device)"; + return dev->name; +} +#define netdev_name(netdev) _kc_netdev_name(netdev) +#endif /* netdev_name */ + +#undef netdev_printk +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) +#define netdev_printk(level, netdev, format, args...) \ +do { \ + struct pci_dev *pdev = _kc_netdev_to_pdev(netdev); \ + printk(level "%s: " format, pci_name(pdev), ##args); \ +} while(0) +#elif ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21) ) +#define netdev_printk(level, netdev, format, args...) \ +do { \ + struct pci_dev *pdev = _kc_netdev_to_pdev(netdev); \ + struct device *dev = pci_dev_to_dev(pdev); \ + dev_printk(level, dev, "%s: " format, \ + netdev_name(netdev), ##args); \ +} while(0) +#else /* 2.6.21 => 2.6.34 */ +#define netdev_printk(level, netdev, format, args...) \ + dev_printk(level, (netdev)->dev.parent, \ + "%s: " format, \ + netdev_name(netdev), ##args) +#endif /* <2.6.0 <2.6.21 <2.6.34 */ +#undef netdev_emerg +#define netdev_emerg(dev, format, args...) \ + netdev_printk(KERN_EMERG, dev, format, ##args) +#undef netdev_alert +#define netdev_alert(dev, format, args...) \ + netdev_printk(KERN_ALERT, dev, format, ##args) +#undef netdev_crit +#define netdev_crit(dev, format, args...) \ + netdev_printk(KERN_CRIT, dev, format, ##args) +#undef netdev_err +#define netdev_err(dev, format, args...) \ + netdev_printk(KERN_ERR, dev, format, ##args) +#undef netdev_warn +#define netdev_warn(dev, format, args...) \ + netdev_printk(KERN_WARNING, dev, format, ##args) +#undef netdev_notice +#define netdev_notice(dev, format, args...) \ + netdev_printk(KERN_NOTICE, dev, format, ##args) +#undef netdev_info +#define netdev_info(dev, format, args...) \ + netdev_printk(KERN_INFO, dev, format, ##args) +#undef netdev_dbg +#if defined(DEBUG) +#define netdev_dbg(__dev, format, args...) \ + netdev_printk(KERN_DEBUG, __dev, format, ##args) +#elif defined(CONFIG_DYNAMIC_DEBUG) +#define netdev_dbg(__dev, format, args...) \ +do { \ + dynamic_dev_dbg((__dev)->dev.parent, "%s: " format, \ + netdev_name(__dev), ##args); \ +} while (0) +#else /* DEBUG */ +#define netdev_dbg(__dev, format, args...) \ +({ \ + if (0) \ + netdev_printk(KERN_DEBUG, __dev, format, ##args); \ + 0; \ +}) +#endif /* DEBUG */ + +#undef netif_printk +#define netif_printk(priv, type, level, dev, fmt, args...) \ +do { \ + if (netif_msg_##type(priv)) \ + netdev_printk(level, (dev), fmt, ##args); \ +} while (0) + +#undef netif_emerg +#define netif_emerg(priv, type, dev, fmt, args...) \ + netif_level(emerg, priv, type, dev, fmt, ##args) +#undef netif_alert +#define netif_alert(priv, type, dev, fmt, args...) \ + netif_level(alert, priv, type, dev, fmt, ##args) +#undef netif_crit +#define netif_crit(priv, type, dev, fmt, args...) \ + netif_level(crit, priv, type, dev, fmt, ##args) +#undef netif_err +#define netif_err(priv, type, dev, fmt, args...) \ + netif_level(err, priv, type, dev, fmt, ##args) +#undef netif_warn +#define netif_warn(priv, type, dev, fmt, args...) \ + netif_level(warn, priv, type, dev, fmt, ##args) +#undef netif_notice +#define netif_notice(priv, type, dev, fmt, args...) \ + netif_level(notice, priv, type, dev, fmt, ##args) +#undef netif_info +#define netif_info(priv, type, dev, fmt, args...) \ + netif_level(info, priv, type, dev, fmt, ##args) +#undef netif_dbg +#define netif_dbg(priv, type, dev, fmt, args...) \ + netif_level(dbg, priv, type, dev, fmt, ##args) + +#ifdef SET_SYSTEM_SLEEP_PM_OPS +#define HAVE_SYSTEM_SLEEP_PM_OPS +#endif + +#ifndef for_each_set_bit +#define for_each_set_bit(bit, addr, size) \ + for ((bit) = find_first_bit((addr), (size)); \ + (bit) < (size); \ + (bit) = find_next_bit((addr), (size), (bit) + 1)) +#endif /* for_each_set_bit */ + +#ifndef DEFINE_DMA_UNMAP_ADDR +#define DEFINE_DMA_UNMAP_ADDR DECLARE_PCI_UNMAP_ADDR +#define DEFINE_DMA_UNMAP_LEN DECLARE_PCI_UNMAP_LEN +#define dma_unmap_addr pci_unmap_addr +#define dma_unmap_addr_set pci_unmap_addr_set +#define dma_unmap_len pci_unmap_len +#define dma_unmap_len_set pci_unmap_len_set +#endif /* DEFINE_DMA_UNMAP_ADDR */ + +#if (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(6,3)) +#ifdef IGB_HWMON +#ifdef CONFIG_DEBUG_LOCK_ALLOC +#define sysfs_attr_init(attr) \ + do { \ + static struct lock_class_key __key; \ + (attr)->key = &__key; \ + } while (0) +#else +#define sysfs_attr_init(attr) do {} while (0) +#endif /* CONFIG_DEBUG_LOCK_ALLOC */ +#endif /* IGB_HWMON */ +#endif /* RHEL_RELEASE_CODE */ + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) +static inline bool _kc_pm_runtime_suspended() +{ + return false; +} +#define pm_runtime_suspended(dev) _kc_pm_runtime_suspended() +#else /* 2.6.0 => 2.6.34 */ +static inline bool _kc_pm_runtime_suspended(struct device __always_unused *dev) +{ + return false; +} +#ifndef pm_runtime_suspended +#define pm_runtime_suspended(dev) _kc_pm_runtime_suspended(dev) +#endif +#endif /* 2.6.0 => 2.6.34 */ + +#ifndef pci_bus_speed +/* override pci_bus_speed introduced in 2.6.19 with an expanded enum type */ +enum _kc_pci_bus_speed { + _KC_PCIE_SPEED_2_5GT = 0x14, + _KC_PCIE_SPEED_5_0GT = 0x15, + _KC_PCIE_SPEED_8_0GT = 0x16, + _KC_PCI_SPEED_UNKNOWN = 0xff, +}; +#define pci_bus_speed _kc_pci_bus_speed +#define PCIE_SPEED_2_5GT _KC_PCIE_SPEED_2_5GT +#define PCIE_SPEED_5_0GT _KC_PCIE_SPEED_5_0GT +#define PCIE_SPEED_8_0GT _KC_PCIE_SPEED_8_0GT +#define PCI_SPEED_UNKNOWN _KC_PCI_SPEED_UNKNOWN +#endif /* pci_bus_speed */ + +#else /* < 2.6.34 */ +#define HAVE_SYSTEM_SLEEP_PM_OPS +#ifndef HAVE_SET_RX_MODE +#define HAVE_SET_RX_MODE +#endif + +#endif /* < 2.6.34 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) ) +ssize_t _kc_simple_write_to_buffer(void *to, size_t available, loff_t *ppos, + const void __user *from, size_t count); +#define simple_write_to_buffer _kc_simple_write_to_buffer + +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,4))) +static inline struct pci_dev *pci_physfn(struct pci_dev *dev) +{ +#ifdef HAVE_PCI_DEV_IS_VIRTFN_BIT +#ifdef CONFIG_PCI_IOV + if (dev->is_virtfn) + dev = dev->physfn; +#endif /* CONFIG_PCI_IOV */ +#endif /* HAVE_PCI_DEV_IS_VIRTFN_BIT */ + return dev; +} +#endif /* ! RHEL >= 6.4 */ + +#ifndef PCI_EXP_LNKSTA_NLW_SHIFT +#define PCI_EXP_LNKSTA_NLW_SHIFT 4 +#endif + +#ifndef numa_node_id +#define numa_node_id() 0 +#endif +#ifndef numa_mem_id +#define numa_mem_id numa_node_id +#endif +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,0))) +#ifdef HAVE_TX_MQ +#include +#ifndef CONFIG_NETDEVICES_MULTIQUEUE +int _kc_netif_set_real_num_tx_queues(struct net_device *, unsigned int); +#else /* CONFIG_NETDEVICES_MULTI_QUEUE */ +static inline int _kc_netif_set_real_num_tx_queues(struct net_device *dev, + unsigned int txq) +{ + dev->egress_subqueue_count = txq; + return 0; +} +#endif /* CONFIG_NETDEVICES_MULTI_QUEUE */ +#else /* HAVE_TX_MQ */ +static inline int _kc_netif_set_real_num_tx_queues(struct net_device __always_unused *dev, + unsigned int __always_unused txq) +{ + return 0; +} +#endif /* HAVE_TX_MQ */ +#define netif_set_real_num_tx_queues(dev, txq) \ + _kc_netif_set_real_num_tx_queues(dev, txq) +#endif /* !(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,0)) */ +#ifndef ETH_FLAG_RXHASH +#define ETH_FLAG_RXHASH (1<<28) +#endif /* ETH_FLAG_RXHASH */ +#if (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,0)) +#define HAVE_IRQ_AFFINITY_HINT +#endif +struct device_node; +#else /* < 2.6.35 */ +#define HAVE_STRUCT_DEVICE_OF_NODE +#define HAVE_PM_QOS_REQUEST_LIST +#define HAVE_IRQ_AFFINITY_HINT +#include +#endif /* < 2.6.35 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ) +int _kc_ethtool_op_set_flags(struct net_device *, u32, u32); +#define ethtool_op_set_flags _kc_ethtool_op_set_flags +u32 _kc_ethtool_op_get_flags(struct net_device *); +#define ethtool_op_get_flags _kc_ethtool_op_get_flags + +enum { + WQ_UNBOUND = 0, + WQ_RESCUER = 0, +}; + +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +#ifdef NET_IP_ALIGN +#undef NET_IP_ALIGN +#endif +#define NET_IP_ALIGN 0 +#endif /* CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS */ + +#ifdef NET_SKB_PAD +#undef NET_SKB_PAD +#endif + +#if (L1_CACHE_BYTES > 32) +#define NET_SKB_PAD L1_CACHE_BYTES +#else +#define NET_SKB_PAD 32 +#endif + +static inline struct sk_buff *_kc_netdev_alloc_skb_ip_align(struct net_device *dev, + unsigned int length) +{ + struct sk_buff *skb; + + skb = alloc_skb(length + NET_SKB_PAD + NET_IP_ALIGN, GFP_ATOMIC); + if (skb) { +#if (NET_IP_ALIGN + NET_SKB_PAD) + skb_reserve(skb, NET_IP_ALIGN + NET_SKB_PAD); +#endif + skb->dev = dev; + } + return skb; +} + +#ifdef netdev_alloc_skb_ip_align +#undef netdev_alloc_skb_ip_align +#endif +#define netdev_alloc_skb_ip_align(n, l) _kc_netdev_alloc_skb_ip_align(n, l) + +#undef netif_level +#define netif_level(level, priv, type, dev, fmt, args...) \ +do { \ + if (netif_msg_##type(priv)) \ + netdev_##level(dev, fmt, ##args); \ +} while (0) + +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,3))) +#undef usleep_range +#define usleep_range(min, max) msleep(DIV_ROUND_UP(min, 1000)) +#endif + +#define u64_stats_update_begin(a) do { } while(0) +#define u64_stats_update_end(a) do { } while(0) +#define u64_stats_fetch_begin(a) do { } while(0) +#define u64_stats_fetch_retry_bh(a,b) (0) +#define u64_stats_fetch_begin_bh(a) (0) + +#if (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,1)) +#define HAVE_8021P_SUPPORT +#endif + +/* RHEL6.4 and SLES11sp2 backported skb_tx_timestamp */ +#if (!(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,4)) && \ + !(SLE_VERSION_CODE >= SLE_VERSION(11,2,0))) +static inline void skb_tx_timestamp(struct sk_buff __always_unused *skb) +{ + return; +} +#endif + +#else /* < 2.6.36 */ + +#define HAVE_PM_QOS_REQUEST_ACTIVE +#define HAVE_8021P_SUPPORT +#define HAVE_NDO_GET_STATS64 +#endif /* < 2.6.36 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ) +#define HAVE_NON_CONST_PCI_DRIVER_NAME +#ifndef netif_set_real_num_tx_queues +static inline int _kc_netif_set_real_num_tx_queues(struct net_device *dev, + unsigned int txq) +{ + netif_set_real_num_tx_queues(dev, txq); + return 0; +} +#define netif_set_real_num_tx_queues(dev, txq) \ + _kc_netif_set_real_num_tx_queues(dev, txq) +#endif +#ifndef netif_set_real_num_rx_queues +static inline int __kc_netif_set_real_num_rx_queues(struct net_device __always_unused *dev, + unsigned int __always_unused rxq) +{ + return 0; +} +#define netif_set_real_num_rx_queues(dev, rxq) \ + __kc_netif_set_real_num_rx_queues((dev), (rxq)) +#endif +#ifndef ETHTOOL_RXNTUPLE_ACTION_CLEAR +#define ETHTOOL_RXNTUPLE_ACTION_CLEAR (-2) +#endif +#ifndef VLAN_N_VID +#define VLAN_N_VID VLAN_GROUP_ARRAY_LEN +#endif /* VLAN_N_VID */ +#ifndef ETH_FLAG_TXVLAN +#define ETH_FLAG_TXVLAN BIT(7) +#endif /* ETH_FLAG_TXVLAN */ +#ifndef ETH_FLAG_RXVLAN +#define ETH_FLAG_RXVLAN BIT(8) +#endif /* ETH_FLAG_RXVLAN */ + +#define WQ_MEM_RECLAIM WQ_RESCUER + +static inline void _kc_skb_checksum_none_assert(struct sk_buff *skb) +{ + WARN_ON(skb->ip_summed != CHECKSUM_NONE); +} +#define skb_checksum_none_assert(skb) _kc_skb_checksum_none_assert(skb) + +static inline void *_kc_vzalloc_node(unsigned long size, int node) +{ + void *addr = vmalloc_node(size, node); + if (addr) + memset(addr, 0, size); + return addr; +} +#define vzalloc_node(_size, _node) _kc_vzalloc_node(_size, _node) + +static inline void *_kc_vzalloc(unsigned long size) +{ + void *addr = vmalloc(size); + if (addr) + memset(addr, 0, size); + return addr; +} +#define vzalloc(_size) _kc_vzalloc(_size) + +#if (!(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(5,7)) || \ + (RHEL_RELEASE_CODE == RHEL_RELEASE_VERSION(6,0))) +static inline __be16 vlan_get_protocol(const struct sk_buff *skb) +{ + if (vlan_tx_tag_present(skb) || + skb->protocol != cpu_to_be16(ETH_P_8021Q)) + return skb->protocol; + + if (skb_headlen(skb) < sizeof(struct vlan_ethhdr)) + return 0; + + return ((struct vlan_ethhdr*)skb->data)->h_vlan_encapsulated_proto; +} +#endif /* !RHEL5.7+ || RHEL6.0 */ + +#ifdef HAVE_HW_TIME_STAMP +#define SKBTX_HW_TSTAMP BIT(0) +#define SKBTX_IN_PROGRESS BIT(2) +#define SKB_SHARED_TX_IS_UNION +#endif + +#ifndef device_wakeup_enable +#define device_wakeup_enable(dev) device_set_wakeup_enable(dev, true) +#endif + +#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,4,18) ) +#ifndef HAVE_VLAN_RX_REGISTER +#define HAVE_VLAN_RX_REGISTER +#endif +#endif /* > 2.4.18 */ +#endif /* < 2.6.37 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) ) +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) ) +#define skb_checksum_start_offset(skb) skb_transport_offset(skb) +#else /* 2.6.22 -> 2.6.37 */ +static inline int _kc_skb_checksum_start_offset(const struct sk_buff *skb) +{ + return skb->csum_start - skb_headroom(skb); +} +#define skb_checksum_start_offset(skb) _kc_skb_checksum_start_offset(skb) +#endif /* 2.6.22 -> 2.6.37 */ +#if IS_ENABLED(CONFIG_DCB) +#ifndef IEEE_8021QAZ_MAX_TCS +#define IEEE_8021QAZ_MAX_TCS 8 +#endif +#ifndef DCB_CAP_DCBX_HOST +#define DCB_CAP_DCBX_HOST 0x01 +#endif +#ifndef DCB_CAP_DCBX_LLD_MANAGED +#define DCB_CAP_DCBX_LLD_MANAGED 0x02 +#endif +#ifndef DCB_CAP_DCBX_VER_CEE +#define DCB_CAP_DCBX_VER_CEE 0x04 +#endif +#ifndef DCB_CAP_DCBX_VER_IEEE +#define DCB_CAP_DCBX_VER_IEEE 0x08 +#endif +#ifndef DCB_CAP_DCBX_STATIC +#define DCB_CAP_DCBX_STATIC 0x10 +#endif +#endif /* CONFIG_DCB */ +#if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,2)) +#define CONFIG_XPS +#endif /* RHEL_RELEASE_VERSION(6,2) */ +#endif /* < 2.6.38 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) ) +#ifndef TC_BITMASK +#define TC_BITMASK 15 +#endif +#ifndef NETIF_F_RXCSUM +#define NETIF_F_RXCSUM BIT(29) +#endif +#ifndef skb_queue_reverse_walk_safe +#define skb_queue_reverse_walk_safe(queue, skb, tmp) \ + for (skb = (queue)->prev, tmp = skb->prev; \ + skb != (struct sk_buff *)(queue); \ + skb = tmp, tmp = skb->prev) +#endif +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +#ifndef FCOE_MTU +#define FCOE_MTU 2158 +#endif +#endif +#if IS_ENABLED(CONFIG_DCB) +#ifndef IEEE_8021QAZ_APP_SEL_ETHERTYPE +#define IEEE_8021QAZ_APP_SEL_ETHERTYPE 1 +#endif +#endif +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,4))) +#define kstrtoul(a, b, c) ((*(c)) = simple_strtoul((a), NULL, (b)), 0) +#define kstrtouint(a, b, c) ((*(c)) = simple_strtoul((a), NULL, (b)), 0) +#define kstrtou32(a, b, c) ((*(c)) = simple_strtoul((a), NULL, (b)), 0) +#endif /* !(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,4)) */ +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(6,0))) +u16 ___kc_skb_tx_hash(struct net_device *, const struct sk_buff *, u16); +#define __skb_tx_hash(n, s, q) ___kc_skb_tx_hash((n), (s), (q)) +u8 _kc_netdev_get_num_tc(struct net_device *dev); +#define netdev_get_num_tc(dev) _kc_netdev_get_num_tc(dev) +int _kc_netdev_set_num_tc(struct net_device *dev, u8 num_tc); +#define netdev_set_num_tc(dev, tc) _kc_netdev_set_num_tc((dev), (tc)) +#define netdev_reset_tc(dev) _kc_netdev_set_num_tc((dev), 0) +#define netdev_set_tc_queue(dev, tc, cnt, off) do {} while (0) +u8 _kc_netdev_get_prio_tc_map(struct net_device *dev, u8 up); +#define netdev_get_prio_tc_map(dev, up) _kc_netdev_get_prio_tc_map(dev, up) +#define netdev_set_prio_tc_map(dev, up, tc) do {} while (0) +#else /* RHEL6.1 or greater */ +#ifndef HAVE_MQPRIO +#define HAVE_MQPRIO +#endif /* HAVE_MQPRIO */ +#if IS_ENABLED(CONFIG_DCB) +#ifndef HAVE_DCBNL_IEEE +#define HAVE_DCBNL_IEEE +#ifndef IEEE_8021QAZ_TSA_STRICT +#define IEEE_8021QAZ_TSA_STRICT 0 +#endif +#ifndef IEEE_8021QAZ_TSA_ETS +#define IEEE_8021QAZ_TSA_ETS 2 +#endif +#ifndef IEEE_8021QAZ_APP_SEL_ETHERTYPE +#define IEEE_8021QAZ_APP_SEL_ETHERTYPE 1 +#endif +#endif +#endif /* CONFIG_DCB */ +#endif /* !(RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(6,0)) */ + +#ifndef udp_csum +#define udp_csum __kc_udp_csum +static inline __wsum __kc_udp_csum(struct sk_buff *skb) +{ + __wsum csum = csum_partial(skb_transport_header(skb), + sizeof(struct udphdr), skb->csum); + + for (skb = skb_shinfo(skb)->frag_list; skb; skb = skb->next) { + csum = csum_add(csum, skb->csum); + } + return csum; +} +#endif /* udp_csum */ +#else /* < 2.6.39 */ +#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +#ifndef HAVE_NETDEV_OPS_FCOE_DDP_TARGET +#define HAVE_NETDEV_OPS_FCOE_DDP_TARGET +#endif +#endif /* CONFIG_FCOE || CONFIG_FCOE_MODULE */ +#ifndef HAVE_MQPRIO +#define HAVE_MQPRIO +#endif +#ifndef HAVE_SETUP_TC +#define HAVE_SETUP_TC +#endif +#ifdef CONFIG_DCB +#ifndef HAVE_DCBNL_IEEE +#define HAVE_DCBNL_IEEE +#endif +#endif /* CONFIG_DCB */ +#ifndef HAVE_NDO_SET_FEATURES +#define HAVE_NDO_SET_FEATURES +#endif +#define HAVE_IRQ_AFFINITY_NOTIFY +#endif /* < 2.6.39 */ + +/*****************************************************************************/ +/* use < 2.6.40 because of a Fedora 15 kernel update where they + * updated the kernel version to 2.6.40.x and they back-ported 3.0 features + * like set_phys_id for ethtool. + */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,40) ) +#ifdef ETHTOOL_GRXRINGS +#ifndef FLOW_EXT +#define FLOW_EXT 0x80000000 +union _kc_ethtool_flow_union { + struct ethtool_tcpip4_spec tcp_ip4_spec; + struct ethtool_usrip4_spec usr_ip4_spec; + __u8 hdata[60]; +}; +struct _kc_ethtool_flow_ext { + __be16 vlan_etype; + __be16 vlan_tci; + __be32 data[2]; +}; +struct _kc_ethtool_rx_flow_spec { + __u32 flow_type; + union _kc_ethtool_flow_union h_u; + struct _kc_ethtool_flow_ext h_ext; + union _kc_ethtool_flow_union m_u; + struct _kc_ethtool_flow_ext m_ext; + __u64 ring_cookie; + __u32 location; +}; +#define ethtool_rx_flow_spec _kc_ethtool_rx_flow_spec +#endif /* FLOW_EXT */ +#endif + +#define pci_disable_link_state_locked pci_disable_link_state + +#ifndef PCI_LTR_VALUE_MASK +#define PCI_LTR_VALUE_MASK 0x000003ff +#endif +#ifndef PCI_LTR_SCALE_MASK +#define PCI_LTR_SCALE_MASK 0x00001c00 +#endif +#ifndef PCI_LTR_SCALE_SHIFT +#define PCI_LTR_SCALE_SHIFT 10 +#endif + +#else /* < 2.6.40 */ +#define HAVE_ETHTOOL_SET_PHYS_ID +#endif /* < 2.6.40 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) ) +#define USE_LEGACY_PM_SUPPORT +#ifndef kfree_rcu +#define kfree_rcu(_ptr, _rcu_head) do { \ + void __kc_kfree_rcu(struct rcu_head *rcu_head) \ + { \ + void *ptr = container_of(rcu_head, \ + typeof(*_ptr), \ + _rcu_head); \ + kfree(ptr); \ + } \ + call_rcu(&(_ptr)->_rcu_head, __kc_kfree_rcu); \ +} while (0) +#define HAVE_KFREE_RCU_BARRIER +#endif /* kfree_rcu */ +#ifndef kstrtol_from_user +#define kstrtol_from_user(s, c, b, r) _kc_kstrtol_from_user(s, c, b, r) +static inline int _kc_kstrtol_from_user(const char __user *s, size_t count, + unsigned int base, long *res) +{ + /* sign, base 2 representation, newline, terminator */ + char buf[1 + sizeof(long) * 8 + 1 + 1]; + + count = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, s, count)) + return -EFAULT; + buf[count] = '\0'; + return strict_strtol(buf, base, res); +} +#endif + +#if (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,0) || \ + RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,7))) +/* 20000base_blah_full Supported and Advertised Registers */ +#define SUPPORTED_20000baseMLD2_Full BIT(21) +#define SUPPORTED_20000baseKR2_Full BIT(22) +#define ADVERTISED_20000baseMLD2_Full BIT(21) +#define ADVERTISED_20000baseKR2_Full BIT(22) +#endif /* RHEL_RELEASE_CODE */ +#endif /* < 3.0.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0) ) +#ifndef __netdev_alloc_skb_ip_align +#define __netdev_alloc_skb_ip_align(d,l,_g) netdev_alloc_skb_ip_align(d,l) +#endif /* __netdev_alloc_skb_ip_align */ +#define dcb_ieee_setapp(dev, app) dcb_setapp(dev, app) +#define dcb_ieee_delapp(dev, app) 0 +#define dcb_ieee_getapp_mask(dev, app) (1 << app->priority) + +/* 1000BASE-T Control register */ +#define CTL1000_AS_MASTER 0x0800 +#define CTL1000_ENABLE_MASTER 0x1000 + +/* kernels less than 3.0.0 don't have this */ +#ifndef ETH_P_8021AD +#define ETH_P_8021AD 0x88A8 +#endif + +/* Stub definition for !CONFIG_OF is introduced later */ +#ifdef CONFIG_OF +static inline struct device_node * +pci_device_to_OF_node(struct pci_dev __maybe_unused *pdev) +{ +#ifdef HAVE_STRUCT_DEVICE_OF_NODE + return pdev ? pdev->dev.of_node : NULL; +#else + return NULL; +#endif /* !HAVE_STRUCT_DEVICE_OF_NODE */ +} +#endif /* CONFIG_OF */ +#else /* < 3.1.0 */ +#ifndef HAVE_DCBNL_IEEE_DELAPP +#define HAVE_DCBNL_IEEE_DELAPP +#endif +#endif /* < 3.1.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) ) +#ifndef dma_zalloc_coherent +#define dma_zalloc_coherent(d, s, h, f) _kc_dma_zalloc_coherent(d, s, h, f) +static inline void *_kc_dma_zalloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + void *ret = dma_alloc_coherent(dev, size, dma_handle, flag); + if (ret) + memset(ret, 0, size); + return ret; +} +#endif +#ifdef ETHTOOL_GRXRINGS +#define HAVE_ETHTOOL_GET_RXNFC_VOID_RULE_LOCS +#endif /* ETHTOOL_GRXRINGS */ + +#ifndef skb_frag_size +#define skb_frag_size(frag) _kc_skb_frag_size(frag) +static inline unsigned int _kc_skb_frag_size(const skb_frag_t *frag) +{ + return frag->size; +} +#endif /* skb_frag_size */ + +#ifndef skb_frag_size_sub +#define skb_frag_size_sub(frag, delta) _kc_skb_frag_size_sub(frag, delta) +static inline void _kc_skb_frag_size_sub(skb_frag_t *frag, int delta) +{ + frag->size -= delta; +} +#endif /* skb_frag_size_sub */ + +#ifndef skb_frag_page +#define skb_frag_page(frag) _kc_skb_frag_page(frag) +static inline struct page *_kc_skb_frag_page(const skb_frag_t *frag) +{ + return frag->page; +} +#endif /* skb_frag_page */ + +#ifndef skb_frag_address +#define skb_frag_address(frag) _kc_skb_frag_address(frag) +static inline void *_kc_skb_frag_address(const skb_frag_t *frag) +{ + return page_address(skb_frag_page(frag)) + frag->page_offset; +} +#endif /* skb_frag_address */ + +#ifndef skb_frag_dma_map +#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) +#include +#endif +#define skb_frag_dma_map(dev,frag,offset,size,dir) \ + _kc_skb_frag_dma_map(dev,frag,offset,size,dir) +static inline dma_addr_t _kc_skb_frag_dma_map(struct device *dev, + const skb_frag_t *frag, + size_t offset, size_t size, + enum dma_data_direction dir) +{ + return dma_map_page(dev, skb_frag_page(frag), + frag->page_offset + offset, size, dir); +} +#endif /* skb_frag_dma_map */ + +#ifndef __skb_frag_unref +#define __skb_frag_unref(frag) __kc_skb_frag_unref(frag) +static inline void __kc_skb_frag_unref(skb_frag_t *frag) +{ + put_page(skb_frag_page(frag)); +} +#endif /* __skb_frag_unref */ + +#ifndef SPEED_UNKNOWN +#define SPEED_UNKNOWN -1 +#endif +#ifndef DUPLEX_UNKNOWN +#define DUPLEX_UNKNOWN 0xff +#endif +#if ((RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,3)) ||\ + (SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(11,3,0))) +#ifndef HAVE_PCI_DEV_FLAGS_ASSIGNED +#define HAVE_PCI_DEV_FLAGS_ASSIGNED +#endif +#endif +#else /* < 3.2.0 */ +#ifndef HAVE_PCI_DEV_FLAGS_ASSIGNED +#define HAVE_PCI_DEV_FLAGS_ASSIGNED +#define HAVE_VF_SPOOFCHK_CONFIGURE +#endif +#ifndef HAVE_SKB_L4_RXHASH +#define HAVE_SKB_L4_RXHASH +#endif +#define HAVE_IOMMU_PRESENT +#define HAVE_PM_QOS_REQUEST_LIST_NEW +#endif /* < 3.2.0 */ + +#if (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE == RHEL_RELEASE_VERSION(6,2)) +#undef ixgbe_get_netdev_tc_txq +#define ixgbe_get_netdev_tc_txq(dev, tc) (&netdev_extended(dev)->qos_data.tc_to_txq[tc]) +#endif +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) ) +/* NOTE: the order of parameters to _kc_alloc_workqueue() is different than + * alloc_workqueue() to avoid compiler warning from -Wvarargs + */ +static inline struct workqueue_struct * __attribute__ ((format(printf, 3, 4))) +_kc_alloc_workqueue(__maybe_unused int flags, __maybe_unused int max_active, + const char *fmt, ...) +{ + struct workqueue_struct *wq; + va_list args, temp; + unsigned int len; + char *p; + + va_start(args, fmt); + va_copy(temp, args); + len = vsnprintf(NULL, 0, fmt, temp); + va_end(temp); + + p = kmalloc(len + 1, GFP_KERNEL); + if (!p) { + va_end(args); + return NULL; + } + + vsnprintf(p, len + 1, fmt, args); + va_end(args); +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ) + wq = create_workqueue(p); +#else + wq = alloc_workqueue(p, flags, max_active); +#endif + kfree(p); + + return wq; +} +#ifdef alloc_workqueue +#undef alloc_workqueue +#endif +#define alloc_workqueue(fmt, flags, max_active, args...) \ + _kc_alloc_workqueue(flags, max_active, fmt, ##args) + +#if !(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,5)) +typedef u32 netdev_features_t; +#endif +#undef PCI_EXP_TYPE_RC_EC +#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ +#ifndef CONFIG_BQL +#define netdev_tx_completed_queue(_q, _p, _b) do {} while (0) +#define netdev_completed_queue(_n, _p, _b) do {} while (0) +#define netdev_tx_sent_queue(_q, _b) do {} while (0) +#define netdev_sent_queue(_n, _b) do {} while (0) +#define netdev_tx_reset_queue(_q) do {} while (0) +#define netdev_reset_queue(_n) do {} while (0) +#endif +#if (SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(11,3,0)) +#define HAVE_ETHTOOL_GRXFHINDIR_SIZE +#endif /* SLE_VERSION(11,3,0) */ +#define netif_xmit_stopped(_q) netif_tx_queue_stopped(_q) +#if !(SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(11,4,0)) +static inline int __kc_ipv6_skip_exthdr(const struct sk_buff *skb, int start, + u8 *nexthdrp, + __be16 __always_unused *frag_offp) +{ + return ipv6_skip_exthdr(skb, start, nexthdrp); +} +#undef ipv6_skip_exthdr +#define ipv6_skip_exthdr(a,b,c,d) __kc_ipv6_skip_exthdr((a), (b), (c), (d)) +#endif /* !SLES11sp4 or greater */ + +#if (!(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,4)) && \ + !(SLE_VERSION_CODE >= SLE_VERSION(11,3,0))) +static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings) +{ + return index % n_rx_rings; +} +#endif + +#else /* ! < 3.3.0 */ +#define HAVE_ETHTOOL_GRXFHINDIR_SIZE +#define HAVE_INT_NDO_VLAN_RX_ADD_VID +#ifdef ETHTOOL_SRXNTUPLE +#undef ETHTOOL_SRXNTUPLE +#endif +#endif /* < 3.3.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0) ) +#ifndef NETIF_F_RXFCS +#define NETIF_F_RXFCS 0 +#endif /* NETIF_F_RXFCS */ +#ifndef NETIF_F_RXALL +#define NETIF_F_RXALL 0 +#endif /* NETIF_F_RXALL */ + +#if !(SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(11,3,0)) +#define NUMTCS_RETURNS_U8 + +int _kc_simple_open(struct inode *inode, struct file *file); +#define simple_open _kc_simple_open +#endif /* !(SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(11,3,0)) */ + +#ifndef skb_add_rx_frag +#define skb_add_rx_frag _kc_skb_add_rx_frag +void _kc_skb_add_rx_frag(struct sk_buff * skb, int i, struct page *page, + int off, int size, unsigned int truesize); +#endif +#ifdef NET_ADDR_RANDOM +#define eth_hw_addr_random(N) do { \ + eth_random_addr(N->dev_addr); \ + N->addr_assign_type |= NET_ADDR_RANDOM; \ + } while (0) +#else /* NET_ADDR_RANDOM */ +#define eth_hw_addr_random(N) eth_random_addr(N->dev_addr) +#endif /* NET_ADDR_RANDOM */ + +#ifndef for_each_set_bit_from +#define for_each_set_bit_from(bit, addr, size) \ + for ((bit) = find_next_bit((addr), (size), (bit)); \ + (bit) < (size); \ + (bit) = find_next_bit((addr), (size), (bit) + 1)) +#endif /* for_each_set_bit_from */ + +#else /* < 3.4.0 */ +#include +#endif /* >= 3.4.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) ) || \ + ( RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,4) ) +#if !defined(NO_PTP_SUPPORT) && IS_ENABLED(CONFIG_PTP_1588_CLOCK) +#define HAVE_PTP_1588_CLOCK +#endif /* !NO_PTP_SUPPORT && IS_ENABLED(CONFIG_PTP_1588_CLOCK) */ +#endif /* >= 3.0.0 || RHEL_RELEASE > 6.4 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0) ) + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#ifndef BITS_PER_LONG_LONG +#define BITS_PER_LONG_LONG 64 +#endif + +#ifndef ether_addr_equal +static inline bool __kc_ether_addr_equal(const u8 *addr1, const u8 *addr2) +{ + return !compare_ether_addr(addr1, addr2); +} +#define ether_addr_equal(_addr1, _addr2) __kc_ether_addr_equal((_addr1),(_addr2)) +#endif + +/* Definitions for !CONFIG_OF_NET are introduced in 3.10 */ +#ifdef CONFIG_OF_NET +static inline int of_get_phy_mode(struct device_node __always_unused *np) +{ + return -ENODEV; +} + +static inline const void * +of_get_mac_address(struct device_node __always_unused *np) +{ + return NULL; +} +#endif +#else +#include +#define HAVE_FDB_OPS +#define HAVE_ETHTOOL_GET_TS_INFO +#endif /* < 3.5.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0) ) +#define PCI_EXP_LNKCAP2 44 /* Link Capability 2 */ + +#ifndef MDIO_EEE_100TX +#define MDIO_EEE_100TX 0x0002 /* 100TX EEE cap */ +#endif +#ifndef MDIO_EEE_1000T +#define MDIO_EEE_1000T 0x0004 /* 1000T EEE cap */ +#endif +#ifndef MDIO_EEE_10GT +#define MDIO_EEE_10GT 0x0008 /* 10GT EEE cap */ +#endif +#ifndef MDIO_EEE_1000KX +#define MDIO_EEE_1000KX 0x0010 /* 1000KX EEE cap */ +#endif +#ifndef MDIO_EEE_10GKX4 +#define MDIO_EEE_10GKX4 0x0020 /* 10G KX4 EEE cap */ +#endif +#ifndef MDIO_EEE_10GKR +#define MDIO_EEE_10GKR 0x0040 /* 10G KR EEE cap */ +#endif + +#ifndef __GFP_MEMALLOC +#define __GFP_MEMALLOC 0 +#endif + +#ifndef eth_broadcast_addr +#define eth_broadcast_addr _kc_eth_broadcast_addr +static inline void _kc_eth_broadcast_addr(u8 *addr) +{ + memset(addr, 0xff, ETH_ALEN); +} +#endif + +#ifndef eth_random_addr +#define eth_random_addr _kc_eth_random_addr +static inline void _kc_eth_random_addr(u8 *addr) +{ + get_random_bytes(addr, ETH_ALEN); + addr[0] &= 0xfe; /* clear multicast */ + addr[0] |= 0x02; /* set local assignment */ +} +#endif /* eth_random_addr */ + +#ifndef DMA_ATTR_SKIP_CPU_SYNC +#define DMA_ATTR_SKIP_CPU_SYNC 0 +#endif +#else /* < 3.6.0 */ +#define HAVE_STRUCT_PAGE_PFMEMALLOC +#endif /* < 3.6.0 */ + +/******************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) ) +#include +#ifndef ADVERTISED_40000baseKR4_Full +/* these defines were all added in one commit, so should be safe + * to trigger activiation on one define + */ +#define SUPPORTED_40000baseKR4_Full BIT(23) +#define SUPPORTED_40000baseCR4_Full BIT(24) +#define SUPPORTED_40000baseSR4_Full BIT(25) +#define SUPPORTED_40000baseLR4_Full BIT(26) +#define ADVERTISED_40000baseKR4_Full BIT(23) +#define ADVERTISED_40000baseCR4_Full BIT(24) +#define ADVERTISED_40000baseSR4_Full BIT(25) +#define ADVERTISED_40000baseLR4_Full BIT(26) +#endif + +#ifndef mmd_eee_cap_to_ethtool_sup_t +/** + * mmd_eee_cap_to_ethtool_sup_t + * @eee_cap: value of the MMD EEE Capability register + * + * A small helper function that translates MMD EEE Capability (3.20) bits + * to ethtool supported settings. + */ +static inline u32 __kc_mmd_eee_cap_to_ethtool_sup_t(u16 eee_cap) +{ + u32 supported = 0; + + if (eee_cap & MDIO_EEE_100TX) + supported |= SUPPORTED_100baseT_Full; + if (eee_cap & MDIO_EEE_1000T) + supported |= SUPPORTED_1000baseT_Full; + if (eee_cap & MDIO_EEE_10GT) + supported |= SUPPORTED_10000baseT_Full; + if (eee_cap & MDIO_EEE_1000KX) + supported |= SUPPORTED_1000baseKX_Full; + if (eee_cap & MDIO_EEE_10GKX4) + supported |= SUPPORTED_10000baseKX4_Full; + if (eee_cap & MDIO_EEE_10GKR) + supported |= SUPPORTED_10000baseKR_Full; + + return supported; +} +#define mmd_eee_cap_to_ethtool_sup_t(eee_cap) \ + __kc_mmd_eee_cap_to_ethtool_sup_t(eee_cap) +#endif /* mmd_eee_cap_to_ethtool_sup_t */ + +#ifndef mmd_eee_adv_to_ethtool_adv_t +/** + * mmd_eee_adv_to_ethtool_adv_t + * @eee_adv: value of the MMD EEE Advertisement/Link Partner Ability registers + * + * A small helper function that translates the MMD EEE Advertisement (7.60) + * and MMD EEE Link Partner Ability (7.61) bits to ethtool advertisement + * settings. + */ +static inline u32 __kc_mmd_eee_adv_to_ethtool_adv_t(u16 eee_adv) +{ + u32 adv = 0; + + if (eee_adv & MDIO_EEE_100TX) + adv |= ADVERTISED_100baseT_Full; + if (eee_adv & MDIO_EEE_1000T) + adv |= ADVERTISED_1000baseT_Full; + if (eee_adv & MDIO_EEE_10GT) + adv |= ADVERTISED_10000baseT_Full; + if (eee_adv & MDIO_EEE_1000KX) + adv |= ADVERTISED_1000baseKX_Full; + if (eee_adv & MDIO_EEE_10GKX4) + adv |= ADVERTISED_10000baseKX4_Full; + if (eee_adv & MDIO_EEE_10GKR) + adv |= ADVERTISED_10000baseKR_Full; + + return adv; +} + +#define mmd_eee_adv_to_ethtool_adv_t(eee_adv) \ + __kc_mmd_eee_adv_to_ethtool_adv_t(eee_adv) +#endif /* mmd_eee_adv_to_ethtool_adv_t */ + +#ifndef ethtool_adv_to_mmd_eee_adv_t +/** + * ethtool_adv_to_mmd_eee_adv_t + * @adv: the ethtool advertisement settings + * + * A small helper function that translates ethtool advertisement settings + * to EEE advertisements for the MMD EEE Advertisement (7.60) and + * MMD EEE Link Partner Ability (7.61) registers. + */ +static inline u16 __kc_ethtool_adv_to_mmd_eee_adv_t(u32 adv) +{ + u16 reg = 0; + + if (adv & ADVERTISED_100baseT_Full) + reg |= MDIO_EEE_100TX; + if (adv & ADVERTISED_1000baseT_Full) + reg |= MDIO_EEE_1000T; + if (adv & ADVERTISED_10000baseT_Full) + reg |= MDIO_EEE_10GT; + if (adv & ADVERTISED_1000baseKX_Full) + reg |= MDIO_EEE_1000KX; + if (adv & ADVERTISED_10000baseKX4_Full) + reg |= MDIO_EEE_10GKX4; + if (adv & ADVERTISED_10000baseKR_Full) + reg |= MDIO_EEE_10GKR; + + return reg; +} +#define ethtool_adv_to_mmd_eee_adv_t(adv) __kc_ethtool_adv_to_mmd_eee_adv_t(adv) +#endif /* ethtool_adv_to_mmd_eee_adv_t */ + +#ifndef pci_pcie_type +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) ) +static inline u8 pci_pcie_type(struct pci_dev *pdev) +{ + int pos; + u16 reg16; + + pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); + BUG_ON(!pos); + pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); + return (reg16 & PCI_EXP_FLAGS_TYPE) >> 4; +} +#else /* < 2.6.24 */ +#define pci_pcie_type(x) (x)->pcie_type +#endif /* < 2.6.24 */ +#endif /* pci_pcie_type */ + +#if ( ! ( RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,4) ) ) && \ + ( ! ( SLE_VERSION_CODE >= SLE_VERSION(11,3,0) ) ) && \ + ( LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) ) +#define ptp_clock_register(caps, args...) ptp_clock_register(caps) +#endif + +#ifndef pcie_capability_read_word +int __kc_pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val); +#define pcie_capability_read_word(d,p,v) __kc_pcie_capability_read_word(d,p,v) +#endif /* pcie_capability_read_word */ + +#ifndef pcie_capability_read_dword +int __kc_pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val); +#define pcie_capability_read_dword(d,p,v) __kc_pcie_capability_read_dword(d,p,v) +#endif + +#ifndef pcie_capability_write_word +int __kc_pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val); +#define pcie_capability_write_word(d,p,v) __kc_pcie_capability_write_word(d,p,v) +#endif /* pcie_capability_write_word */ + +#ifndef pcie_capability_clear_and_set_word +int __kc_pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos, + u16 clear, u16 set); +#define pcie_capability_clear_and_set_word(d,p,c,s) \ + __kc_pcie_capability_clear_and_set_word(d,p,c,s) +#endif /* pcie_capability_clear_and_set_word */ + +#ifndef pcie_capability_clear_word +int __kc_pcie_capability_clear_word(struct pci_dev *dev, int pos, + u16 clear); +#define pcie_capability_clear_word(d, p, c) \ + __kc_pcie_capability_clear_word(d, p, c) +#endif /* pcie_capability_clear_word */ + +#ifndef PCI_EXP_LNKSTA2 +#define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ +#endif + +#if (SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(11,3,0)) +#define USE_CONST_DEV_UC_CHAR +#define HAVE_NDO_FDB_ADD_NLATTR +#endif + +#if !(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,8)) +#define napi_gro_flush(_napi, _flush_old) napi_gro_flush(_napi) +#endif /* !RHEL6.8+ */ + +#if (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,6)) +#include +#else + +#define DEFINE_HASHTABLE(name, bits) \ + struct hlist_head name[1 << (bits)] = \ + { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT } + +#define DEFINE_READ_MOSTLY_HASHTABLE(name, bits) \ + struct hlist_head name[1 << (bits)] __read_mostly = \ + { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT } + +#define DECLARE_HASHTABLE(name, bits) \ + struct hlist_head name[1 << (bits)] + +#define HASH_SIZE(name) (ARRAY_SIZE(name)) +#define HASH_BITS(name) ilog2(HASH_SIZE(name)) + +/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */ +#define hash_min(val, bits) \ + (sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits)) + +static inline void __hash_init(struct hlist_head *ht, unsigned int sz) +{ + unsigned int i; + + for (i = 0; i < sz; i++) + INIT_HLIST_HEAD(&ht[i]); +} + +#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable)) + +#define hash_add(hashtable, node, key) \ + hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]) + +static inline bool hash_hashed(struct hlist_node *node) +{ + return !hlist_unhashed(node); +} + +static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz) +{ + unsigned int i; + + for (i = 0; i < sz; i++) + if (!hlist_empty(&ht[i])) + return false; + + return true; +} + +#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable)) + +static inline void hash_del(struct hlist_node *node) +{ + hlist_del_init(node); +} +#endif /* RHEL >= 6.6 */ + +/* We don't have @flags support prior to 3.7, so we'll simply ignore the flags + * parameter on these older kernels. + */ +#define __setup_timer(_timer, _fn, _data, _flags) \ + setup_timer((_timer), (_fn), (_data)) \ + +#if ( ! ( RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,7) ) ) && \ + ( ! ( SLE_VERSION_CODE >= SLE_VERSION(12,0,0) ) ) + +#ifndef mod_delayed_work +/** + * __mod_delayed_work - modify delay or queue delayed work + * @wq: workqueue to use + * @dwork: delayed work to queue + * @delay: number of jiffies to wait before queueing + * + * Return: %true if @dwork was pending and was rescheduled; + * %false if it wasn't pending + * + * Note: the dwork parameter was declared as a void* + * to avoid comptibility problems with early 2.6 kernels + * where struct delayed_work is not declared. Unlike the original + * implementation flags are not preserved and it shouldn't be + * used in the interrupt context. + */ +static inline bool __mod_delayed_work(struct workqueue_struct *wq, + void *dwork, + unsigned long delay) +{ + bool ret = cancel_delayed_work(dwork); + queue_delayed_work(wq, dwork, delay); + return ret; +} +#define mod_delayed_work(wq, dwork, delay) __mod_delayed_work(wq, dwork, delay) +#endif /* mod_delayed_work */ + +#endif /* !(RHEL >= 6.7) && !(SLE >= 12.0) */ +#else /* >= 3.7.0 */ +#include +#define HAVE_CONST_STRUCT_PCI_ERROR_HANDLERS +#define USE_CONST_DEV_UC_CHAR +#define HAVE_NDO_FDB_ADD_NLATTR +#endif /* >= 3.7.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0) ) +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,5)) && \ + !(SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(11,4,0))) +#ifndef pci_sriov_set_totalvfs +static inline int __kc_pci_sriov_set_totalvfs(struct pci_dev __always_unused *dev, u16 __always_unused numvfs) +{ + return 0; +} +#define pci_sriov_set_totalvfs(a, b) __kc_pci_sriov_set_totalvfs((a), (b)) +#endif +#endif /* !(RHEL_RELEASE_CODE >= 6.5 && SLE_VERSION_CODE >= 11.4) */ +#ifndef PCI_EXP_LNKCTL_ASPM_L0S +#define PCI_EXP_LNKCTL_ASPM_L0S 0x01 /* L0s Enable */ +#endif +#ifndef PCI_EXP_LNKCTL_ASPM_L1 +#define PCI_EXP_LNKCTL_ASPM_L1 0x02 /* L1 Enable */ +#endif +#define HAVE_CONFIG_HOTPLUG +/* Reserved Ethernet Addresses per IEEE 802.1Q */ +static const u8 eth_reserved_addr_base[ETH_ALEN] __aligned(2) = { + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; + +#ifndef is_link_local_ether_addr +static inline bool __kc_is_link_local_ether_addr(const u8 *addr) +{ + __be16 *a = (__be16 *)addr; + static const __be16 *b = (const __be16 *)eth_reserved_addr_base; + static const __be16 m = cpu_to_be16(0xfff0); + + return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | ((a[2] ^ b[2]) & m)) == 0; +} +#define is_link_local_ether_addr(addr) __kc_is_link_local_ether_addr(addr) +#endif /* is_link_local_ether_addr */ + +#ifndef FLOW_MAC_EXT +#define FLOW_MAC_EXT 0x40000000 +#endif /* FLOW_MAC_EXT */ + +#if (SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(11,4,0)) +#define HAVE_SRIOV_CONFIGURE +#endif + +#ifndef PCI_EXP_LNKCAP_SLS_2_5GB +#define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */ +#endif + +#ifndef PCI_EXP_LNKCAP_SLS_5_0GB +#define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ +#endif + +#undef PCI_EXP_LNKCAP2_SLS_2_5GB +#define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */ + +#undef PCI_EXP_LNKCAP2_SLS_5_0GB +#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5GT/s */ + +#undef PCI_EXP_LNKCAP2_SLS_8_0GB +#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8GT/s */ + +#else /* >= 3.8.0 */ +#ifndef __devinit +#define __devinit +#endif + +#ifndef __devinitdata +#define __devinitdata +#endif + +#ifndef __devinitconst +#define __devinitconst +#endif + +#ifndef __devexit +#define __devexit +#endif + +#ifndef __devexit_p +#define __devexit_p +#endif + +#ifndef HAVE_ENCAP_CSUM_OFFLOAD +#define HAVE_ENCAP_CSUM_OFFLOAD +#endif + +#ifndef HAVE_GRE_ENCAP_OFFLOAD +#define HAVE_GRE_ENCAP_OFFLOAD +#endif + +#ifndef HAVE_SRIOV_CONFIGURE +#define HAVE_SRIOV_CONFIGURE +#endif + +#define HAVE_BRIDGE_ATTRIBS +#ifndef BRIDGE_MODE_VEB +#define BRIDGE_MODE_VEB 0 /* Default loopback mode */ +#endif /* BRIDGE_MODE_VEB */ +#ifndef BRIDGE_MODE_VEPA +#define BRIDGE_MODE_VEPA 1 /* 802.1Qbg defined VEPA mode */ +#endif /* BRIDGE_MODE_VEPA */ +#endif /* >= 3.8.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) ) + +#undef BUILD_BUG_ON +#ifdef __CHECKER__ +#define BUILD_BUG_ON(condition) (0) +#else /* __CHECKER__ */ +#ifndef __compiletime_warning +#if defined(__GNUC__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40400) +#define __compiletime_warning(message) __attribute__((warning(message))) +#else /* __GNUC__ */ +#define __compiletime_warning(message) +#endif /* __GNUC__ */ +#endif /* __compiletime_warning */ +#ifndef __compiletime_error +#if defined(__GNUC__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40400) +#define __compiletime_error(message) __attribute__((error(message))) +#define __compiletime_error_fallback(condition) do { } while (0) +#else /* __GNUC__ */ +#define __compiletime_error(message) +#define __compiletime_error_fallback(condition) \ + do { ((void)sizeof(char[1 - 2 * condition])); } while (0) +#endif /* __GNUC__ */ +#else /* __compiletime_error */ +#define __compiletime_error_fallback(condition) do { } while (0) +#endif /* __compiletime_error */ +#define __compiletime_assert(condition, msg, prefix, suffix) \ + do { \ + bool __cond = !(condition); \ + extern void prefix ## suffix(void) __compiletime_error(msg); \ + if (__cond) \ + prefix ## suffix(); \ + __compiletime_error_fallback(__cond); \ + } while (0) + +#define _compiletime_assert(condition, msg, prefix, suffix) \ + __compiletime_assert(condition, msg, prefix, suffix) +#define compiletime_assert(condition, msg) \ + _compiletime_assert(condition, msg, __compiletime_assert_, __LINE__) +#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg) +#ifndef __OPTIMIZE__ +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) +#else /* __OPTIMIZE__ */ +#define BUILD_BUG_ON(condition) \ + BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition) +#endif /* __OPTIMIZE__ */ +#endif /* __CHECKER__ */ + +#undef hlist_entry +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#undef hlist_entry_safe +#define hlist_entry_safe(ptr, type, member) \ + ({ typeof(ptr) ____ptr = (ptr); \ + ____ptr ? hlist_entry(____ptr, type, member) : NULL; \ + }) + +#undef hlist_for_each_entry +#define hlist_for_each_entry(pos, head, member) \ + for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member); \ + pos; \ + pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) + +#undef hlist_for_each_entry_safe +#define hlist_for_each_entry_safe(pos, n, head, member) \ + for (pos = hlist_entry_safe((head)->first, typeof(*pos), member); \ + pos && ({ n = pos->member.next; 1; }); \ + pos = hlist_entry_safe(n, typeof(*pos), member)) + +#undef hlist_for_each_entry_continue +#define hlist_for_each_entry_continue(pos, member) \ + for (pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member);\ + pos; \ + pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) + +#undef hlist_for_each_entry_from +#define hlist_for_each_entry_from(pos, member) \ + for (; pos; \ + pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) + +#undef hash_for_each +#define hash_for_each(name, bkt, obj, member) \ + for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry(obj, &name[bkt], member) + +#undef hash_for_each_safe +#define hash_for_each_safe(name, bkt, tmp, obj, member) \ + for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry_safe(obj, tmp, &name[bkt], member) + +#undef hash_for_each_possible +#define hash_for_each_possible(name, obj, member, key) \ + hlist_for_each_entry(obj, &name[hash_min(key, HASH_BITS(name))], member) + +#undef hash_for_each_possible_safe +#define hash_for_each_possible_safe(name, obj, tmp, member, key) \ + hlist_for_each_entry_safe(obj, tmp,\ + &name[hash_min(key, HASH_BITS(name))], member) + +#ifdef CONFIG_XPS +int __kc_netif_set_xps_queue(struct net_device *, const struct cpumask *, u16); +#define netif_set_xps_queue(_dev, _mask, _idx) __kc_netif_set_xps_queue((_dev), (_mask), (_idx)) +#else /* CONFIG_XPS */ +#define netif_set_xps_queue(_dev, _mask, _idx) do {} while (0) +#endif /* CONFIG_XPS */ + +#ifdef HAVE_NETDEV_SELECT_QUEUE +#define _kc_hashrnd 0xd631614b /* not so random hash salt */ +u16 __kc_netdev_pick_tx(struct net_device *dev, struct sk_buff *skb); +#define __netdev_pick_tx __kc_netdev_pick_tx +#endif /* HAVE_NETDEV_SELECT_QUEUE */ +#else +#define HAVE_BRIDGE_FILTER +#define HAVE_FDB_DEL_NLATTR +#endif /* < 3.9.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ) +#ifndef NAPI_POLL_WEIGHT +#define NAPI_POLL_WEIGHT 64 +#endif +#ifdef CONFIG_PCI_IOV +int __kc_pci_vfs_assigned(struct pci_dev *dev); +#else +static inline int __kc_pci_vfs_assigned(struct pci_dev __always_unused *dev) +{ + return 0; +} +#endif +#define pci_vfs_assigned(dev) __kc_pci_vfs_assigned(dev) + +#ifndef list_first_entry_or_null +#define list_first_entry_or_null(ptr, type, member) \ + (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL) +#endif + +#ifndef VLAN_TX_COOKIE_MAGIC +static inline struct sk_buff *__kc__vlan_hwaccel_put_tag(struct sk_buff *skb, + u16 vlan_tci) +{ +#ifdef VLAN_TAG_PRESENT + vlan_tci |= VLAN_TAG_PRESENT; +#endif + skb->vlan_tci = vlan_tci; + return skb; +} +#define __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci) \ + __kc__vlan_hwaccel_put_tag(skb, vlan_tci) +#endif + +#ifdef HAVE_FDB_OPS +#if defined(HAVE_NDO_FDB_ADD_NLATTR) +int __kc_ndo_dflt_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, u16 flags); +#elif defined(USE_CONST_DEV_UC_CHAR) +int __kc_ndo_dflt_fdb_add(struct ndmsg *ndm, struct net_device *dev, + const unsigned char *addr, u16 flags); +#else +int __kc_ndo_dflt_fdb_add(struct ndmsg *ndm, struct net_device *dev, + unsigned char *addr, u16 flags); +#endif /* HAVE_NDO_FDB_ADD_NLATTR */ +#if defined(HAVE_FDB_DEL_NLATTR) +int __kc_ndo_dflt_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr); +#elif defined(USE_CONST_DEV_UC_CHAR) +int __kc_ndo_dflt_fdb_del(struct ndmsg *ndm, struct net_device *dev, + const unsigned char *addr); +#else +int __kc_ndo_dflt_fdb_del(struct ndmsg *ndm, struct net_device *dev, + unsigned char *addr); +#endif /* HAVE_FDB_DEL_NLATTR */ +#define ndo_dflt_fdb_add __kc_ndo_dflt_fdb_add +#define ndo_dflt_fdb_del __kc_ndo_dflt_fdb_del +#endif /* HAVE_FDB_OPS */ + +#ifndef PCI_DEVID +#define PCI_DEVID(bus, devfn) ((((u16)(bus)) << 8) | (devfn)) +#endif + +/* The definitions for these functions when CONFIG_OF_NET is defined are + * pulled in from . For kernels older than 3.5 we already have + * backports for when CONFIG_OF_NET is true. These are separated and + * duplicated in order to cover all cases so that all kernels get either the + * real definitions (when CONFIG_OF_NET is defined) or the stub definitions + * (when CONFIG_OF_NET is not defined, or the kernel is too old to have real + * definitions). + */ +#ifndef CONFIG_OF_NET +static inline int of_get_phy_mode(struct device_node __always_unused *np) +{ + return -ENODEV; +} + +static inline const void * +of_get_mac_address(struct device_node __always_unused *np) +{ + return NULL; +} +#endif + +#else /* >= 3.10.0 */ +#define HAVE_ENCAP_TSO_OFFLOAD +#define USE_DEFAULT_FDB_DEL_DUMP +#define HAVE_SKB_INNER_NETWORK_HEADER + +#if (RHEL_RELEASE_CODE && \ + (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,0)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8,0))) +#define HAVE_RHEL7_PCI_DRIVER_RH +#if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,2)) +#define HAVE_RHEL7_PCI_RESET_NOTIFY +#endif /* RHEL >= 7.2 */ +#if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,3)) +#if (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,5)) +#define HAVE_GENEVE_RX_OFFLOAD +#endif /* RHEL >=7.3 && RHEL < 7.5 */ +#define HAVE_ETHTOOL_FLOW_UNION_IP6_SPEC +#define HAVE_RHEL7_NET_DEVICE_OPS_EXT +#if !defined(HAVE_UDP_ENC_TUNNEL) && IS_ENABLED(CONFIG_GENEVE) +#define HAVE_UDP_ENC_TUNNEL +#endif +#endif /* RHEL >= 7.3 */ + +/* new hooks added to net_device_ops_extended in RHEL7.4 */ +#if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,4)) +#define HAVE_RHEL7_NETDEV_OPS_EXT_NDO_SET_VF_VLAN +#define HAVE_RHEL7_NETDEV_OPS_EXT_NDO_UDP_TUNNEL +#define HAVE_UDP_ENC_RX_OFFLOAD +#endif /* RHEL >= 7.4 */ +#endif /* RHEL >= 7.0 && RHEL < 8.0 */ + +#if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,0)) +#define HAVE_TCF_BLOCK_CB_REGISTER_EXTACK +#define NO_NETDEV_BPF_PROG_ATTACHED +#endif /* RHEL >= 8.0 */ +#endif /* >= 3.10.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) ) +#define netdev_notifier_info_to_dev(ptr) ptr +#ifndef time_in_range64 +#define time_in_range64(a, b, c) \ + (time_after_eq64(a, b) && \ + time_before_eq64(a, c)) +#endif /* time_in_range64 */ +#if ((RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,6)) ||\ + (SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(11,4,0))) +#define HAVE_NDO_SET_VF_LINK_STATE +#endif +#if RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2)) +#define HAVE_NDO_SELECT_QUEUE_ACCEL_FALLBACK +#endif +#else /* >= 3.11.0 */ +#define HAVE_NDO_SET_VF_LINK_STATE +#define HAVE_SKB_INNER_PROTOCOL +#define HAVE_MPLS_FEATURES +#endif /* >= 3.11.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0) ) +int __kc_pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, + enum pcie_link_width *width); +#ifndef pcie_get_minimum_link +#define pcie_get_minimum_link(_p, _s, _w) __kc_pcie_get_minimum_link(_p, _s, _w) +#endif + +#if (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(6,7)) +int _kc_pci_wait_for_pending_transaction(struct pci_dev *dev); +#define pci_wait_for_pending_transaction _kc_pci_wait_for_pending_transaction +#endif /* = 3.12.0 */ +#if ( SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(12,0,0)) +#define HAVE_NDO_SELECT_QUEUE_ACCEL_FALLBACK +#endif +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0) ) +#define HAVE_VXLAN_RX_OFFLOAD +#if !defined(HAVE_UDP_ENC_TUNNEL) && IS_ENABLED(CONFIG_VXLAN) +#define HAVE_UDP_ENC_TUNNEL +#endif +#endif /* < 4.8.0 */ +#define HAVE_NDO_GET_PHYS_PORT_ID +#define HAVE_NETIF_SET_XPS_QUEUE_CONST_MASK +#endif /* >= 3.12.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0) ) +#define dma_set_mask_and_coherent(_p, _m) __kc_dma_set_mask_and_coherent(_p, _m) +int __kc_dma_set_mask_and_coherent(struct device *dev, u64 mask); +#ifndef u64_stats_init +#define u64_stats_init(a) do { } while(0) +#endif +#undef BIT_ULL +#define BIT_ULL(n) (1ULL << (n)) + +#if (!(SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(12,0,0)) && \ + !(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,0))) +static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev) +{ + dev = pci_physfn(dev); + if (pci_is_root_bus(dev->bus)) + return NULL; + + return dev->bus->self; +} +#endif + +#if (SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(12,1,0)) +#undef HAVE_STRUCT_PAGE_PFMEMALLOC +#define HAVE_DCBNL_OPS_SETAPP_RETURN_INT +#endif +#ifndef list_next_entry +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) +#endif +#ifndef list_prev_entry +#define list_prev_entry(pos, member) \ + list_entry((pos)->member.prev, typeof(*(pos)), member) +#endif + +#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,6,20) ) +#define devm_kcalloc(dev, cnt, size, flags) \ + devm_kzalloc(dev, cnt * size, flags) +#endif /* > 2.6.20 */ + +#if (!(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,2))) +#define list_last_entry(ptr, type, member) list_entry((ptr)->prev, type, member) +#endif + +#else /* >= 3.13.0 */ +#define HAVE_VXLAN_CHECKS +#if (UBUNTU_VERSION_CODE && UBUNTU_VERSION_CODE >= UBUNTU_VERSION(3,13,0,24)) +#define HAVE_NDO_SELECT_QUEUE_ACCEL_FALLBACK +#else +#define HAVE_NDO_SELECT_QUEUE_ACCEL +#endif +#define HAVE_HWMON_DEVICE_REGISTER_WITH_GROUPS +#endif + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) ) + +#ifndef U16_MAX +#define U16_MAX ((u16)~0U) +#endif + +#ifndef U32_MAX +#define U32_MAX ((u32)~0U) +#endif + +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,2))) +#define dev_consume_skb_any(x) dev_kfree_skb_any(x) +#define dev_consume_skb_irq(x) dev_kfree_skb_irq(x) +#endif + +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,0)) && \ + !(SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(12,0,0))) + +/* it isn't expected that this would be a #define unless we made it so */ +#ifndef skb_set_hash + +#define PKT_HASH_TYPE_NONE 0 +#define PKT_HASH_TYPE_L2 1 +#define PKT_HASH_TYPE_L3 2 +#define PKT_HASH_TYPE_L4 3 + +enum _kc_pkt_hash_types { + _KC_PKT_HASH_TYPE_NONE = PKT_HASH_TYPE_NONE, + _KC_PKT_HASH_TYPE_L2 = PKT_HASH_TYPE_L2, + _KC_PKT_HASH_TYPE_L3 = PKT_HASH_TYPE_L3, + _KC_PKT_HASH_TYPE_L4 = PKT_HASH_TYPE_L4, +}; +#define pkt_hash_types _kc_pkt_hash_types + +#define skb_set_hash __kc_skb_set_hash +static inline void __kc_skb_set_hash(struct sk_buff __maybe_unused *skb, + u32 __maybe_unused hash, + int __maybe_unused type) +{ +#ifdef HAVE_SKB_L4_RXHASH + skb->l4_rxhash = (type == PKT_HASH_TYPE_L4); +#endif +#ifdef NETIF_F_RXHASH + skb->rxhash = hash; +#endif +} +#endif /* !skb_set_hash */ + +#else /* RHEL_RELEASE_CODE >= 7.0 || SLE_VERSION_CODE >= 12.0 */ + +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,5))) +#ifndef HAVE_VXLAN_RX_OFFLOAD +#define HAVE_VXLAN_RX_OFFLOAD +#endif /* HAVE_VXLAN_RX_OFFLOAD */ +#endif + +#if !defined(HAVE_UDP_ENC_TUNNEL) && IS_ENABLED(CONFIG_VXLAN) +#define HAVE_UDP_ENC_TUNNEL +#endif + +#ifndef HAVE_VXLAN_CHECKS +#define HAVE_VXLAN_CHECKS +#endif /* HAVE_VXLAN_CHECKS */ +#endif /* !(RHEL_RELEASE_CODE >= 7.0 && SLE_VERSION_CODE >= 12.0) */ + +#if ((RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,3)) ||\ + (SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(12,0,0))) +#define HAVE_NDO_DFWD_OPS +#endif + +#ifndef pci_enable_msix_range +int __kc_pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, + int minvec, int maxvec); +#define pci_enable_msix_range __kc_pci_enable_msix_range +#endif + +#ifndef ether_addr_copy +#define ether_addr_copy __kc_ether_addr_copy +static inline void __kc_ether_addr_copy(u8 *dst, const u8 *src) +{ +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) + *(u32 *)dst = *(const u32 *)src; + *(u16 *)(dst + 4) = *(const u16 *)(src + 4); +#else + u16 *a = (u16 *)dst; + const u16 *b = (const u16 *)src; + + a[0] = b[0]; + a[1] = b[1]; + a[2] = b[2]; +#endif +} +#endif /* ether_addr_copy */ +int __kc_ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, + int target, unsigned short *fragoff, int *flags); +#define ipv6_find_hdr(a, b, c, d, e) __kc_ipv6_find_hdr((a), (b), (c), (d), (e)) + +#ifndef OPTIMIZE_HIDE_VAR +#ifdef __GNUC__ +#define OPTIMIZER_HIDE_VAR(var) __asm__ ("" : "=r" (var) : "0" (var)) +#else +#include +#define OPTIMIZE_HIDE_VAR(var) barrier() +#endif +#endif + +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,0)) && \ + !(SLE_VERSION_CODE && SLE_VERSION_CODE >= SLE_VERSION(10,4,0))) +static inline __u32 skb_get_hash_raw(const struct sk_buff *skb) +{ +#ifdef NETIF_F_RXHASH + return skb->rxhash; +#else + return 0; +#endif /* NETIF_F_RXHASH */ +} +#endif /* !RHEL > 5.9 && !SLES >= 10.4 */ + +#if (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,5)) +#define request_firmware_direct request_firmware +#endif /* !RHEL || RHEL < 7.5 */ + +#else /* >= 3.14.0 */ + +/* for ndo_dfwd_ ops add_station, del_station and _start_xmit */ +#ifndef HAVE_NDO_DFWD_OPS +#define HAVE_NDO_DFWD_OPS +#endif +#define HAVE_NDO_SELECT_QUEUE_ACCEL_FALLBACK +#endif /* 3.14.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0) ) +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,1)) && \ + !(UBUNTU_VERSION_CODE && UBUNTU_VERSION_CODE >= UBUNTU_VERSION(3,13,0,30))) +#define u64_stats_fetch_begin_irq u64_stats_fetch_begin_bh +#define u64_stats_fetch_retry_irq u64_stats_fetch_retry_bh +#endif + +char *_kc_devm_kstrdup(struct device *dev, const char *s, gfp_t gfp); +#define devm_kstrdup(dev, s, gfp) _kc_devm_kstrdup(dev, s, gfp) + +#else +#define HAVE_NET_GET_RANDOM_ONCE +#define HAVE_PTP_1588_CLOCK_PINS +#define HAVE_NETDEV_PORT +#endif /* 3.15.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) ) +#ifndef smp_mb__before_atomic +#define smp_mb__before_atomic() smp_mb() +#define smp_mb__after_atomic() smp_mb() +#endif +#ifndef __dev_uc_sync +#ifdef HAVE_SET_RX_MODE +#ifdef NETDEV_HW_ADDR_T_UNICAST +int __kc_hw_addr_sync_dev(struct netdev_hw_addr_list *list, + struct net_device *dev, + int (*sync)(struct net_device *, const unsigned char *), + int (*unsync)(struct net_device *, const unsigned char *)); +void __kc_hw_addr_unsync_dev(struct netdev_hw_addr_list *list, + struct net_device *dev, + int (*unsync)(struct net_device *, const unsigned char *)); +#endif +#ifndef NETDEV_HW_ADDR_T_MULTICAST +int __kc_dev_addr_sync_dev(struct dev_addr_list **list, int *count, + struct net_device *dev, + int (*sync)(struct net_device *, const unsigned char *), + int (*unsync)(struct net_device *, const unsigned char *)); +void __kc_dev_addr_unsync_dev(struct dev_addr_list **list, int *count, + struct net_device *dev, + int (*unsync)(struct net_device *, const unsigned char *)); +#endif +#endif /* HAVE_SET_RX_MODE */ + +static inline int __kc_dev_uc_sync(struct net_device __maybe_unused *dev, + int __maybe_unused (*sync)(struct net_device *, const unsigned char *), + int __maybe_unused (*unsync)(struct net_device *, const unsigned char *)) +{ +#ifdef NETDEV_HW_ADDR_T_UNICAST + return __kc_hw_addr_sync_dev(&dev->uc, dev, sync, unsync); +#elif defined(HAVE_SET_RX_MODE) + return __kc_dev_addr_sync_dev(&dev->uc_list, &dev->uc_count, + dev, sync, unsync); +#else + return 0; +#endif +} +#define __dev_uc_sync __kc_dev_uc_sync + +static inline void __kc_dev_uc_unsync(struct net_device __maybe_unused *dev, + int __maybe_unused (*unsync)(struct net_device *, const unsigned char *)) +{ +#ifdef HAVE_SET_RX_MODE +#ifdef NETDEV_HW_ADDR_T_UNICAST + __kc_hw_addr_unsync_dev(&dev->uc, dev, unsync); +#else /* NETDEV_HW_ADDR_T_MULTICAST */ + __kc_dev_addr_unsync_dev(&dev->uc_list, &dev->uc_count, dev, unsync); +#endif /* NETDEV_HW_ADDR_T_UNICAST */ +#endif /* HAVE_SET_RX_MODE */ +} +#define __dev_uc_unsync __kc_dev_uc_unsync + +static inline int __kc_dev_mc_sync(struct net_device __maybe_unused *dev, + int __maybe_unused (*sync)(struct net_device *, const unsigned char *), + int __maybe_unused (*unsync)(struct net_device *, const unsigned char *)) +{ +#ifdef NETDEV_HW_ADDR_T_MULTICAST + return __kc_hw_addr_sync_dev(&dev->mc, dev, sync, unsync); +#elif defined(HAVE_SET_RX_MODE) + return __kc_dev_addr_sync_dev(&dev->mc_list, &dev->mc_count, + dev, sync, unsync); +#else + return 0; +#endif + +} +#define __dev_mc_sync __kc_dev_mc_sync + +static inline void __kc_dev_mc_unsync(struct net_device __maybe_unused *dev, + int __maybe_unused (*unsync)(struct net_device *, const unsigned char *)) +{ +#ifdef HAVE_SET_RX_MODE +#ifdef NETDEV_HW_ADDR_T_MULTICAST + __kc_hw_addr_unsync_dev(&dev->mc, dev, unsync); +#else /* NETDEV_HW_ADDR_T_MULTICAST */ + __kc_dev_addr_unsync_dev(&dev->mc_list, &dev->mc_count, dev, unsync); +#endif /* NETDEV_HW_ADDR_T_MULTICAST */ +#endif /* HAVE_SET_RX_MODE */ +} +#define __dev_mc_unsync __kc_dev_mc_unsync +#endif /* __dev_uc_sync */ + +#if RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,1)) +#define HAVE_NDO_SET_VF_MIN_MAX_TX_RATE +#endif + +#ifndef NETIF_F_GSO_UDP_TUNNEL_CSUM +/* if someone backports this, hopefully they backport as a #define. + * declare it as zero on older kernels so that if it get's or'd in + * it won't effect anything, therefore preventing core driver changes + */ +#define NETIF_F_GSO_UDP_TUNNEL_CSUM 0 +#define SKB_GSO_UDP_TUNNEL_CSUM 0 +#endif +void *__kc_devm_kmemdup(struct device *dev, const void *src, size_t len, + gfp_t gfp); +#define devm_kmemdup __kc_devm_kmemdup + +#else +#if ( ( LINUX_VERSION_CODE < KERNEL_VERSION(4,13,0) ) && \ + ! ( SLE_VERSION_CODE && ( SLE_VERSION_CODE >= SLE_VERSION(12,4,0)) ) ) +#define HAVE_PCI_ERROR_HANDLER_RESET_NOTIFY +#endif /* >= 3.16.0 && < 4.13.0 && !(SLES >= 12sp4) */ +#define HAVE_NDO_SET_VF_MIN_MAX_TX_RATE +#endif /* 3.16.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0) ) +#if !(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,8) && \ + RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)) && \ + !(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,2)) +#ifndef timespec64 +#define timespec64 timespec +static inline struct timespec64 timespec_to_timespec64(const struct timespec ts) +{ + return ts; +} +static inline struct timespec timespec64_to_timespec(const struct timespec64 ts64) +{ + return ts64; +} +#define timespec64_equal timespec_equal +#define timespec64_compare timespec_compare +#define set_normalized_timespec64 set_normalized_timespec +#define timespec64_add_safe timespec_add_safe +#define timespec64_add timespec_add +#define timespec64_sub timespec_sub +#define timespec64_valid timespec_valid +#define timespec64_valid_strict timespec_valid_strict +#define timespec64_to_ns timespec_to_ns +#define ns_to_timespec64 ns_to_timespec +#define ktime_to_timespec64 ktime_to_timespec +#define ktime_get_ts64 ktime_get_ts +#define ktime_get_real_ts64 ktime_get_real_ts +#define timespec64_add_ns timespec_add_ns +#endif /* timespec64 */ +#endif /* !(RHEL6.8= RHEL_RELEASE_VERSION(6,8) && \ + RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)) +static inline void ktime_get_real_ts64(struct timespec64 *ts) +{ + *ts = ktime_to_timespec64(ktime_get_real()); +} + +static inline void ktime_get_ts64(struct timespec64 *ts) +{ + *ts = ktime_to_timespec64(ktime_get()); +} +#endif + +#if !(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,4)) +#define hlist_add_behind(_a, _b) hlist_add_after(_b, _a) +#endif + +#if (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,5)) +#endif /* RHEL_RELEASE_CODE < RHEL7.5 */ + +#if (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,3)) +static inline u64 ktime_get_ns(void) +{ + return ktime_to_ns(ktime_get()); +} + +static inline u64 ktime_get_real_ns(void) +{ + return ktime_to_ns(ktime_get_real()); +} + +static inline u64 ktime_get_boot_ns(void) +{ + return ktime_to_ns(ktime_get_boottime()); +} +#endif /* RHEL < 7.3 */ + +#else +#define HAVE_DCBNL_OPS_SETAPP_RETURN_INT +#include +#endif /* 3.17.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) ) +#ifndef NO_PTP_SUPPORT +#include +struct sk_buff *__kc_skb_clone_sk(struct sk_buff *skb); +void __kc_skb_complete_tx_timestamp(struct sk_buff *skb, + struct skb_shared_hwtstamps *hwtstamps); +#define skb_clone_sk __kc_skb_clone_sk +#define skb_complete_tx_timestamp __kc_skb_complete_tx_timestamp +#endif +#if (!(RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,2)))) +u32 __kc_eth_get_headlen(const struct net_device *dev, unsigned char *data, + unsigned int max_len); +#else +unsigned int __kc_eth_get_headlen(unsigned char *data, unsigned int max_len); +#endif /* !RHEL >= 8.2 */ + +#define eth_get_headlen __kc_eth_get_headlen +#ifndef ETH_P_XDSA +#define ETH_P_XDSA 0x00F8 +#endif +/* RHEL 7.1 backported csum_level, but SLES 12 and 12-SP1 did not */ +#if RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,1)) +#define HAVE_SKBUFF_CSUM_LEVEL +#endif /* >= RH 7.1 */ + +/* RHEL 7.3 backported xmit_more */ +#if (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,3)) +#define HAVE_SKB_XMIT_MORE +#endif /* >= RH 7.3 */ + +#undef GENMASK +#define GENMASK(h, l) \ + (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) +#undef GENMASK_ULL +#define GENMASK_ULL(h, l) \ + (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) + +#else /* 3.18.0 */ +#define HAVE_SKBUFF_CSUM_LEVEL +#define HAVE_SKB_XMIT_MORE +#define HAVE_SKB_INNER_PROTOCOL_TYPE +#endif /* 3.18.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,18,4) ) +#else +#define HAVE_NDO_FEATURES_CHECK +#endif /* 3.18.4 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,18,13) ) +#ifndef WRITE_ONCE +#define WRITE_ONCE(x, val) ({ ACCESS_ONCE(x) = (val); }) +#endif +#endif /* 3.18.13 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) ) +/* netdev_phys_port_id renamed to netdev_phys_item_id */ +#define netdev_phys_item_id netdev_phys_port_id + +static inline void _kc_napi_complete_done(struct napi_struct *napi, + int __always_unused work_done) { + napi_complete(napi); +} +/* don't use our backport if the distro kernels already have it */ +#if (SLE_VERSION_CODE && (SLE_VERSION_CODE < SLE_VERSION(12,3,0))) || \ + (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,5))) +#define napi_complete_done _kc_napi_complete_done +#endif + +int _kc_bitmap_print_to_pagebuf(bool list, char *buf, + const unsigned long *maskp, int nmaskbits); +#define bitmap_print_to_pagebuf _kc_bitmap_print_to_pagebuf + +#ifndef NETDEV_RSS_KEY_LEN +#define NETDEV_RSS_KEY_LEN (13 * 4) +#endif +#if (!(RHEL_RELEASE_CODE && \ + ((RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,7) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)) || \ + (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,2))))) +#define netdev_rss_key_fill(buffer, len) __kc_netdev_rss_key_fill(buffer, len) +#endif /* RHEL_RELEASE_CODE */ +void __kc_netdev_rss_key_fill(void *buffer, size_t len); +#define SPEED_20000 20000 +#define SPEED_40000 40000 +#ifndef dma_rmb +#define dma_rmb() rmb() +#endif +#ifndef dev_alloc_pages +#ifndef NUMA_NO_NODE +#define NUMA_NO_NODE -1 +#endif +#define dev_alloc_pages(_order) alloc_pages_node(NUMA_NO_NODE, (GFP_ATOMIC | __GFP_COLD | __GFP_COMP | __GFP_MEMALLOC), (_order)) +#endif +#ifndef dev_alloc_page +#define dev_alloc_page() dev_alloc_pages(0) +#endif +#if !defined(eth_skb_pad) && !defined(skb_put_padto) +/** + * __kc_skb_put_padto - increase size and pad an skbuff up to a minimal size + * @skb: buffer to pad + * @len: minimal length + * + * Pads up a buffer to ensure the trailing bytes exist and are + * blanked. If the buffer already contains sufficient data it + * is untouched. Otherwise it is extended. Returns zero on + * success. The skb is freed on error. + */ +static inline int __kc_skb_put_padto(struct sk_buff *skb, unsigned int len) +{ + unsigned int size = skb->len; + + if (unlikely(size < len)) { + len -= size; + if (skb_pad(skb, len)) + return -ENOMEM; + __skb_put(skb, len); + } + return 0; +} +#define skb_put_padto(skb, len) __kc_skb_put_padto(skb, len) + +static inline int __kc_eth_skb_pad(struct sk_buff *skb) +{ + return __kc_skb_put_padto(skb, ETH_ZLEN); +} +#define eth_skb_pad(skb) __kc_eth_skb_pad(skb) +#endif /* eth_skb_pad && skb_put_padto */ + +#ifndef SKB_ALLOC_NAPI +/* RHEL 7.2 backported napi_alloc_skb and friends */ +static inline struct sk_buff *__kc_napi_alloc_skb(struct napi_struct *napi, unsigned int length) +{ + return netdev_alloc_skb_ip_align(napi->dev, length); +} +#define napi_alloc_skb(napi,len) __kc_napi_alloc_skb(napi,len) +#define __napi_alloc_skb(napi,len,mask) __kc_napi_alloc_skb(napi,len) +#endif /* SKB_ALLOC_NAPI */ +#define HAVE_CONFIG_PM_RUNTIME +#if (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(6,7)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0))) +#define HAVE_RXFH_HASHFUNC +#endif /* 6.7 < RHEL < 7.0 */ +#if RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,1)) +#define HAVE_RXFH_HASHFUNC +#define NDO_DFLT_BRIDGE_GETLINK_HAS_BRFLAGS +#endif /* RHEL > 7.1 */ +#ifndef napi_schedule_irqoff +#define napi_schedule_irqoff napi_schedule +#endif +#ifndef READ_ONCE +#define READ_ONCE(_x) ACCESS_ONCE(_x) +#endif +#if RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2)) +#define HAVE_NDO_FDB_ADD_VID +#endif +#ifndef ETH_MODULE_SFF_8636 +#define ETH_MODULE_SFF_8636 0x3 +#endif +#ifndef ETH_MODULE_SFF_8636_LEN +#define ETH_MODULE_SFF_8636_LEN 256 +#endif +#ifndef ETH_MODULE_SFF_8436 +#define ETH_MODULE_SFF_8436 0x4 +#endif +#ifndef ETH_MODULE_SFF_8436_LEN +#define ETH_MODULE_SFF_8436_LEN 256 +#endif +#ifndef writel_relaxed +#define writel_relaxed writel +#endif +#else /* 3.19.0 */ +#define HAVE_NDO_FDB_ADD_VID +#define HAVE_RXFH_HASHFUNC +#define NDO_DFLT_BRIDGE_GETLINK_HAS_BRFLAGS +#endif /* 3.19.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(3,20,0) ) +/* vlan_tx_xx functions got renamed to skb_vlan */ +#ifndef skb_vlan_tag_get +#define skb_vlan_tag_get vlan_tx_tag_get +#endif +#ifndef skb_vlan_tag_present +#define skb_vlan_tag_present vlan_tx_tag_present +#endif +#if RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,1)) +#define HAVE_INCLUDE_LINUX_TIMECOUNTER_H +#endif +#if RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2)) +#define HAVE_NDO_BRIDGE_SET_DEL_LINK_FLAGS +#endif +#else +#define HAVE_INCLUDE_LINUX_TIMECOUNTER_H +#define HAVE_NDO_BRIDGE_SET_DEL_LINK_FLAGS +#endif /* 3.20.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) ) +/* Definition for CONFIG_OF was introduced earlier */ +#if !defined(CONFIG_OF) && \ + !(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2)) +static inline struct device_node * +pci_device_to_OF_node(const struct pci_dev __always_unused *pdev) { return NULL; } +#else /* !CONFIG_OF && RHEL < 7.3 */ +#define HAVE_DDP_PROFILE_UPLOAD_SUPPORT +#endif /* !CONFIG_OF && RHEL < 7.3 */ +#else /* < 4.0 */ +#define HAVE_DDP_PROFILE_UPLOAD_SUPPORT +#endif /* < 4.0 */ + +/*****************************************************************************/ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0) ) +#ifndef NO_PTP_SUPPORT +#ifdef HAVE_INCLUDE_LINUX_TIMECOUNTER_H +#include +#else +#include +#endif +static inline void __kc_timecounter_adjtime(struct timecounter *tc, s64 delta) +{ + tc->nsec += delta; +} + +static inline struct net_device * +of_find_net_device_by_node(struct device_node __always_unused *np) +{ + return NULL; +} + +#define timecounter_adjtime __kc_timecounter_adjtime +#endif +#if ((RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,2))) || \ + (SLE_VERSION_CODE && (SLE_VERSION_CODE >= SLE_VERSION(12,2,0)))) +#define HAVE_NDO_SET_VF_RSS_QUERY_EN +#endif +#if RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2)) +#define HAVE_NDO_BRIDGE_GETLINK_NLFLAGS +#endif +#if !((RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(6,8) && RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)) && \ + (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2)) && \ + (SLE_VERSION_CODE > SLE_VERSION(12,1,0))) +unsigned int _kc_cpumask_local_spread(unsigned int i, int node); +#define cpumask_local_spread _kc_cpumask_local_spread +#endif +#else /* >= 4,1,0 */ +#define HAVE_PTP_CLOCK_INFO_GETTIME64 +#define HAVE_NDO_BRIDGE_GETLINK_NLFLAGS +#define HAVE_PASSTHRU_FEATURES_CHECK +#define HAVE_NDO_SET_VF_RSS_QUERY_EN +#define HAVE_NDO_SET_TX_MAXRATE +#endif /* 4,1,0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,1,9)) +#if (!(RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2)) && \ + !((SLE_VERSION_CODE == SLE_VERSION(11,3,0)) && \ + (SLE_LOCALVERSION_CODE >= SLE_LOCALVERSION(0,47,71))) && \ + !((SLE_VERSION_CODE == SLE_VERSION(11,4,0)) && \ + (SLE_LOCALVERSION_CODE >= SLE_LOCALVERSION(65,0,0))) && \ + !(SLE_VERSION_CODE >= SLE_VERSION(12,1,0))) +static inline bool page_is_pfmemalloc(struct page __maybe_unused *page) +{ +#ifdef HAVE_STRUCT_PAGE_PFMEMALLOC + return page->pfmemalloc; +#else + return false; +#endif +} +#endif /* !RHEL7.2+ && !SLES11sp3(3.0.101-0.47.71+ update) && !SLES11sp4(3.0.101-65+ update) & !SLES12sp1+ */ +#else +#undef HAVE_STRUCT_PAGE_PFMEMALLOC +#endif /* 4.1.9 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0)) +#if (!(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,2)) && \ + !(SLE_VERSION_CODE >= SLE_VERSION(12,1,0))) +#define ETHTOOL_RX_FLOW_SPEC_RING 0x00000000FFFFFFFFULL +#define ETHTOOL_RX_FLOW_SPEC_RING_VF 0x000000FF00000000ULL +#define ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF 32 +static inline __u64 ethtool_get_flow_spec_ring(__u64 ring_cookie) +{ + return ETHTOOL_RX_FLOW_SPEC_RING & ring_cookie; +}; + +static inline __u64 ethtool_get_flow_spec_ring_vf(__u64 ring_cookie) +{ + return (ETHTOOL_RX_FLOW_SPEC_RING_VF & ring_cookie) >> + ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF; +}; +#endif /* ! RHEL >= 7.2 && ! SLES >= 12.1 */ +#if (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,4)) +#define HAVE_NDO_DFLT_BRIDGE_GETLINK_VLAN_SUPPORT +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)) +#if (!((RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(6,8) && \ + RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0)) || \ + RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,2))) +static inline bool pci_ari_enabled(struct pci_bus *bus) +{ + return bus->self && bus->self->ari_enabled; +} +#endif /* !(RHEL6.8+ || RHEL7.2+) */ +#else +static inline bool pci_ari_enabled(struct pci_bus *bus) +{ + return false; +} +#endif /* 2.6.27 */ +#else +#define HAVE_NDO_DFLT_BRIDGE_GETLINK_VLAN_SUPPORT +#define HAVE_VF_STATS +#endif /* 4.2.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,4,0)) +#if (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,3)) +#define HAVE_NDO_SET_VF_TRUST +#endif /* (RHEL_RELEASE >= 7.3) */ +#ifndef CONFIG_64BIT +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) +#include /* 32-bit readq/writeq */ +#else /* 3.3.0 => 4.3.x */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)) +#include +#endif /* 2.6.26 => 3.3.0 */ +#ifndef readq +static inline __u64 readq(const volatile void __iomem *addr) +{ + const volatile u32 __iomem *p = addr; + u32 low, high; + + low = readl(p); + high = readl(p + 1); + + return low + ((u64)high << 32); +} +#define readq readq +#endif + +#ifndef writeq +static inline void writeq(__u64 val, volatile void __iomem *addr) +{ + writel(val, addr); + writel(val >> 32, addr + 4); +} +#define writeq writeq +#endif +#endif /* < 3.3.0 */ +#endif /* !CONFIG_64BIT */ +#else /* < 4.4.0 */ +#define HAVE_NDO_SET_VF_TRUST + +#ifndef CONFIG_64BIT +#include /* 32-bit readq/writeq */ +#endif /* !CONFIG_64BIT */ +#endif /* 4.4.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)) +/* protect against a likely backport */ +#ifndef NETIF_F_CSUM_MASK +#define NETIF_F_CSUM_MASK NETIF_F_ALL_CSUM +#endif /* NETIF_F_CSUM_MASK */ +#ifndef NETIF_F_SCTP_CRC +#define NETIF_F_SCTP_CRC NETIF_F_SCTP_CSUM +#endif /* NETIF_F_SCTP_CRC */ +#if (!(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,3))) +#define eth_platform_get_mac_address _kc_eth_platform_get_mac_address +int _kc_eth_platform_get_mac_address(struct device *dev __maybe_unused, + u8 *mac_addr __maybe_unused); +#endif /* !(RHEL_RELEASE >= 7.3) */ +#else /* 4.5.0 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0) ) +#define HAVE_GENEVE_RX_OFFLOAD +#if !defined(HAVE_UDP_ENC_TUNNEL) && IS_ENABLED(CONFIG_GENEVE) +#define HAVE_UDP_ENC_TUNNEL +#endif +#endif /* < 4.8.0 */ +#define HAVE_NETIF_NAPI_ADD_CALLS_NAPI_HASH_ADD +#endif /* 4.5.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)) +#if !(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,3)) +static inline unsigned char *skb_checksum_start(const struct sk_buff *skb) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)) + return skb->head + skb->csum_start; +#else /* < 2.6.22 */ + return skb_transport_header(skb); +#endif +} +#endif + +#if !(UBUNTU_VERSION_CODE && \ + UBUNTU_VERSION_CODE >= UBUNTU_VERSION(4,4,0,21)) && \ + !(RHEL_RELEASE_CODE && \ + (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2))) && \ + !(SLE_VERSION_CODE && (SLE_VERSION_CODE >= SLE_VERSION(12,3,0))) +static inline void napi_consume_skb(struct sk_buff *skb, + int __always_unused budget) +{ + dev_consume_skb_any(skb); +} + +#endif /* UBUNTU 4,4,0,21, RHEL 7.2, SLES12 SP3 */ +#if !(SLE_VERSION_CODE && (SLE_VERSION_CODE >= SLE_VERSION(12,3,0))) && \ + !(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,4)) +static inline void csum_replace_by_diff(__sum16 *sum, __wsum diff) +{ + * sum = csum_fold(csum_add(diff, ~csum_unfold(*sum))); +} +#endif +#if !(RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,2))) && \ + !(SLE_VERSION_CODE && (SLE_VERSION_CODE > SLE_VERSION(12,3,0))) +static inline void page_ref_inc(struct page *page) +{ + get_page(page); +} +#else +#define HAVE_PAGE_COUNT_BULK_UPDATE +#endif +#ifndef IPV4_USER_FLOW +#define IPV4_USER_FLOW 0x0d /* spec only (usr_ip4_spec) */ +#endif + +#if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,4)) +#define HAVE_TC_SETUP_CLSFLOWER +#define HAVE_TC_FLOWER_ENC +#endif + +#if ((RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,7)) || \ + (SLE_VERSION_CODE >= SLE_VERSION(12,2,0))) +#define HAVE_TC_SETUP_CLSU32 +#endif + +#if (SLE_VERSION_CODE >= SLE_VERSION(12,2,0)) +#define HAVE_TC_SETUP_CLSFLOWER +#endif + +#else /* >= 4.6.0 */ +#define HAVE_PAGE_COUNT_BULK_UPDATE +#define HAVE_ETHTOOL_FLOW_UNION_IP6_SPEC +#define HAVE_PTP_CROSSTIMESTAMP +#define HAVE_TC_SETUP_CLSFLOWER +#define HAVE_TC_SETUP_CLSU32 +#endif /* 4.6.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)) +#if ((SLE_VERSION_CODE >= SLE_VERSION(12,3,0)) ||\ + (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,4))) +#define HAVE_NETIF_TRANS_UPDATE +#endif /* SLES12sp3+ || RHEL7.4+ */ +#if ((UBUNTU_VERSION_CODE >= UBUNTU_VERSION(4,4,0,21)) || \ + (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,4)) || \ + (SLE_VERSION_CODE >= SLE_VERSION(12,3,0))) +#define HAVE_DEVLINK_SUPPORT +#endif /* UBUNTU 4,4,0,21, RHEL 7.4, SLES12 SP3 */ +#if ((RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,3)) ||\ + (SLE_VERSION_CODE >= SLE_VERSION(12,3,0))) +#define HAVE_ETHTOOL_25G_BITS +#define HAVE_ETHTOOL_50G_BITS +#define HAVE_ETHTOOL_100G_BITS +#endif /* RHEL7.3+ || SLES12sp3+ */ +#else /* 4.7.0 */ +#define HAVE_DEVLINK_SUPPORT +#define HAVE_NETIF_TRANS_UPDATE +#define HAVE_ETHTOOL_CONVERT_U32_AND_LINK_MODE +#define HAVE_ETHTOOL_25G_BITS +#define HAVE_ETHTOOL_50G_BITS +#define HAVE_ETHTOOL_100G_BITS +#define HAVE_TCF_MIRRED_REDIRECT +#endif /* 4.7.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)) +#if !(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,4)) +enum udp_parsable_tunnel_type { + UDP_TUNNEL_TYPE_VXLAN, + UDP_TUNNEL_TYPE_GENEVE, +}; +struct udp_tunnel_info { + unsigned short type; + sa_family_t sa_family; + __be16 port; +}; +#endif + +#if (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,5)) +#define HAVE_TCF_EXTS_TO_LIST +#endif + +#if (UBUNTU_VERSION_CODE && UBUNTU_VERSION_CODE < UBUNTU_VERSION(4,8,0,0)) +#define tc_no_actions(_exts) true +#define tc_for_each_action(_a, _exts) while (0) +#endif +#if !(SLE_VERSION_CODE && (SLE_VERSION_CODE >= SLE_VERSION(12,3,0))) &&\ + !(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,4)) +static inline int +#ifdef HAVE_NON_CONST_PCI_DRIVER_NAME +pci_request_io_regions(struct pci_dev *pdev, char *name) +#else +pci_request_io_regions(struct pci_dev *pdev, const char *name) +#endif +{ + return pci_request_selected_regions(pdev, + pci_select_bars(pdev, IORESOURCE_IO), name); +} + +static inline void +pci_release_io_regions(struct pci_dev *pdev) +{ + return pci_release_selected_regions(pdev, + pci_select_bars(pdev, IORESOURCE_IO)); +} + +static inline int +#ifdef HAVE_NON_CONST_PCI_DRIVER_NAME +pci_request_mem_regions(struct pci_dev *pdev, char *name) +#else +pci_request_mem_regions(struct pci_dev *pdev, const char *name) +#endif +{ + return pci_request_selected_regions(pdev, + pci_select_bars(pdev, IORESOURCE_MEM), name); +} + +static inline void +pci_release_mem_regions(struct pci_dev *pdev) +{ + return pci_release_selected_regions(pdev, + pci_select_bars(pdev, IORESOURCE_MEM)); +} +#endif /* !SLE_VERSION(12,3,0) */ +#if ((RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,4)) ||\ + (SLE_VERSION_CODE >= SLE_VERSION(12,3,0))) +#define HAVE_ETHTOOL_NEW_50G_BITS +#endif /* RHEL7.4+ || SLES12sp3+ */ +#else +#define HAVE_UDP_ENC_RX_OFFLOAD +#define HAVE_TCF_EXTS_TO_LIST +#define HAVE_ETHTOOL_NEW_50G_BITS +#endif /* 4.8.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,9,0)) +#ifdef HAVE_TC_SETUP_CLSFLOWER +#if (!(RHEL_RELEASE_CODE) && !(SLE_VERSION_CODE) || \ + (SLE_VERSION_CODE && (SLE_VERSION_CODE < SLE_VERSION(12,3,0)))) +#define HAVE_TC_FLOWER_VLAN_IN_TAGS +#endif /* !RHEL_RELEASE_CODE && !SLE_VERSION_CODE || = RHEL_RELEASE_VERSION(7,4)) +#define HAVE_ETHTOOL_NEW_1G_BITS +#define HAVE_ETHTOOL_NEW_10G_BITS +#endif /* RHEL7.4+ */ +#if (!(SLE_VERSION_CODE) && !(RHEL_RELEASE_CODE)) || \ + SLE_VERSION_CODE && (SLE_VERSION_CODE <= SLE_VERSION(12,3,0)) || \ + RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE <= RHEL_RELEASE_VERSION(7,5)) +#define time_is_before_jiffies64(a) time_after64(get_jiffies_64(), a) +#endif /* !SLE_VERSION_CODE && !RHEL_RELEASE_CODE || (SLES <= 12.3.0) || (RHEL <= 7.5) */ +#if (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,4)) +static inline void bitmap_from_u64(unsigned long *dst, u64 mask) +{ + dst[0] = mask & ULONG_MAX; + + if (sizeof(mask) > sizeof(unsigned long)) + dst[1] = mask >> 32; +} +#endif /* =4.9 */ +#define HAVE_FLOW_DISSECTOR_KEY_VLAN_PRIO +#define HAVE_ETHTOOL_NEW_1G_BITS +#define HAVE_ETHTOOL_NEW_10G_BITS +#endif /* KERNEL_VERSION(4.9.0) */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)) +/* SLES 12.3 and RHEL 7.5 backported this interface */ +#if (!SLE_VERSION_CODE && !RHEL_RELEASE_CODE) || \ + (SLE_VERSION_CODE && (SLE_VERSION_CODE < SLE_VERSION(12,3,0))) || \ + (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,5))) +static inline bool _kc_napi_complete_done2(struct napi_struct *napi, + int __always_unused work_done) +{ + /* it was really hard to get napi_complete_done to be safe to call + * recursively without running into our own kcompat, so just use + * napi_complete + */ + napi_complete(napi); + + /* true means that the stack is telling the driver to go-ahead and + * re-enable interrupts + */ + return true; +} + +#ifdef napi_complete_done +#undef napi_complete_done +#endif +#define napi_complete_done _kc_napi_complete_done2 +#endif /* sles and rhel exclusion for < 4.10 */ +#if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,4)) +#define HAVE_DEV_WALK_API +#define HAVE_ETHTOOL_NEW_2500MB_BITS +#define HAVE_ETHTOOL_5G_BITS +#endif /* RHEL7.4+ */ +#if (SLE_VERSION_CODE && (SLE_VERSION_CODE == SLE_VERSION(12,3,0))) +#define HAVE_STRUCT_DMA_ATTRS +#endif /* (SLES == 12.3.0) */ +#if (SLE_VERSION_CODE && (SLE_VERSION_CODE >= SLE_VERSION(12,3,0))) +#define HAVE_NETDEVICE_MIN_MAX_MTU +#endif /* (SLES >= 12.3.0) */ +#if (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,5))) +#define HAVE_STRUCT_DMA_ATTRS +#define HAVE_RHEL7_EXTENDED_MIN_MAX_MTU +#define HAVE_NETDEVICE_MIN_MAX_MTU +#endif +#if (!(SLE_VERSION_CODE && (SLE_VERSION_CODE >= SLE_VERSION(12,3,0))) && \ + !(RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,5)))) +#ifndef dma_map_page_attrs +#define dma_map_page_attrs __kc_dma_map_page_attrs +static inline dma_addr_t __kc_dma_map_page_attrs(struct device *dev, + struct page *page, + size_t offset, size_t size, + enum dma_data_direction dir, + unsigned long __always_unused attrs) +{ + return dma_map_page(dev, page, offset, size, dir); +} +#endif + +#ifndef dma_unmap_page_attrs +#define dma_unmap_page_attrs __kc_dma_unmap_page_attrs +static inline void __kc_dma_unmap_page_attrs(struct device *dev, + dma_addr_t addr, size_t size, + enum dma_data_direction dir, + unsigned long __always_unused attrs) +{ + dma_unmap_page(dev, addr, size, dir); +} +#endif + +static inline void __page_frag_cache_drain(struct page *page, + unsigned int count) +{ +#ifdef HAVE_PAGE_COUNT_BULK_UPDATE + if (!page_ref_sub_and_test(page, count)) + return; + + init_page_count(page); +#else + BUG_ON(count > 1); + if (!count) + return; +#endif + __free_pages(page, compound_order(page)); +} +#endif /* !SLE_VERSION(12,3,0) && !RHEL_VERSION(7,5) */ +#if ((SLE_VERSION_CODE && (SLE_VERSION_CODE > SLE_VERSION(12,3,0))) ||\ + (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,5))) +#define HAVE_SWIOTLB_SKIP_CPU_SYNC +#endif + +#if ((SLE_VERSION_CODE && (SLE_VERSION_CODE < SLE_VERSION(15,0,0))) ||\ + (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE <= RHEL_RELEASE_VERSION(7,4)))) +#define page_frag_free __free_page_frag +#endif +#ifndef ETH_MIN_MTU +#define ETH_MIN_MTU 68 +#endif /* ETH_MIN_MTU */ +#else /* >= 4.10 */ +#define HAVE_TC_FLOWER_ENC +#define HAVE_NETDEVICE_MIN_MAX_MTU +#define HAVE_SWIOTLB_SKIP_CPU_SYNC +#define HAVE_NETDEV_TC_RESETS_XPS +#define HAVE_XPS_QOS_SUPPORT +#define HAVE_DEV_WALK_API +#define HAVE_ETHTOOL_NEW_2500MB_BITS +#define HAVE_ETHTOOL_5G_BITS +/* kernel 4.10 onwards, as part of busy_poll rewrite, new state were added + * which is part of NAPI:state. If NAPI:state=NAPI_STATE_IN_BUSY_POLL, + * it means napi_poll is invoked in busy_poll context + */ +#define HAVE_NAPI_STATE_IN_BUSY_POLL +#define HAVE_TCF_MIRRED_EGRESS_REDIRECT +#endif /* 4.10.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0)) +#ifdef CONFIG_NET_RX_BUSY_POLL +#define HAVE_NDO_BUSY_POLL +#endif /* CONFIG_NET_RX_BUSY_POLL */ +#if ((SLE_VERSION_CODE && (SLE_VERSION_CODE >= SLE_VERSION(12,3,0))) || \ + (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,5)))) +#define HAVE_VOID_NDO_GET_STATS64 +#endif /* (SLES >= 12.3.0) && (RHEL >= 7.5) */ + +static inline void _kc_dev_kfree_skb_irq(struct sk_buff *skb) +{ + if (!skb) + return; + dev_kfree_skb_irq(skb); +} + +#undef dev_kfree_skb_irq +#define dev_kfree_skb_irq _kc_dev_kfree_skb_irq + +static inline void _kc_dev_consume_skb_irq(struct sk_buff *skb) +{ + if (!skb) + return; + dev_consume_skb_irq(skb); +} + +#undef dev_consume_skb_irq +#define dev_consume_skb_irq _kc_dev_consume_skb_irq + +static inline void _kc_dev_kfree_skb_any(struct sk_buff *skb) +{ + if (!skb) + return; + dev_kfree_skb_any(skb); +} + +#undef dev_kfree_skb_any +#define dev_kfree_skb_any _kc_dev_kfree_skb_any + +static inline void _kc_dev_consume_skb_any(struct sk_buff *skb) +{ + if (!skb) + return; + dev_consume_skb_any(skb); +} + +#undef dev_consume_skb_any +#define dev_consume_skb_any _kc_dev_consume_skb_any + +#else /* > 4.11 */ +#define HAVE_VOID_NDO_GET_STATS64 +#define HAVE_VM_OPS_FAULT_NO_VMA +#endif /* 4.11.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,13,0)) +#if ((SLE_VERSION_CODE && (SLE_VERSION_CODE > SLE_VERSION(12,3,0))) || \ + (RHEL_RELEASE_CODE && RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,5))) +#define HAVE_TCF_EXTS_HAS_ACTION +#endif +#define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */ +#if (SLE_VERSION_CODE && (SLE_VERSION_CODE >= SLE_VERSION(12,4,0))) +#define HAVE_PCI_ERROR_HANDLER_RESET_PREPARE +#endif /* SLES >= 12sp4 */ +#else /* > 4.13 */ +#define HAVE_HWTSTAMP_FILTER_NTP_ALL +#define HAVE_NDO_SETUP_TC_CHAIN_INDEX +#define HAVE_PCI_ERROR_HANDLER_RESET_PREPARE +#define HAVE_PTP_CLOCK_DO_AUX_WORK +#endif /* 4.13.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)) +#ifdef ETHTOOL_GLINKSETTINGS +#ifndef ethtool_link_ksettings_del_link_mode +#define ethtool_link_ksettings_del_link_mode(ptr, name, mode) \ + __clear_bit(ETHTOOL_LINK_MODE_ ## mode ## _BIT, (ptr)->link_modes.name) +#endif +#endif /* ETHTOOL_GLINKSETTINGS */ +#if (SLE_VERSION_CODE && (SLE_VERSION_CODE >= SLE_VERSION(12,4,0))) +#define HAVE_NDO_SETUP_TC_REMOVE_TC_TO_NETDEV +#endif + +#if (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,5))) +#define HAVE_NDO_SETUP_TC_REMOVE_TC_TO_NETDEV +#define HAVE_RHEL7_NETDEV_OPS_EXT_NDO_SETUP_TC +#endif + +#define TIMER_DATA_TYPE unsigned long +#define TIMER_FUNC_TYPE void (*)(TIMER_DATA_TYPE) + +#define timer_setup(timer, callback, flags) \ + __setup_timer((timer), (TIMER_FUNC_TYPE)(callback), \ + (TIMER_DATA_TYPE)(timer), (flags)) + +#define from_timer(var, callback_timer, timer_fieldname) \ + container_of(callback_timer, typeof(*var), timer_fieldname) + +#ifndef xdp_do_flush_map +#define xdp_do_flush_map() do {} while (0) +#endif +struct _kc_xdp_buff { + void *data; + void *data_end; + void *data_hard_start; +}; +#define xdp_buff _kc_xdp_buff +struct _kc_bpf_prog { +}; +#define bpf_prog _kc_bpf_prog +#ifndef DIV_ROUND_DOWN_ULL +#define DIV_ROUND_DOWN_ULL(ll, d) \ + ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; }) +#endif /* DIV_ROUND_DOWN_ULL */ +#else /* > 4.14 */ +#define HAVE_XDP_SUPPORT +#define HAVE_NDO_SETUP_TC_REMOVE_TC_TO_NETDEV +#define HAVE_TCF_EXTS_HAS_ACTION +#endif /* 4.14.0 */ + +/*****************************************************************************/ +#ifndef ETHTOOL_GLINKSETTINGS + +#define __ETHTOOL_LINK_MODE_MASK_NBITS 32 +#define ETHTOOL_LINK_MASK_SIZE BITS_TO_LONGS(__ETHTOOL_LINK_MODE_MASK_NBITS) + +/** + * struct ethtool_link_ksettings + * @link_modes: supported and advertising, single item arrays + * @link_modes.supported: bitmask of supported link speeds + * @link_modes.advertising: bitmask of currently advertised speeds + * @base: base link details + * @base.speed: current link speed + * @base.port: current port type + * @base.duplex: current duplex mode + * @base.autoneg: current autonegotiation settings + * + * This struct and the following macros provide a way to support the old + * ethtool get/set_settings API on older kernels, but in the style of the new + * GLINKSETTINGS API. In this way, the same code can be used to support both + * APIs as seemlessly as possible. + * + * It should be noted the old API only has support up to the first 32 bits. + */ +struct ethtool_link_ksettings { + struct { + u32 speed; + u8 port; + u8 duplex; + u8 autoneg; + } base; + struct { + unsigned long supported[ETHTOOL_LINK_MASK_SIZE]; + unsigned long advertising[ETHTOOL_LINK_MASK_SIZE]; + } link_modes; +}; + +#define ETHTOOL_LINK_NAME_advertising(mode) ADVERTISED_ ## mode +#define ETHTOOL_LINK_NAME_supported(mode) SUPPORTED_ ## mode +#define ETHTOOL_LINK_NAME(name) ETHTOOL_LINK_NAME_ ## name +#define ETHTOOL_LINK_CONVERT(name, mode) ETHTOOL_LINK_NAME(name)(mode) + +/** + * ethtool_link_ksettings_zero_link_mode + * @ptr: ptr to ksettings struct + * @name: supported or advertising + */ +#define ethtool_link_ksettings_zero_link_mode(ptr, name)\ + (*((ptr)->link_modes.name) = 0x0) + +/** + * ethtool_link_ksettings_add_link_mode + * @ptr: ptr to ksettings struct + * @name: supported or advertising + * @mode: link mode to add + */ +#define ethtool_link_ksettings_add_link_mode(ptr, name, mode)\ + (*((ptr)->link_modes.name) |= (typeof(*((ptr)->link_modes.name)))ETHTOOL_LINK_CONVERT(name, mode)) + +/** + * ethtool_link_ksettings_del_link_mode + * @ptr: ptr to ksettings struct + * @name: supported or advertising + * @mode: link mode to delete + */ +#define ethtool_link_ksettings_del_link_mode(ptr, name, mode)\ + (*((ptr)->link_modes.name) &= ~(typeof(*((ptr)->link_modes.name)))ETHTOOL_LINK_CONVERT(name, mode)) + +/** + * ethtool_link_ksettings_test_link_mode + * @ptr: ptr to ksettings struct + * @name: supported or advertising + * @mode: link mode to add + */ +#define ethtool_link_ksettings_test_link_mode(ptr, name, mode)\ + (!!(*((ptr)->link_modes.name) & ETHTOOL_LINK_CONVERT(name, mode))) + +/** + * _kc_ethtool_ksettings_to_cmd - Convert ethtool_link_ksettings to ethtool_cmd + * @ks: ethtool_link_ksettings struct + * @cmd: ethtool_cmd struct + * + * Convert an ethtool_link_ksettings structure into the older ethtool_cmd + * structure. We provide this in kcompat.h so that drivers can easily + * implement the older .{get|set}_settings as wrappers around the new api. + * Hence, we keep it prefixed with _kc_ to make it clear this isn't actually + * a real function in the kernel. + */ +static inline void +_kc_ethtool_ksettings_to_cmd(struct ethtool_link_ksettings *ks, + struct ethtool_cmd *cmd) +{ + cmd->supported = (u32)ks->link_modes.supported[0]; + cmd->advertising = (u32)ks->link_modes.advertising[0]; + ethtool_cmd_speed_set(cmd, ks->base.speed); + cmd->duplex = ks->base.duplex; + cmd->autoneg = ks->base.autoneg; + cmd->port = ks->base.port; +} + +#endif /* !ETHTOOL_GLINKSETTINGS */ + +/*****************************************************************************/ +#if ((LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)) || \ + (SLE_VERSION_CODE && (SLE_VERSION_CODE <= SLE_VERSION(12,3,0))) || \ + (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE <= RHEL_RELEASE_VERSION(7,5)))) +#define phy_speed_to_str _kc_phy_speed_to_str +const char *_kc_phy_speed_to_str(int speed); +#else /* (LINUX >= 4.14.0) || (SLES > 12.3.0) || (RHEL > 7.5) */ +#include +#endif /* (LINUX < 4.14.0) || (SLES <= 12.3.0) || (RHEL <= 7.5) */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)) +#if ((RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,6))) || \ + (SLE_VERSION_CODE && (SLE_VERSION_CODE >= SLE_VERSION(15,1,0)))) +#define HAVE_TC_CB_AND_SETUP_QDISC_MQPRIO +#define HAVE_TCF_BLOCK +#else /* RHEL >= 7.6 || SLES >= 15.1 */ +#define TC_SETUP_QDISC_MQPRIO TC_SETUP_MQPRIO +#endif /* !(RHEL >= 7.6) && !(SLES >= 15.1) */ +void _kc_ethtool_intersect_link_masks(struct ethtool_link_ksettings *dst, + struct ethtool_link_ksettings *src); +#define ethtool_intersect_link_masks _kc_ethtool_intersect_link_masks +#else /* >= 4.15 */ +#define HAVE_NDO_BPF +#define HAVE_XDP_BUFF_DATA_META +#define HAVE_TC_CB_AND_SETUP_QDISC_MQPRIO +#define HAVE_TCF_BLOCK +#endif /* 4.15.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,16,0)) +#define pci_printk(level, pdev, fmt, arg...) \ + dev_printk(level, &(pdev)->dev, fmt, ##arg) +#define pci_emerg(pdev, fmt, arg...) dev_emerg(&(pdev)->dev, fmt, ##arg) +#define pci_alert(pdev, fmt, arg...) dev_alert(&(pdev)->dev, fmt, ##arg) +#define pci_crit(pdev, fmt, arg...) dev_crit(&(pdev)->dev, fmt, ##arg) +#define pci_err(pdev, fmt, arg...) dev_err(&(pdev)->dev, fmt, ##arg) +#define pci_warn(pdev, fmt, arg...) dev_warn(&(pdev)->dev, fmt, ##arg) +#define pci_notice(pdev, fmt, arg...) dev_notice(&(pdev)->dev, fmt, ##arg) +#define pci_info(pdev, fmt, arg...) dev_info(&(pdev)->dev, fmt, ##arg) +#define pci_dbg(pdev, fmt, arg...) dev_dbg(&(pdev)->dev, fmt, ##arg) + +#ifndef array_index_nospec +static inline unsigned long _kc_array_index_mask_nospec(unsigned long index, + unsigned long size) +{ + /* + * Always calculate and emit the mask even if the compiler + * thinks the mask is not needed. The compiler does not take + * into account the value of @index under speculation. + */ + OPTIMIZER_HIDE_VAR(index); + return ~(long)(index | (size - 1UL - index)) >> (BITS_PER_LONG - 1); +} + +#define array_index_nospec(index, size) \ +({ \ + typeof(index) _i = (index); \ + typeof(size) _s = (size); \ + unsigned long _mask = _kc_array_index_mask_nospec(_i, _s); \ + \ + BUILD_BUG_ON(sizeof(_i) > sizeof(long)); \ + BUILD_BUG_ON(sizeof(_s) > sizeof(long)); \ + \ + (typeof(_i)) (_i & _mask); \ +}) +#endif /* array_index_nospec */ +#if (!(RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,6))) && \ + !(SLE_VERSION_CODE && (SLE_VERSION_CODE >= SLE_VERSION(15,1,0)))) +#ifdef HAVE_TC_CB_AND_SETUP_QDISC_MQPRIO +#include +static inline bool +tc_cls_can_offload_and_chain0(const struct net_device *dev, + struct tc_cls_common_offload *common) +{ + if (!tc_can_offload(dev)) + return false; + if (common->chain_index) + return false; + + return true; +} +#endif /* HAVE_TC_CB_AND_SETUP_QDISC_MQPRIO */ +#endif /* !(RHEL >= 7.6) && !(SLES >= 15.1) */ +#ifndef sizeof_field +#define sizeof_field(TYPE, MEMBER) (sizeof((((TYPE *)0)->MEMBER))) +#endif /* sizeof_field */ +#else /* >= 4.16 */ +#include +#define HAVE_XDP_BUFF_RXQ +#define HAVE_TC_FLOWER_OFFLOAD_COMMON_EXTACK +#define HAVE_TCF_MIRRED_DEV +#define HAVE_VF_STATS_DROPPED +#endif /* 4.16.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0)) +#include +#include +#define PCIE_SPEED_16_0GT 0x17 +#define PCI_EXP_LNKCAP_SLS_16_0GB 0x00000004 /* LNKCAP2 SLS Vector bit 3 */ +#define PCI_EXP_LNKSTA_CLS_16_0GB 0x0004 /* Current Link Speed 16.0GT/s */ +#define PCI_EXP_LNKCAP2_SLS_16_0GB 0x00000010 /* Supported Speed 16GT/s */ +void _kc_pcie_print_link_status(struct pci_dev *dev); +#define pcie_print_link_status _kc_pcie_print_link_status +#else /* >= 4.17.0 */ +#define HAVE_XDP_BUFF_IN_XDP_H +#endif /* 4.17.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,18,0)) +#ifdef NETIF_F_HW_L2FW_DOFFLOAD +#include +#ifndef macvlan_supports_dest_filter +#define macvlan_supports_dest_filter _kc_macvlan_supports_dest_filter +static inline bool _kc_macvlan_supports_dest_filter(struct net_device *dev) +{ + struct macvlan_dev *macvlan = netdev_priv(dev); + + return macvlan->mode == MACVLAN_MODE_PRIVATE || + macvlan->mode == MACVLAN_MODE_VEPA || + macvlan->mode == MACVLAN_MODE_BRIDGE; +} +#endif + +#if (!SLE_VERSION_CODE || (SLE_VERSION_CODE < SLE_VERSION(15,1,0))) +#ifndef macvlan_accel_priv +#define macvlan_accel_priv _kc_macvlan_accel_priv +static inline void *_kc_macvlan_accel_priv(struct net_device *dev) +{ + struct macvlan_dev *macvlan = netdev_priv(dev); + + return macvlan->fwd_priv; +} +#endif + +#ifndef macvlan_release_l2fw_offload +#define macvlan_release_l2fw_offload _kc_macvlan_release_l2fw_offload +static inline int _kc_macvlan_release_l2fw_offload(struct net_device *dev) +{ + struct macvlan_dev *macvlan = netdev_priv(dev); + + macvlan->fwd_priv = NULL; + return dev_uc_add(macvlan->lowerdev, dev->dev_addr); +} +#endif +#endif /* !SLES || SLES < 15.1 */ +#endif /* NETIF_F_HW_L2FW_DOFFLOAD */ +#include "kcompat_overflow.h" + +#if (SLE_VERSION_CODE < SLE_VERSION(15,1,0)) +#define firmware_request_nowarn request_firmware_direct +#endif /* !SLES || SLES < 15.1 */ + +#else +#include +#include +#define HAVE_XDP_FRAME_STRUCT +#define HAVE_XDP_SOCK +#define HAVE_NDO_XDP_XMIT_BULK_AND_FLAGS +#define NO_NDO_XDP_FLUSH +#define HAVE_AF_XDP_SUPPORT +#ifndef xdp_umem_get_data +static inline char *__kc_xdp_umem_get_data(struct xdp_umem *umem, u64 addr) +{ + return umem->pages[addr >> PAGE_SHIFT].addr + (addr & (PAGE_SIZE - 1)); +} + +#define xdp_umem_get_data __kc_xdp_umem_get_data +#endif /* !xdp_umem_get_data */ +#ifndef xdp_umem_get_dma +static inline dma_addr_t __kc_xdp_umem_get_dma(struct xdp_umem *umem, u64 addr) +{ + return umem->pages[addr >> PAGE_SHIFT].dma + (addr & (PAGE_SIZE - 1)); +} + +#define xdp_umem_get_dma __kc_xdp_umem_get_dma +#endif /* !xdp_umem_get_dma */ +#endif /* 4.18.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0)) +#define bitmap_alloc(nbits, flags) \ + kmalloc_array(BITS_TO_LONGS(nbits), sizeof(unsigned long), flags) +#define bitmap_zalloc(nbits, flags) bitmap_alloc(nbits, ((flags) | __GFP_ZERO)) +#define bitmap_free(bitmap) kfree(bitmap) +#ifdef ETHTOOL_GLINKSETTINGS +#define ethtool_ks_clear(ptr, name) \ + ethtool_link_ksettings_zero_link_mode(ptr, name) +#define ethtool_ks_add_mode(ptr, name, mode) \ + ethtool_link_ksettings_add_link_mode(ptr, name, mode) +#define ethtool_ks_del_mode(ptr, name, mode) \ + ethtool_link_ksettings_del_link_mode(ptr, name, mode) +#define ethtool_ks_test(ptr, name, mode) \ + ethtool_link_ksettings_test_link_mode(ptr, name, mode) +#endif /* ETHTOOL_GLINKSETTINGS */ +#define HAVE_NETPOLL_CONTROLLER +#define REQUIRE_PCI_CLEANUP_AER_ERROR_STATUS +#if (SLE_VERSION_CODE && (SLE_VERSION_CODE >= SLE_VERSION(15,1,0))) +#define HAVE_TCF_MIRRED_DEV +#define HAVE_NDO_SELECT_QUEUE_SB_DEV +#define HAVE_TCF_BLOCK_CB_REGISTER_EXTACK +#endif +#if ((RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,0)) ||\ + (SLE_VERSION_CODE >= SLE_VERSION(15,1,0))) +#define HAVE_TCF_EXTS_FOR_EACH_ACTION +#undef HAVE_TCF_EXTS_TO_LIST +#endif /* RHEL8.0+ */ +#else /* >= 4.19.0 */ +#define HAVE_TCF_BLOCK_CB_REGISTER_EXTACK +#define NO_NETDEV_BPF_PROG_ATTACHED +#define HAVE_NDO_SELECT_QUEUE_SB_DEV +#define HAVE_NETDEV_SB_DEV +#undef HAVE_TCF_EXTS_TO_LIST +#define HAVE_TCF_EXTS_FOR_EACH_ACTION +#define HAVE_TCF_VLAN_TPID +#endif /* 4.19.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,20,0)) +#define HAVE_XDP_UMEM_PROPS +#ifdef HAVE_AF_XDP_SUPPORT +#ifndef napi_if_scheduled_mark_missed +static inline bool __kc_napi_if_scheduled_mark_missed(struct napi_struct *n) +{ + unsigned long val, new; + + do { + val = READ_ONCE(n->state); + if (val & NAPIF_STATE_DISABLE) + return true; + + if (!(val & NAPIF_STATE_SCHED)) + return false; + + new = val | NAPIF_STATE_MISSED; + } while (cmpxchg(&n->state, val, new) != val); + + return true; +} + +#define napi_if_scheduled_mark_missed __kc_napi_if_scheduled_mark_missed +#endif /* !napi_if_scheduled_mark_missed */ +#endif /* HAVE_AF_XDP_SUPPORT */ +#else /* >= 4.20.0 */ +#define HAVE_AF_XDP_ZC_SUPPORT +#define HAVE_VXLAN_TYPE +#endif /* 4.20.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)) +#if (!(RHEL_RELEASE_CODE && RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(8,0))) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0)) +#define NETLINK_MAX_COOKIE_LEN 20 +struct netlink_ext_ack { + const char *_msg; + const struct nlattr *bad_attr; + u8 cookie[NETLINK_MAX_COOKIE_LEN]; + u8 cookie_len; +}; + +#endif /* < 4.12 */ +static inline int _kc_dev_open(struct net_device *netdev, + struct netlink_ext_ack __always_unused *extack) +{ + return dev_open(netdev); +} + +#define dev_open _kc_dev_open +#endif /* !(RHEL_RELEASE_CODE && RHEL > RHEL(8,0)) */ +#if (RHEL_RELEASE_CODE && \ + (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,7) && \ + RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8,0)) || \ + (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,1))) +#define HAVE_PTP_SYS_OFFSET_EXTENDED_IOCTL +#else /* RHEL >= 7.7 && RHEL < 8.0 || RHEL >= 8.1 */ +struct ptp_system_timestamp { + struct timespec64 pre_ts; + struct timespec64 post_ts; +}; + +static inline void +ptp_read_system_prets(struct ptp_system_timestamp __always_unused *sts) +{ + ; +} + +static inline void +ptp_read_system_postts(struct ptp_system_timestamp __always_unused *sts) +{ + ; +} +#endif /* !(RHEL >= 7.7 && RHEL != 8.0) */ +#if (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,1))) +#define HAVE_NDO_BRIDGE_SETLINK_EXTACK +#endif /* RHEL 8.1 */ +#if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,2)) +#define HAVE_TC_INDIR_BLOCK +#endif /* RHEL 8.2 */ +#else /* >= 5.0.0 */ +#define HAVE_PTP_SYS_OFFSET_EXTENDED_IOCTL +#define HAVE_NDO_BRIDGE_SETLINK_EXTACK +#define HAVE_DMA_ALLOC_COHERENT_ZEROES_MEM +#define HAVE_GENEVE_TYPE +#define HAVE_TC_INDIR_BLOCK +#endif /* 5.0.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,1,0)) +#if (RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,1))) +#define HAVE_TC_FLOW_RULE_INFRASTRUCTURE +#define HAVE_NDO_FDB_ADD_EXTACK +#else /* RHEL < 8.1 */ +#ifdef HAVE_TC_SETUP_CLSFLOWER +#include + +struct flow_match { + struct flow_dissector *dissector; + void *mask; + void *key; +}; + +struct flow_match_basic { + struct flow_dissector_key_basic *key, *mask; +}; + +struct flow_match_control { + struct flow_dissector_key_control *key, *mask; +}; + +struct flow_match_eth_addrs { + struct flow_dissector_key_eth_addrs *key, *mask; +}; + +#ifdef HAVE_TC_FLOWER_ENC +struct flow_match_enc_keyid { + struct flow_dissector_key_keyid *key, *mask; +}; +#endif + +#ifndef HAVE_TC_FLOWER_VLAN_IN_TAGS +struct flow_match_vlan { + struct flow_dissector_key_vlan *key, *mask; +}; +#endif + +struct flow_match_ipv4_addrs { + struct flow_dissector_key_ipv4_addrs *key, *mask; +}; + +struct flow_match_ipv6_addrs { + struct flow_dissector_key_ipv6_addrs *key, *mask; +}; + +struct flow_match_ports { + struct flow_dissector_key_ports *key, *mask; +}; + +struct flow_rule { + struct flow_match match; +#if 0 + /* In 5.1+ kernels, action is a member of struct flow_rule but is + * not compatible with how we kcompat tc_cls_flower_offload_flow_rule + * below. By not declaring it here, any driver that attempts to use + * action as an element of struct flow_rule will fail to compile + * instead of silently trying to access memory that shouldn't be. + */ + struct flow_action action; +#endif +}; + +void flow_rule_match_basic(const struct flow_rule *rule, + struct flow_match_basic *out); +void flow_rule_match_control(const struct flow_rule *rule, + struct flow_match_control *out); +void flow_rule_match_eth_addrs(const struct flow_rule *rule, + struct flow_match_eth_addrs *out); +#ifndef HAVE_TC_FLOWER_VLAN_IN_TAGS +void flow_rule_match_vlan(const struct flow_rule *rule, + struct flow_match_vlan *out); +#endif +void flow_rule_match_ipv4_addrs(const struct flow_rule *rule, + struct flow_match_ipv4_addrs *out); +void flow_rule_match_ipv6_addrs(const struct flow_rule *rule, + struct flow_match_ipv6_addrs *out); +void flow_rule_match_ports(const struct flow_rule *rule, + struct flow_match_ports *out); +#ifdef HAVE_TC_FLOWER_ENC +void flow_rule_match_enc_ports(const struct flow_rule *rule, + struct flow_match_ports *out); +void flow_rule_match_enc_control(const struct flow_rule *rule, + struct flow_match_control *out); +void flow_rule_match_enc_ipv4_addrs(const struct flow_rule *rule, + struct flow_match_ipv4_addrs *out); +void flow_rule_match_enc_ipv6_addrs(const struct flow_rule *rule, + struct flow_match_ipv6_addrs *out); +void flow_rule_match_enc_keyid(const struct flow_rule *rule, + struct flow_match_enc_keyid *out); +#endif + +static inline struct flow_rule * +tc_cls_flower_offload_flow_rule(struct tc_cls_flower_offload *tc_flow_cmd) +{ + return (struct flow_rule *)&tc_flow_cmd->dissector; +} + +static inline bool flow_rule_match_key(const struct flow_rule *rule, + enum flow_dissector_key_id key) +{ + return dissector_uses_key(rule->match.dissector, key); +} +#endif /* HAVE_TC_SETUP_CLSFLOWER */ + +#endif /* RHEL < 8.1 */ +#else /* >= 5.1.0 */ +#define HAVE_NDO_FDB_ADD_EXTACK +#define NO_XDP_QUERY_XSK_UMEM +#define HAVE_TC_FLOW_RULE_INFRASTRUCTURE +#define HAVE_TC_FLOWER_ENC_IP +#endif /* 5.1.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,2,0)) +#if (defined HAVE_SKB_XMIT_MORE) && \ +(!(RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,2)))) +#define netdev_xmit_more() (skb->xmit_more) +#else +#define netdev_xmit_more() (0) +#endif + +#if (!(RHEL_RELEASE_CODE && (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,2)))) +#ifndef eth_get_headlen +static inline u32 +__kc_eth_get_headlen(const struct net_device __always_unused *dev, void *data, + unsigned int len) +{ + return eth_get_headlen(data, len); +} + +#define eth_get_headlen(dev, data, len) __kc_eth_get_headlen(dev, data, len) +#endif /* !eth_get_headlen */ +#endif /* !RHEL >= 8.2 */ + +#ifndef mmiowb +#ifdef CONFIG_IA64 +#define mmiowb() asm volatile ("mf.a" ::: "memory") +#else +#define mmiowb() +#endif +#endif /* mmiowb */ + +#else /* >= 5.2.0 */ +#define HAVE_NDO_SELECT_QUEUE_FALLBACK_REMOVED +#define SPIN_UNLOCK_IMPLIES_MMIOWB +#endif /* 5.2.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,3,0)) +#if (!(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,2))) +#define flow_block_offload tc_block_offload +#define flow_block_command tc_block_command +#define flow_cls_offload tc_cls_flower_offload +#define flow_block_binder_type tcf_block_binder_type +#define flow_cls_common_offload tc_cls_common_offload +#define flow_cls_offload_flow_rule tc_cls_flower_offload_flow_rule +#define FLOW_CLS_REPLACE TC_CLSFLOWER_REPLACE +#define FLOW_CLS_DESTROY TC_CLSFLOWER_DESTROY +#define FLOW_CLS_STATS TC_CLSFLOWER_STATS +#define FLOW_CLS_TMPLT_CREATE TC_CLSFLOWER_TMPLT_CREATE +#define FLOW_CLS_TMPLT_DESTROY TC_CLSFLOWER_TMPLT_DESTROY +#define FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS \ + TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS +#define FLOW_BLOCK_BIND TC_BLOCK_BIND +#define FLOW_BLOCK_UNBIND TC_BLOCK_UNBIND + +#ifdef HAVE_TC_CB_AND_SETUP_QDISC_MQPRIO +#include + +int _kc_flow_block_cb_setup_simple(struct flow_block_offload *f, + struct list_head *driver_list, + tc_setup_cb_t *cb, + void *cb_ident, void *cb_priv, + bool ingress_only); + +#define flow_block_cb_setup_simple(f, driver_list, cb, cb_ident, cb_priv, \ + ingress_only) \ + _kc_flow_block_cb_setup_simple(f, driver_list, cb, cb_ident, cb_priv, \ + ingress_only) +#endif /* HAVE_TC_CB_AND_SETUP_QDISC_MQPRIO */ +#else /* RHEL >= 8.2 */ +#define HAVE_FLOW_BLOCK_API +#endif /* RHEL >= 8.2 */ +#else /* >= 5.3.0 */ +#define XSK_UMEM_RETURNS_XDP_DESC +#define HAVE_FLOW_BLOCK_API +#endif /* 5.3.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0)) +#if (!(RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8,2)) && \ + !(SLE_VERSION_CODE >= SLE_VERSION(15,2,0))) +static inline unsigned int skb_frag_off(const skb_frag_t *frag) +{ + return frag->page_offset; +} + +static inline void skb_frag_off_add(skb_frag_t *frag, int delta) +{ + frag->page_offset += delta; +} +#define __flow_indr_block_cb_register __tc_indr_block_cb_register +#define __flow_indr_block_cb_unregister __tc_indr_block_cb_unregister +#endif /* !(RHEL >= 8.2) && !(SLES >= 15sp2) */ +#if (SLE_VERSION_CODE >= SLE_VERSION(15,2,0)) +#define HAVE_NDO_XSK_WAKEUP +#endif /* SLES15sp2 */ +#else /* >= 5.4.0 */ +#define HAVE_NDO_XSK_WAKEUP +#endif /* 5.4.0 */ + +/*****************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,6,0)) +#else /* >= 5.6.0 */ +#define HAVE_TX_TIMEOUT_TXQUEUE +#endif /* 5.6.0 */ + +#endif /* _KCOMPAT_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/kcompat_overflow.h b/drivers/net/ethernet/intel/i40e/kcompat_overflow.h new file mode 100644 index 000000000..b7848fed0 --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/kcompat_overflow.h @@ -0,0 +1,319 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ + +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +#ifndef __LINUX_OVERFLOW_H +#define __LINUX_OVERFLOW_H + +#include + +/* + * In the fallback code below, we need to compute the minimum and + * maximum values representable in a given type. These macros may also + * be useful elsewhere, so we provide them outside the + * COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW block. + * + * It would seem more obvious to do something like + * + * #define type_min(T) (T)(is_signed_type(T) ? (T)1 << (8*sizeof(T)-1) : 0) + * #define type_max(T) (T)(is_signed_type(T) ? ((T)1 << (8*sizeof(T)-1)) - 1 : ~(T)0) + * + * Unfortunately, the middle expressions, strictly speaking, have + * undefined behaviour, and at least some versions of gcc warn about + * the type_max expression (but not if -fsanitize=undefined is in + * effect; in that case, the warning is deferred to runtime...). + * + * The slightly excessive casting in type_min is to make sure the + * macros also produce sensible values for the exotic type _Bool. [The + * overflow checkers only almost work for _Bool, but that's + * a-feature-not-a-bug, since people shouldn't be doing arithmetic on + * _Bools. Besides, the gcc builtins don't allow _Bool* as third + * argument.] + * + * Idea stolen from + * https://mail-index.netbsd.org/tech-misc/2007/02/05/0000.html - + * credit to Christian Biere. + */ +/* The is_signed_type macro is redefined in a few places in various kernel + * headers. If this header is included at the same time as one of those, we + * will generate compilation warnings. Since we can't fix every old kernel, + * rename is_signed_type for this file to _kc_is_signed_type. This prevents + * the macro name collision, and should be safe since our drivers do not + * directly call the macro. + */ +#define _kc_is_signed_type(type) (((type)(-1)) < (type)1) +#define __type_half_max(type) ((type)1 << (8*sizeof(type) - 1 - _kc_is_signed_type(type))) +#define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T))) +#define type_min(T) ((T)((T)-type_max(T)-(T)1)) + + +#ifdef COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW +/* + * For simplicity and code hygiene, the fallback code below insists on + * a, b and *d having the same type (similar to the min() and max() + * macros), whereas gcc's type-generic overflow checkers accept + * different types. Hence we don't just make check_add_overflow an + * alias for __builtin_add_overflow, but add type checks similar to + * below. + */ +#define check_add_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + __builtin_add_overflow(__a, __b, __d); \ +}) + +#define check_sub_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + __builtin_sub_overflow(__a, __b, __d); \ +}) + +#define check_mul_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + __builtin_mul_overflow(__a, __b, __d); \ +}) + +#else + + +/* Checking for unsigned overflow is relatively easy without causing UB. */ +#define __unsigned_add_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + *__d = __a + __b; \ + *__d < __a; \ +}) +#define __unsigned_sub_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + *__d = __a - __b; \ + __a < __b; \ +}) +/* + * If one of a or b is a compile-time constant, this avoids a division. + */ +#define __unsigned_mul_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + *__d = __a * __b; \ + __builtin_constant_p(__b) ? \ + __b > 0 && __a > type_max(typeof(__a)) / __b : \ + __a > 0 && __b > type_max(typeof(__b)) / __a; \ +}) + +/* + * For signed types, detecting overflow is much harder, especially if + * we want to avoid UB. But the interface of these macros is such that + * we must provide a result in *d, and in fact we must produce the + * result promised by gcc's builtins, which is simply the possibly + * wrapped-around value. Fortunately, we can just formally do the + * operations in the widest relevant unsigned type (u64) and then + * truncate the result - gcc is smart enough to generate the same code + * with and without the (u64) casts. + */ + +/* + * Adding two signed integers can overflow only if they have the same + * sign, and overflow has happened iff the result has the opposite + * sign. + */ +#define __signed_add_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + *__d = (u64)__a + (u64)__b; \ + (((~(__a ^ __b)) & (*__d ^ __a)) \ + & type_min(typeof(__a))) != 0; \ +}) + +/* + * Subtraction is similar, except that overflow can now happen only + * when the signs are opposite. In this case, overflow has happened if + * the result has the opposite sign of a. + */ +#define __signed_sub_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + *__d = (u64)__a - (u64)__b; \ + ((((__a ^ __b)) & (*__d ^ __a)) \ + & type_min(typeof(__a))) != 0; \ +}) + +/* + * Signed multiplication is rather hard. gcc always follows C99, so + * division is truncated towards 0. This means that we can write the + * overflow check like this: + * + * (a > 0 && (b > MAX/a || b < MIN/a)) || + * (a < -1 && (b > MIN/a || b < MAX/a) || + * (a == -1 && b == MIN) + * + * The redundant casts of -1 are to silence an annoying -Wtype-limits + * (included in -Wextra) warning: When the type is u8 or u16, the + * __b_c_e in check_mul_overflow obviously selects + * __unsigned_mul_overflow, but unfortunately gcc still parses this + * code and warns about the limited range of __b. + */ + +#define __signed_mul_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + typeof(a) __tmax = type_max(typeof(a)); \ + typeof(a) __tmin = type_min(typeof(a)); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + *__d = (u64)__a * (u64)__b; \ + (__b > 0 && (__a > __tmax/__b || __a < __tmin/__b)) || \ + (__b < (typeof(__b))-1 && (__a > __tmin/__b || __a < __tmax/__b)) || \ + (__b == (typeof(__b))-1 && __a == __tmin); \ +}) + + +#define check_add_overflow(a, b, d) \ + __builtin_choose_expr(_kc_is_signed_type(typeof(a)), \ + __signed_add_overflow(a, b, d), \ + __unsigned_add_overflow(a, b, d)) + +#define check_sub_overflow(a, b, d) \ + __builtin_choose_expr(_kc_is_signed_type(typeof(a)), \ + __signed_sub_overflow(a, b, d), \ + __unsigned_sub_overflow(a, b, d)) + +#define check_mul_overflow(a, b, d) \ + __builtin_choose_expr(_kc_is_signed_type(typeof(a)), \ + __signed_mul_overflow(a, b, d), \ + __unsigned_mul_overflow(a, b, d)) + + +#endif /* COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW */ + +/** check_shl_overflow() - Calculate a left-shifted value and check overflow + * + * @a: Value to be shifted + * @s: How many bits left to shift + * @d: Pointer to where to store the result + * + * Computes *@d = (@a << @s) + * + * Returns true if '*d' cannot hold the result or when 'a << s' doesn't + * make sense. Example conditions: + * - 'a << s' causes bits to be lost when stored in *d. + * - 's' is garbage (e.g. negative) or so large that the result of + * 'a << s' is guaranteed to be 0. + * - 'a' is negative. + * - 'a << s' sets the sign bit, if any, in '*d'. + * + * '*d' will hold the results of the attempted shift, but is not + * considered "safe for use" if false is returned. + */ +#define check_shl_overflow(a, s, d) ({ \ + typeof(a) _a = a; \ + typeof(s) _s = s; \ + typeof(d) _d = d; \ + u64 _a_full = _a; \ + unsigned int _to_shift = \ + _s >= 0 && _s < 8 * sizeof(*d) ? _s : 0; \ + *_d = (_a_full << _to_shift); \ + (_to_shift != _s || *_d < 0 || _a < 0 || \ + (*_d >> _to_shift) != _a); \ +}) + +/** + * array_size() - Calculate size of 2-dimensional array. + * + * @a: dimension one + * @b: dimension two + * + * Calculates size of 2-dimensional array: @a * @b. + * + * Returns: number of bytes needed to represent the array or SIZE_MAX on + * overflow. + */ +static inline __must_check size_t array_size(size_t a, size_t b) +{ + size_t bytes; + + if (check_mul_overflow(a, b, &bytes)) + return SIZE_MAX; + + return bytes; +} + +/** + * array3_size() - Calculate size of 3-dimensional array. + * + * @a: dimension one + * @b: dimension two + * @c: dimension three + * + * Calculates size of 3-dimensional array: @a * @b * @c. + * + * Returns: number of bytes needed to represent the array or SIZE_MAX on + * overflow. + */ +static inline __must_check size_t array3_size(size_t a, size_t b, size_t c) +{ + size_t bytes; + + if (check_mul_overflow(a, b, &bytes)) + return SIZE_MAX; + if (check_mul_overflow(bytes, c, &bytes)) + return SIZE_MAX; + + return bytes; +} + +static inline __must_check size_t __ab_c_size(size_t n, size_t size, size_t c) +{ + size_t bytes; + + if (check_mul_overflow(n, size, &bytes)) + return SIZE_MAX; + if (check_add_overflow(bytes, c, &bytes)) + return SIZE_MAX; + + return bytes; +} + +/** + * struct_size() - Calculate size of structure with trailing array. + * @p: Pointer to the structure. + * @member: Name of the array member. + * @n: Number of elements in the array. + * + * Calculates size of memory needed for structure @p followed by an + * array of @n @member elements. + * + * Return: number of bytes needed or SIZE_MAX on overflow. + */ +#define struct_size(p, member, n) \ + __ab_c_size(n, \ + sizeof(*(p)->member) + __must_be_array((p)->member),\ + sizeof(*(p))) + +#endif /* __LINUX_OVERFLOW_H */ diff --git a/drivers/net/ethernet/intel/i40e/kcompat_vfd.c b/drivers/net/ethernet/intel/i40e/kcompat_vfd.c new file mode 100644 index 000000000..ce206f10e --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/kcompat_vfd.c @@ -0,0 +1,2550 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2013 - 2020 Intel Corporation. */ + +#include "kcompat.h" +#include "kcompat_vfd.h" + +#define to_dev(obj) container_of(obj, struct device, kobj) + +const struct vfd_ops *vfd_ops = NULL; + +/** + * __get_pf_pdev - helper function to get the pdev + * @kobj: kobject passed + * @pdev: PCI device information struct + */ +static int __get_pf_pdev(struct kobject *kobj, struct pci_dev **pdev) +{ + struct device *dev; + + if (!kobj->parent) + return -EINVAL; + + /* get pdev */ + dev = to_dev(kobj->parent); + *pdev = to_pci_dev(dev); + + return 0; +} + +/** + * __get_pdev_and_vfid - helper function to get the pdev and the vf id + * @kobj: kobject passed + * @pdev: PCI device information struct + * @vf_id: VF id of the VF under consideration + */ +static int __get_pdev_and_vfid(struct kobject *kobj, struct pci_dev **pdev, + int *vf_id) +{ + struct device *dev; + + if (!kobj->parent->parent) + return -EINVAL; + + /* get pdev */ + dev = to_dev(kobj->parent->parent); + *pdev = to_pci_dev(dev); + + /* get vf_id */ + if (kstrtoint(kobj->name, 10, vf_id) != 0) { + dev_err(&(*pdev)->dev, "Failed to convert %s to vf_id\n", + kobj->name); + return -EINVAL; + } + + return 0; +} + +/** + * __parse_bool_data - helper function to parse boolean data + * @pdev: PCI device information struct + * @buff: buffer with input data + * @attr_name: name of the attribute + * @data: pointer to output data + */ +static int __parse_bool_data(struct pci_dev *pdev, const char *buff, + const char *attr_name, bool *data) +{ + if (sysfs_streq("on", buff)) { + *data = true; + } else if (sysfs_streq("off", buff)) { + *data = false; + } else { + dev_err(&pdev->dev, "set %s: invalid input string", attr_name); + return -EINVAL; + } + return 0; +} + +/** + * __parse_egress_ingress_input - helper function for ingress/egress_mirror attributes + * @pdev: PCI device information struct + * @buff: buffer with input data + * @attr_name: name of the attribute + * @data_new: pointer to input data merged with the old data + * @data_old: pointer to old data of the attribute + * + * Get the input data for egress_mirror or ingress_mirror attribute in the form + * "add " or "rem ". + * Set the output data to off if in "rem ", matches old data. + * + */ +static int __parse_egress_ingress_input(struct pci_dev *pdev, const char *buff, + const char *attr_name, int *data_new, + int *data_old) +{ + int ret = 0; + char *p; + + if (strstr(buff, "add")) { + p = strstr(buff, "add"); + + ret = kstrtoint(p + sizeof("add"), 10, data_new); + if (ret) { + dev_err(&pdev->dev, + "add %s: input error %d\n", attr_name, ret); + return ret; + } + } else if (strstr(buff, "rem")) { + p = strstr(buff, "rem"); + + ret = kstrtoint(p + sizeof("rem"), 10, data_new); + if (ret) { + dev_err(&pdev->dev, + "rem %s: input error %d\n", attr_name, ret); + return ret; + } + + if (*data_new == *data_old) { + if (!strcmp(attr_name, "egress_mirror")) + *data_new = VFD_EGRESS_MIRROR_OFF; + else if (!strcmp(attr_name, "ingress_mirror")) + *data_new = VFD_INGRESS_MIRROR_OFF; + } else { + dev_err(&pdev->dev, + "rem %s: input doesn't match current value", + attr_name); + return -EINVAL; + } + } else { + dev_err(&pdev->dev, "set %s: invalid input string", attr_name); + return -EINVAL; + } + + return ret; +} + +/** + * __parse_add_rem_bitmap - helper function to parse bitmap data + * @pdev: PCI device information struct + * @buff: buffer with input data + * @attr_name: name of the attribute + * @data_new: pointer to input data merged with the old data + * @data_old: pointer to old data of the attribute + * + * If passed add: set data_new to "data_old || data_input" + * If passed rem: set data_new to "data_old || ~data_input" + */ +static int __parse_add_rem_bitmap(struct pci_dev *pdev, const char *buff, + const char *attr_name, + unsigned long *data_new, + unsigned long *data_old) +{ + int ret = 0; + char *p; + + if (strstr(buff, "add")) { + p = strstr(buff, "add"); + bitmap_zero(data_new, VLAN_N_VID); + + ret = bitmap_parselist(p + sizeof("add"), data_new, VLAN_N_VID); + if (ret) { + dev_err(&pdev->dev, + "add %s: input error %d\n", attr_name, ret); + return ret; + } + + bitmap_or(data_new, data_new, data_old, VLAN_N_VID); + } else if (strstr(buff, "rem")) { + p = strstr(buff, "rem"); + bitmap_zero(data_new, VLAN_N_VID); + + ret = bitmap_parselist(p + sizeof("rem"), data_new, VLAN_N_VID); + if (ret) { + dev_err(&pdev->dev, + "rem %s: input error %d\n", attr_name, ret); + return ret; + } + + /* new = old & ~rem */ + bitmap_andnot(data_new, data_old, data_new, VLAN_N_VID); + } else { + dev_err(&pdev->dev, "set %s: invalid input string", attr_name); + return -EINVAL; + } + return 0; +} + +/** + * __parse_promisc_input - helper function for promisc attributes + * @buff: buffer with input data + * @count: size of buff + * @cmd: return pointer to cmd into buff + * @subcmd: return pointer to subcmd into buff + * + * Get the input data for promisc attributes in the form "add/rem mcast/ucast". + */ +static int __parse_promisc_input(const char *buff, size_t count, + const char **cmd, const char **subcmd) +{ + size_t idx = 0; + + /* Remove start spaces */ + while (buff[idx] == ' ' && idx < count) + idx++; + + /* Parse cmd */ + if (strncmp(&buff[idx], "add", strlen("add")) == 0) { + *cmd = &buff[idx]; + idx += strlen("add"); + } else if (strncmp(&buff[idx], "rem", strlen("rem")) == 0) { + *cmd = &buff[idx]; + idx += strlen("rem"); + } else { + return -EINVAL; + } + + if (buff[idx++] != ' ') + return -EINVAL; + + /* Remove spaces between cmd */ + while (buff[idx] == ' ' && idx < count) + idx++; + + /* Parse subcmd */ + if (strncmp(&buff[idx], "ucast", strlen("ucast")) == 0) { + *subcmd = &buff[idx]; + idx += strlen("ucast"); + } else if (strncmp(&buff[idx], "mcast", strlen("mcast")) == 0) { + *subcmd = &buff[idx]; + idx += strlen("mcast"); + } else { + return -EINVAL; + } + + /* Remove spaces after subcmd */ + while ((buff[idx] == ' ' || buff[idx] == '\n') && idx < count) + idx++; + + if (idx != count) + return -EINVAL; + + return 0; +} + +/* Handlers for each VFd operation */ + +/** + * vfd_trunk_show - handler for trunk show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * + * Get current data from driver and copy to buffer + **/ +static ssize_t vfd_trunk_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + + DECLARE_BITMAP(data, VLAN_N_VID); + bitmap_zero(data, VLAN_N_VID); + + if (!vfd_ops->get_trunk) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_trunk(pdev, vf_id, data); + if (ret) + ret = bitmap_print_to_pagebuf(1, buff, data, VLAN_N_VID); + + return ret; +} + +/** + * vfd_trunk_store - handler for trunk store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + * + * Get current data from driver, compose new data based on input values + * depending on "add" or "rem" command, and pass new data to the driver to set. + * + * On success return count, indicating that we used the whole buffer. On + * failure return a negative error condition. + **/ +static ssize_t vfd_trunk_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + unsigned long *data_old, *data_new; + struct pci_dev *pdev; + int vf_id, ret = 0; + + if (!vfd_ops->set_trunk || !vfd_ops->get_trunk) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + data_old = kcalloc(BITS_TO_LONGS(VLAN_N_VID), sizeof(unsigned long), + GFP_KERNEL); + if (!data_old) + return -ENOMEM; + data_new = kcalloc(BITS_TO_LONGS(VLAN_N_VID), sizeof(unsigned long), + GFP_KERNEL); + if (!data_new) { + kfree(data_old); + return -ENOMEM; + } + + ret = vfd_ops->get_trunk(pdev, vf_id, data_old); + if (ret < 0) + goto err_free; + + ret = __parse_add_rem_bitmap(pdev, buff, "trunk", data_new, data_old); + if (ret) + goto err_free; + + if (!bitmap_equal(data_new, data_old, VLAN_N_VID)) + ret = vfd_ops->set_trunk(pdev, vf_id, data_new); + +err_free: + kfree(data_old); + kfree(data_new); + return ret ? ret : count; +} + +/** + * vfd_vlan_mirror_show - handler for vlan_mirror show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * + * Get current data from driver and copy to buffer + **/ +static ssize_t vfd_vlan_mirror_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + + DECLARE_BITMAP(data, VLAN_N_VID); + bitmap_zero(data, VLAN_N_VID); + + if (!vfd_ops->get_vlan_mirror) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_vlan_mirror(pdev, vf_id, data); + if (ret) + ret = bitmap_print_to_pagebuf(1, buff, data, VLAN_N_VID); + + return ret; +} + +/** + * vfd_vlan_mirror_store - handler for vlan_mirror store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + * + * Get current data from driver, compose new data based on input values + * depending on "add" or "rem" command, and pass new data to the driver to set. + * + * On success return count, indicating that we used the whole buffer. On + * failure return a negative error condition. + **/ +static ssize_t vfd_vlan_mirror_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + unsigned long *data_old, *data_new; + struct pci_dev *pdev; + int vf_id, ret = 0; + + if (!vfd_ops->set_vlan_mirror || !vfd_ops->get_vlan_mirror) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + data_old = kcalloc(BITS_TO_LONGS(VLAN_N_VID), sizeof(unsigned long), + GFP_KERNEL); + if (!data_old) + return -ENOMEM; + data_new = kcalloc(BITS_TO_LONGS(VLAN_N_VID), sizeof(unsigned long), + GFP_KERNEL); + if (!data_new) { + kfree(data_old); + return -ENOMEM; + } + + ret = vfd_ops->get_vlan_mirror(pdev, vf_id, data_old); + if (ret < 0) + goto err_free; + + ret = __parse_add_rem_bitmap(pdev, buff, "vlan_mirror", + data_new, data_old); + if (ret) + goto err_free; + + if (!bitmap_equal(data_new, data_old, VLAN_N_VID)) + ret = vfd_ops->set_vlan_mirror(pdev, vf_id, data_new); + +err_free: + kfree(data_old); + kfree(data_new); + return ret ? ret : count; +} + +/** + * vfd_egress_mirror_show - handler for egress_mirror show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_egress_mirror_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + int data; + + if (!vfd_ops->get_egress_mirror) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_egress_mirror(pdev, vf_id, &data); + if (ret < 0) + return ret; + + if (data == VFD_EGRESS_MIRROR_OFF) + ret = scnprintf(buff, PAGE_SIZE, "off\n"); + else + ret = scnprintf(buff, PAGE_SIZE, "%u\n", data); + + return ret; +} + +/** + * vfd_egress_mirror_store - handler for egress_mirror store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_egress_mirror_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + int data_new, data_old; + struct pci_dev *pdev; + int vf_id, ret = 0; + + if (!vfd_ops->set_egress_mirror || !vfd_ops->get_egress_mirror) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_egress_mirror(pdev, vf_id, &data_old); + if (ret < 0) + return ret; + + ret = __parse_egress_ingress_input(pdev, buff, "egress_mirror", + &data_new, &data_old); + if (ret) + return ret; + if(data_new == vf_id) { + dev_err(&pdev->dev, "VF %d: Setting egress_mirror to itself is not allowed\n", vf_id); + return -EINVAL; + } + + if (data_new != data_old) + ret = vfd_ops->set_egress_mirror(pdev, vf_id, data_new); + + return ret ? ret : count; +} + +/** + * vfd_ingress_mirror_show - handler for ingress_mirror show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_ingress_mirror_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + int data; + + if (!vfd_ops->get_ingress_mirror) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_ingress_mirror(pdev, vf_id, &data); + if (ret < 0) + return ret; + + if (data == VFD_INGRESS_MIRROR_OFF) + ret = scnprintf(buff, PAGE_SIZE, "off\n"); + else + ret = scnprintf(buff, PAGE_SIZE, "%u\n", data); + + return ret; +} + +/** + * vfd_ingress_mirror_store - handler for ingress_mirror store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_ingress_mirror_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + int data_new, data_old; + struct pci_dev *pdev; + int vf_id, ret = 0; + + if (!vfd_ops->set_ingress_mirror || !vfd_ops->get_ingress_mirror) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_ingress_mirror(pdev, vf_id, &data_old); + if (ret < 0) + return ret; + + ret = __parse_egress_ingress_input(pdev, buff, "ingress_mirror", + &data_new, &data_old); + if (ret) + return ret; + if(data_new == vf_id) { + dev_err(&pdev->dev, "VF %d: Setting ingress_mirror to itself is not allowed\n", vf_id); + return -EINVAL; + } + + if (data_new != data_old) + ret = vfd_ops->set_ingress_mirror(pdev, vf_id, data_new); + + return ret ? ret : count; +} + +/** + * vfd_mac_anti_spoof_show - handler for mac_anti_spoof show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_mac_anti_spoof_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + bool data; + + if (!vfd_ops->get_mac_anti_spoof) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_mac_anti_spoof(pdev, vf_id, &data); + if (ret < 0) + return ret; + + if (data) + ret = scnprintf(buff, PAGE_SIZE, "on\n"); + else + ret = scnprintf(buff, PAGE_SIZE, "off\n"); + + return ret; +} + +/** + * vfd_mac_anti_spoof_store - handler for mac_anti_spoof store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + * + * On success return count, indicating that we used the whole buffer. On + * failure return a negative error condition. + **/ +static ssize_t vfd_mac_anti_spoof_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + bool data_new, data_old; + + if (!vfd_ops->set_mac_anti_spoof || !vfd_ops->get_mac_anti_spoof) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_mac_anti_spoof(pdev, vf_id, &data_old); + if (ret < 0) + return ret; + + ret = __parse_bool_data(pdev, buff, "mac_anti_spoof", &data_new); + if (ret) + return ret; + + if (data_new != data_old) + ret = vfd_ops->set_mac_anti_spoof(pdev, vf_id, data_new); + + return ret ? ret : count; +} + +/** + * vfd_vlan_anti_spoof_show - handler for vlan_anti_spoof show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_vlan_anti_spoof_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + bool data; + + if (!vfd_ops->get_vlan_anti_spoof) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_vlan_anti_spoof(pdev, vf_id, &data); + if (ret < 0) + return ret; + + if (data) + ret = scnprintf(buff, PAGE_SIZE, "on\n"); + else + ret = scnprintf(buff, PAGE_SIZE, "off\n"); + + return ret; +} + +/** + * vfd_vlan_anti_spoof_store - handler for vlan_anti_spoof store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + * + * On success return count, indicating that we used the whole buffer. On + * failure return a negative error condition. + **/ +static ssize_t vfd_vlan_anti_spoof_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + bool data_new, data_old; + + if (!vfd_ops->set_vlan_anti_spoof || !vfd_ops->get_vlan_anti_spoof) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_vlan_anti_spoof(pdev, vf_id, &data_old); + if (ret < 0) + return ret; + + ret = __parse_bool_data(pdev, buff, "vlan_anti_spoof", &data_new); + if (ret) + return ret; + + if (data_new != data_old) + ret = vfd_ops->set_vlan_anti_spoof(pdev, vf_id, data_new); + + return ret ? ret : count; +} + +/** + * vfd_allow_untagged_show - handler for allow_untagged show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_allow_untagged_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + bool data; + + if (!vfd_ops->get_allow_untagged) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_allow_untagged(pdev, vf_id, &data); + if (ret < 0) + return ret; + + if (data) + ret = scnprintf(buff, PAGE_SIZE, "on\n"); + else + ret = scnprintf(buff, PAGE_SIZE, "off\n"); + + return ret; +} + +/** + * vfd_allow_untagged_store - handler for allow_untagged store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_allow_untagged_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + bool data_new, data_old; + + if (!vfd_ops->set_allow_untagged || !vfd_ops->get_allow_untagged) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_allow_untagged(pdev, vf_id, &data_old); + if (ret < 0) + return ret; + + ret = __parse_bool_data(pdev, buff, "allow_untagged", &data_new); + if (ret) + return ret; + + if (data_new != data_old) + ret = vfd_ops->set_allow_untagged(pdev, vf_id, data_new); + + return ret ? ret : count; +} + +/** + * vfd_loopback_show - handler for loopback show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_loopback_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + bool data; + + if (!vfd_ops->get_loopback) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_loopback(pdev, vf_id, &data); + if (ret < 0) + return ret; + + if (data) + ret = scnprintf(buff, PAGE_SIZE, "on\n"); + else + ret = scnprintf(buff, PAGE_SIZE, "off\n"); + + return ret; +} + +/** + * vfd_loopback_store - handler for loopback store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_loopback_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + bool data_new, data_old; + + if (!vfd_ops->set_loopback || !vfd_ops->get_loopback) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_loopback(pdev, vf_id, &data_old); + if (ret < 0) + return ret; + + ret = __parse_bool_data(pdev, buff, "loopback", &data_new); + if (ret) + return ret; + + if (data_new != data_old) + ret = vfd_ops->set_loopback(pdev, vf_id, data_new); + + return ret ? ret : count; +} + +/** + * vfd_mac_show - handler for mac show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_mac_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buff) +{ + u8 macaddr[ETH_ALEN]; + struct pci_dev *pdev; + int vf_id, ret = 0; + + if (!vfd_ops->get_mac) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_mac(pdev, vf_id, macaddr); + if (ret < 0) + return ret; + + ret = scnprintf(buff, PAGE_SIZE, "%pM\n", macaddr); + + return ret; +} + +/** + * vfd_mac_store - handler for mac store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_mac_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + u8 macaddr[ETH_ALEN]; + u8 macaddr_old[ETH_ALEN]; + struct pci_dev *pdev; + int vf_id, ret = 0; + + if (!vfd_ops->set_mac || !vfd_ops->get_mac) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_mac(pdev, vf_id, macaddr_old); + if (ret < 0) + return ret; + + ret = sscanf(buff, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &macaddr[0], &macaddr[1], &macaddr[2], + &macaddr[3], &macaddr[4], &macaddr[5]); + + if (ret != 6) + return -EINVAL; + + if (!ether_addr_equal(macaddr, macaddr_old)) + ret = vfd_ops->set_mac(pdev, vf_id, macaddr); + + return ret ? ret : count; +} + +/** + * vfd_mac_list_show - handler for mac_list show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + * + * This function also frees the memory allocated for mac_list in another function. + * + **/ +static ssize_t vfd_mac_list_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + unsigned int mac_num_allowed, mac_num_list, mac_num_count; + const char *overflow_msg = "... and more\n"; + unsigned int mac_msg_len = 3*ETH_ALEN; + struct list_head *pos, *n; + struct pci_dev *pdev; + int vf_id, ret; + char *written; + LIST_HEAD(mac_list); + + if (!vfd_ops->get_mac_list) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_mac_list(pdev, vf_id, &mac_list); + if (ret < 0) + goto err_free; + + mac_num_list = 0; + mac_num_count = 0; + list_for_each_safe(pos, n, &mac_list) + mac_num_list++; + + mac_num_allowed = (PAGE_SIZE - 1) / mac_msg_len; + if (mac_num_list > mac_num_allowed) + mac_num_allowed = (PAGE_SIZE - 1 - strlen(overflow_msg)) / + mac_msg_len; + + written = buff; + list_for_each_safe(pos, n, &mac_list) { + struct vfd_macaddr *mac = NULL; + + mac_num_count++; + mac = list_entry(pos, struct vfd_macaddr, list); + if (mac_num_count > mac_num_allowed) { + ret += scnprintf(written, PAGE_SIZE - ret, + "%s", overflow_msg); + goto err_free; + } else if (list_is_last(pos, &mac_list)) { + ret += scnprintf(written, PAGE_SIZE - ret, + "%pM\n", mac->mac); + } else { + ret += scnprintf(written, PAGE_SIZE - ret, + "%pM,", mac->mac); + } + written += mac_msg_len; + } + +err_free: + list_for_each_safe(pos, n, &mac_list) { + struct vfd_macaddr *mac = NULL; + + mac = list_entry(pos, struct vfd_macaddr, list); + list_del(pos); + kfree(mac); + } + return ret; +} + +/** + * vfd_mac_list_store - handler for mac_list store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + * + * Get input mac list into the linked list and depending on "add" or "rem" command + * pass the input mac list to the driver to either add or remove macs to the list. + * + * This function also frees the memory allocated for mac_list in another function. + * + **/ +static ssize_t vfd_mac_list_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + struct list_head *pos, *n; + struct pci_dev *pdev; + u8 macaddr[ETH_ALEN]; + int vf_id, ret; + size_t shift; + bool add; + LIST_HEAD(mac_list_inp); + + if (!vfd_ops->add_macs_to_list || !vfd_ops->rem_macs_from_list) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + if (strstr(buff, "add")) { + shift = sizeof("add"); + add = true; + } else if (strstr(buff, "rem")) { + shift = sizeof("rem"); + add = false; + } else { + dev_err(&pdev->dev, "Invalid input string"); + ret = -EINVAL; + goto err_free; + } + + /* Get input data */ + for (;;) { + struct vfd_macaddr *mac_new; + + if (*(buff + shift) == ' ' || *(buff + shift) == ',') { + shift++; + continue; + } + + ret = sscanf(buff + shift, + "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &macaddr[0], &macaddr[1], &macaddr[2], + &macaddr[3], &macaddr[4], &macaddr[5]); + + if (ret != 6) + break; + + if (!is_valid_ether_addr(macaddr)) { + shift += 3*ETH_ALEN; + continue; + } + + mac_new = kmalloc(sizeof(struct vfd_macaddr), GFP_KERNEL); + if (!mac_new) { + ret = -ENOMEM; + goto err_free; + } + + ether_addr_copy(mac_new->mac, macaddr); + list_add(&mac_new->list, &mac_list_inp); + + shift += 3*ETH_ALEN; + } + + if (add) + ret = vfd_ops->add_macs_to_list(pdev, vf_id, &mac_list_inp); + else + ret = vfd_ops->rem_macs_from_list(pdev, vf_id, &mac_list_inp); + +err_free: + list_for_each_safe(pos, n, &mac_list_inp) { + struct vfd_macaddr *mac = NULL; + + mac = list_entry(pos, struct vfd_macaddr, list); + list_del(pos); + kfree(mac); + } + return ret ? ret : count; +} + +/** + * vfd_promisc_show - handler for promisc show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_promisc_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + u8 data; + + if (!vfd_ops->get_promisc) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_promisc(pdev, vf_id, &data); + if (ret < 0) + return ret; + + if (data == VFD_PROMISC_UNICAST) + ret = scnprintf(buff, PAGE_SIZE, "ucast\n"); + else if (data == VFD_PROMISC_MULTICAST) + ret = scnprintf(buff, PAGE_SIZE, "mcast\n"); + else if (data == (VFD_PROMISC_UNICAST | VFD_PROMISC_MULTICAST)) + ret = scnprintf(buff, PAGE_SIZE, "ucast, mcast\n"); + else if (data == VFD_PROMISC_OFF) + ret = scnprintf(buff, PAGE_SIZE, "off\n"); + + return ret; +} + +/** + * vfd_promisc_store - handler for promisc store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_promisc_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + u8 data_new, data_old; + struct pci_dev *pdev; + const char *subcmd; + const char *cmd; + int vf_id, ret; + + if (!vfd_ops->get_promisc || !vfd_ops->set_promisc) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_promisc(pdev, vf_id, &data_old); + if (ret < 0) + return ret; + + ret = __parse_promisc_input(buff, count, &cmd, &subcmd); + if (ret) + goto promisc_err; + + if (strncmp(cmd, "add", strlen("add")) == 0) { + if (strncmp(subcmd, "ucast", strlen("ucast")) == 0) + data_new = data_old | VFD_PROMISC_UNICAST; + else if (strncmp(subcmd, "mcast", strlen("mcast")) == 0) + data_new = data_old | VFD_PROMISC_MULTICAST; + else + goto promisc_err; + } else if (strncmp(cmd, "rem", strlen("rem")) == 0) { + if (strncmp(subcmd, "ucast", strlen("ucast")) == 0) + data_new = data_old & ~VFD_PROMISC_UNICAST; + else if (strncmp(subcmd, "mcast", strlen("mcast")) == 0) + data_new = data_old & ~VFD_PROMISC_MULTICAST; + else + goto promisc_err; + } else { + goto promisc_err; + } + + if (data_new != data_old) + ret = vfd_ops->set_promisc(pdev, vf_id, data_new); + + return ret ? ret : count; + +promisc_err: + dev_err(&pdev->dev, "Invalid input string"); + return -EINVAL; +} + +/** + * vfd_vlan_strip_show - handler for vlan_strip show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_vlan_strip_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + bool data; + + if (!vfd_ops->get_vlan_strip) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_vlan_strip(pdev, vf_id, &data); + if (ret < 0) + return ret; + + if (data) + ret = scnprintf(buff, PAGE_SIZE, "on\n"); + else + ret = scnprintf(buff, PAGE_SIZE, "off\n"); + + return ret; +} + +/** + * vfd_vlan_strip_store - handler for vlan_strip store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_vlan_strip_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + bool data_new, data_old; + + if (!vfd_ops->set_vlan_strip || !vfd_ops->get_vlan_strip) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_vlan_strip(pdev, vf_id, &data_old); + if (ret < 0) + return ret; + + ret = __parse_bool_data(pdev, buff, "vlan_strip", &data_new); + if (ret) + return ret; + + if (data_new != data_old) + ret = vfd_ops->set_vlan_strip(pdev, vf_id, data_new); + + return ret ? ret : count; +} + +/** + * vfd_link_state_show - handler for link_state show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_link_state_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + enum vfd_link_speed link_speed; + struct pci_dev *pdev; + int vf_id, ret = 0; + bool enabled; + + if (!vfd_ops->get_link_state) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_link_state(pdev, vf_id, &enabled, &link_speed); + if (ret < 0) + return ret; + + if (enabled) { + const char *speed_str; + + switch (link_speed) { + case VFD_LINK_SPEED_100MB: + speed_str = "100 Mbps"; + break; + case VFD_LINK_SPEED_1GB: + speed_str = "1 Gbps"; + break; + case VFD_LINK_SPEED_2_5GB: + speed_str = "2.5 Gbps"; + break; + case VFD_LINK_SPEED_5GB: + speed_str = "5 Gbps"; + break; + case VFD_LINK_SPEED_10GB: + speed_str = "10 Gbps"; + break; + case VFD_LINK_SPEED_40GB: + speed_str = "40 Gbps"; + break; + case VFD_LINK_SPEED_20GB: + speed_str = "20 Gbps"; + break; + case VFD_LINK_SPEED_25GB: + speed_str = "25 Gbps"; + break; + case VFD_LINK_SPEED_UNKNOWN: + speed_str = "unknown speed"; + break; + default: + dev_err(&pdev->dev, "Link speed is not supported"); + return -EOPNOTSUPP; + } + + ret = scnprintf(buff, PAGE_SIZE, "%s, %s\n", "up", speed_str); + } else { + ret = scnprintf(buff, PAGE_SIZE, "down\n"); + } + + return ret; +} + +/** + * vfd_link_state_store - handler for link_state store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_link_state_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + u8 data; + + if (!vfd_ops->set_link_state) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + if (sysfs_streq("enable", buff)) { + data = VFD_LINKSTATE_ON; + } else if (sysfs_streq("disable", buff)) { + data = VFD_LINKSTATE_OFF; + } else if (sysfs_streq("auto", buff)) { + data = VFD_LINKSTATE_AUTO; + } else { + dev_err(&pdev->dev, "Invalid input string"); + return -EINVAL; + } + + ret = vfd_ops->set_link_state(pdev, vf_id, data); + + return ret ? ret : count; +} + +/** + * vfd_enable_show - handler for VF enable/disable show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_enable_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret; + bool data; + + if (!vfd_ops->get_vf_enable) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_vf_enable(pdev, vf_id, &data); + if (ret < 0) + return ret; + + if (data) + ret = scnprintf(buff, PAGE_SIZE, "on\n"); + else + ret = scnprintf(buff, PAGE_SIZE, "off\n"); + + return ret; +} + +/** + * vfd_enable_store - handler for VF enable/disable store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + * + * On success return count, indicating that we used the whole buffer. On + * failure return a negative error condition. + **/ +static ssize_t vfd_enable_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + bool data_new, data_old; + struct pci_dev *pdev; + int vf_id, ret; + + if (!vfd_ops->set_vf_enable || !vfd_ops->get_vf_enable) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_vf_enable(pdev, vf_id, &data_old); + if (ret < 0) + return ret; + + ret = __parse_bool_data(pdev, buff, "enable", &data_new); + if (ret) + return ret; + + if (data_new != data_old) + ret = vfd_ops->set_vf_enable(pdev, vf_id, data_new); + + return ret ? ret : count; +} + +/** + * vfd_max_tx_rate_show - handler for mac_tx_rate show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_max_tx_rate_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + unsigned int max_tx_rate; + struct pci_dev *pdev; + int vf_id, ret; + + if (!vfd_ops->get_max_tx_rate) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_max_tx_rate(pdev, vf_id, &max_tx_rate); + if (ret < 0) + return ret; + + ret = scnprintf(buff, PAGE_SIZE, "%u\n", max_tx_rate); + return ret; +} + +/** + * vfd_max_tx_rate_store - handler for max_tx_rate store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_max_tx_rate_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + unsigned int max_tx_rate; + struct pci_dev *pdev; + int vf_id, ret; + + if (!vfd_ops->set_max_tx_rate) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = kstrtouint(buff, 10, &max_tx_rate); + if (ret) { + dev_err(&pdev->dev, + "Invalid argument, not a decimal number: %s", buff); + return ret; + } + + ret = vfd_ops->set_max_tx_rate(pdev, vf_id, &max_tx_rate); + + return ret ? ret : count; +} + +/** + * vfd_min_tx_rate_show - handler for min_tx_rate show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_min_tx_rate_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + if (!vfd_ops->get_min_tx_rate) + return -EOPNOTSUPP; + + return vfd_ops->get_min_tx_rate(kobj, attr, buff); +} + +/** + * vfd_min_tx_rate_store - handler for min_tx_rate store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_min_tx_rate_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + if (!vfd_ops->set_min_tx_rate) + return -EOPNOTSUPP; + + return vfd_ops->set_min_tx_rate(kobj, attr, buff, count); +} + +/** + * vfd_spoofcheck_show - handler for spoofcheck show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_spoofcheck_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + if (!vfd_ops->get_spoofcheck) + return -EOPNOTSUPP; + + return vfd_ops->get_spoofcheck(kobj, attr, buff); +} + +/** + * vfd_spoofcheck_store - handler for spoofcheck store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_spoofcheck_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + if (!vfd_ops->set_spoofcheck) + return -EOPNOTSUPP; + + return vfd_ops->set_spoofcheck(kobj, attr, buff, count); +} + +/** + * vfd_trust_show - handler for trust show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_trust_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret; + bool data; + + if (!vfd_ops->get_trust_state) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_trust_state(pdev, vf_id, &data); + if (ret < 0) + return ret; + + if (data) + ret = scnprintf(buff, PAGE_SIZE, "on\n"); + else + ret = scnprintf(buff, PAGE_SIZE, "off\n"); + + return ret; +} + +/** + * vfd_trust_store - handler for trust store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_trust_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + bool data_new, data_old; + struct pci_dev *pdev; + int vf_id, ret; + + if (!vfd_ops->set_trust_state || !vfd_ops->get_trust_state) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_trust_state(pdev, vf_id, &data_old); + if (ret < 0) + return ret; + + ret = __parse_bool_data(pdev, buff, "trust", &data_new); + if (ret) + return ret; + + if (data_new != data_old) + ret = vfd_ops->set_trust_state(pdev, vf_id, data_new); + + return ret ? ret : count; +} + +/** + * vfd_reset_stats_store - handler for reset stats store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_reset_stats_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + int vf_id, reset, ret; + struct pci_dev *pdev; + + if (!vfd_ops->reset_stats) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + ret = kstrtoint(buff, 10, &reset); + if (ret) { + dev_err(&pdev->dev, "Invalid input\n"); + return ret; + } + + if (reset != 1) + return -EINVAL; + + ret = vfd_ops->reset_stats(pdev, vf_id); + + return ret ? ret : count; +} + +/** + * vfd_rx_bytes_show - handler for rx_bytes show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_rx_bytes_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + u64 data; + + if (!vfd_ops->get_rx_bytes) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_rx_bytes(pdev, vf_id, &data); + if (ret < 0) + return ret; + + ret = scnprintf(buff, PAGE_SIZE, "%llu\n", data); + + return ret; +} + +/** + * vfd_rx_dropped_show - handler for rx_dropped show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_rx_dropped_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + u64 data; + + if (!vfd_ops->get_rx_dropped) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_rx_dropped(pdev, vf_id, &data); + if (ret < 0) + return ret; + + ret = scnprintf(buff, PAGE_SIZE, "%llu\n", data); + + return ret; +} + +/** + * vfd_rx_packets_show - handler for rx_packets show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_rx_packets_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + u64 data; + + if (!vfd_ops->get_rx_packets) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_rx_packets(pdev, vf_id, &data); + if (ret < 0) + return ret; + + ret = scnprintf(buff, PAGE_SIZE, "%llu\n", data); + + return ret; +} + +/** + * vfd_tx_bytes_show - handler for tx_bytes show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_tx_bytes_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + u64 data; + + if (!vfd_ops->get_tx_bytes) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_tx_bytes(pdev, vf_id, &data); + if (ret < 0) + return ret; + + ret = scnprintf(buff, PAGE_SIZE, "%llu\n", data); + + return ret; +} + +/** + * vfd_tx_dropped_show - handler for tx_dropped show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_tx_dropped_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + u64 data; + + if (!vfd_ops->get_tx_dropped) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_tx_dropped(pdev, vf_id, &data); + if (ret < 0) + return ret; + + ret = scnprintf(buff, PAGE_SIZE, "%llu\n", data); + + return ret; +} + +/** + * vfd_tx_packets_show - handler for tx_packets show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_tx_packets_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + u64 data; + + if (!vfd_ops->get_tx_packets) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_tx_packets(pdev, vf_id, &data); + if (ret < 0) + return ret; + + ret = scnprintf(buff, PAGE_SIZE, "%llu\n", data); + + return ret; +} + +/** + * vfd_tx_spoofed_show - handler for tx_spoofed show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_tx_spoofed_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + u64 data; + + if (!vfd_ops->get_tx_spoofed) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_tx_spoofed(pdev, vf_id, &data); + if (ret < 0) + return ret; + + ret = scnprintf(buff, PAGE_SIZE, "%llu\n", data); + + return ret; +} + +/** + * vfd_tx_errors_show - handler for tx_errors show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_tx_errors_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret = 0; + u64 data; + + if (!vfd_ops->get_tx_errors) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_tx_errors(pdev, vf_id, &data); + if (ret < 0) + return ret; + + ret = scnprintf(buff, PAGE_SIZE, "%llu\n", data); + + return ret; +} + +/** + * qos_share_show - handler for the bw_share show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t qos_share_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret; + u8 data = 0; + + if (!vfd_ops->get_vf_bw_share) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_vf_bw_share(pdev, vf_id, &data); + if (ret < 0) { + dev_err(&pdev->dev, "No bw share applied for VF %d\n", vf_id); + return ret; + } + + ret = scnprintf(buff, PAGE_SIZE, "%u\n", data); + + return ret; +} + +/** + * qos_share_store - handler for the bw_share store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t qos_share_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + struct pci_dev *pdev; + int vf_id, ret; + u8 bw_share; + + if (!vfd_ops->set_vf_bw_share) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + /* parse the bw_share */ + ret = kstrtou8(buff, 10, &bw_share); + if (ret) { + dev_err(&pdev->dev, "Invalid input\n"); + return ret; + } + + /* check that the BW is between 1 and 100 */ + if (bw_share < 1 || bw_share > 100) { + dev_err(&pdev->dev, "BW share has to be between 1-100\n"); + return -EINVAL; + } + ret = vfd_ops->set_vf_bw_share(pdev, vf_id, bw_share); + return ret ? ret : count; +} + +/** + * pf_qos_apply_store - handler for pf qos apply store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t pf_qos_apply_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + int ret, apply; + struct pci_dev *pdev; + + if (!vfd_ops->set_pf_qos_apply) + return -EOPNOTSUPP; + + ret = __get_pf_pdev(kobj, &pdev); + if (ret) + return ret; + + ret = kstrtoint(buff, 10, &apply); + if (ret) { + dev_err(&pdev->dev, + "Invalid input\n"); + return ret; + } + + if (apply != 1) + return -EINVAL; + + ret = vfd_ops->set_pf_qos_apply(pdev); + + return ret ? ret : count; +} + +/** + * pf_ingress_mirror_show - handler for PF ingress mirror show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t pf_ingress_mirror_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int ret, data; + + if (!vfd_ops->get_pf_ingress_mirror) + return -EOPNOTSUPP; + + ret = __get_pf_pdev(kobj, &pdev); + if (ret) + return ret; + + ret = vfd_ops->get_pf_ingress_mirror(pdev, &data); + if (ret < 0) + return ret; + + if (data == VFD_INGRESS_MIRROR_OFF) + ret = scnprintf(buff, PAGE_SIZE, "off\n"); + else + ret = scnprintf(buff, PAGE_SIZE, "%u\n", data); + + return ret; +} + +/** + * pf_ingress_mirror_store - handler for pf ingress mirror store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t pf_ingress_mirror_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + int data_new, data_old; + struct pci_dev *pdev; + int ret; + + if (!vfd_ops->set_pf_ingress_mirror || !vfd_ops->get_pf_ingress_mirror) + return -EOPNOTSUPP; + + ret = __get_pf_pdev(kobj, &pdev); + if (ret) + return ret; + + ret = vfd_ops->get_pf_ingress_mirror(pdev, &data_old); + if (ret < 0) + return ret; + + ret = __parse_egress_ingress_input(pdev, buff, "ingress_mirror", + &data_new, &data_old); + if (ret) + return ret; + + if (data_new != data_old) + ret = vfd_ops->set_pf_ingress_mirror(pdev, data_new); + + return ret ? ret : count; +} + +/** + * pf_egress_mirror_show - handler for PF egress mirror show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t pf_egress_mirror_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int ret, data; + + if (!vfd_ops->get_pf_egress_mirror) + return -EOPNOTSUPP; + + ret = __get_pf_pdev(kobj, &pdev); + if (ret) + return ret; + + ret = vfd_ops->get_pf_egress_mirror(pdev, &data); + if (ret < 0) + return ret; + + if (data == VFD_EGRESS_MIRROR_OFF) + ret = scnprintf(buff, PAGE_SIZE, "off\n"); + else + ret = scnprintf(buff, PAGE_SIZE, "%u\n", data); + + return ret; +} + +/** + * pf_egress_mirror_store - handler for pf egress mirror store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t pf_egress_mirror_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + int data_new, data_old; + struct pci_dev *pdev; + int ret; + + if (!vfd_ops->set_pf_egress_mirror || !vfd_ops->get_pf_egress_mirror) + return -EOPNOTSUPP; + + ret = __get_pf_pdev(kobj, &pdev); + if (ret) + return ret; + + ret = vfd_ops->get_pf_egress_mirror(pdev, &data_old); + if (ret < 0) + return ret; + + ret = __parse_egress_ingress_input(pdev, buff, "egress_mirror", + &data_new, &data_old); + if (ret) + return ret; + + if (data_new != data_old) + ret = vfd_ops->set_pf_egress_mirror(pdev, data_new); + + return ret ? ret : count; +} + +/** + * pf_tpid_show - handler for pf tpid show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t pf_tpid_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + u16 data; + int ret; + + if (!vfd_ops->get_pf_tpid) + return -EOPNOTSUPP; + + ret = __get_pf_pdev(kobj, &pdev); + if (ret) + return ret; + + ret = vfd_ops->get_pf_tpid(pdev, &data); + if (ret < 0) + return ret; + + ret = scnprintf(buff, PAGE_SIZE, "%x\n", data); + + return ret; +} + +/** + * pf_tpid_store - handler for pf tpid store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t pf_tpid_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + struct pci_dev *pdev; + u16 data; + int ret; + + if (!vfd_ops->set_pf_tpid) + return -EOPNOTSUPP; + + ret = __get_pf_pdev(kobj, &pdev); + if (ret) + return ret; + + ret = kstrtou16(buff, 16, &data); + if (ret) { + dev_err(&pdev->dev, "Invalid input\n"); + return ret; + } + + ret = vfd_ops->set_pf_tpid(pdev, data); + + return ret ? ret : count; +} + +/** + * vfd_num_queues_show - handler for num_queues show function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer for data + **/ +static ssize_t vfd_num_queues_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + struct pci_dev *pdev; + int vf_id, ret; + int data; + + if (!vfd_ops->get_num_queues) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_num_queues(pdev, vf_id, &data); + if (ret) + return ret; + + ret = scnprintf(buff, PAGE_SIZE, "%d\n", data); + + return ret; +} + +/** + * vfd_num_queues_store - handler for num_queues store function + * @kobj: kobject being called + * @attr: struct kobj_attribute + * @buff: buffer with input data + * @count: size of buff + **/ +static ssize_t vfd_num_queues_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buff, size_t count) +{ + int data_new, data_old; + struct pci_dev *pdev; + int vf_id, ret; + + if (!vfd_ops->set_num_queues) + return -EOPNOTSUPP; + + ret = __get_pdev_and_vfid(kobj, &pdev, &vf_id); + if (ret) + return ret; + + ret = vfd_ops->get_num_queues(pdev, vf_id, &data_old); + if (ret) + return ret; + + ret = kstrtoint(buff, 10, &data_new); + if (ret) { + dev_err(&pdev->dev, "Invalid input\n"); + return ret; + } + + if (data_new < 1) { + dev_err(&pdev->dev, "VF queue count must be at least 1\n"); + return -EINVAL; + } + + if (data_new != data_old) + ret = vfd_ops->set_num_queues(pdev, vf_id, data_new); + + return ret ? ret : count; +} + +static struct kobj_attribute trunk_attribute = + __ATTR(trunk, 0644, vfd_trunk_show, vfd_trunk_store); +static struct kobj_attribute vlan_mirror_attribute = + __ATTR(vlan_mirror, 0644, vfd_vlan_mirror_show, vfd_vlan_mirror_store); +static struct kobj_attribute egress_mirror_attribute = + __ATTR(egress_mirror, 0644, + vfd_egress_mirror_show, vfd_egress_mirror_store); +static struct kobj_attribute ingress_mirror_attribute = + __ATTR(ingress_mirror, 0644, + vfd_ingress_mirror_show, vfd_ingress_mirror_store); +static struct kobj_attribute mac_anti_spoof_attribute = + __ATTR(mac_anti_spoof, 0644, + vfd_mac_anti_spoof_show, vfd_mac_anti_spoof_store); +static struct kobj_attribute vlan_anti_spoof_attribute = + __ATTR(vlan_anti_spoof, 0644, + vfd_vlan_anti_spoof_show, vfd_vlan_anti_spoof_store); +static struct kobj_attribute allow_untagged_attribute = + __ATTR(allow_untagged, 0644, + vfd_allow_untagged_show, vfd_allow_untagged_store); +static struct kobj_attribute loopback_attribute = + __ATTR(loopback, 0644, vfd_loopback_show, vfd_loopback_store); +static struct kobj_attribute mac_attribute = + __ATTR(mac, 0644, vfd_mac_show, vfd_mac_store); +static struct kobj_attribute mac_list_attribute = + __ATTR(mac_list, 0644, vfd_mac_list_show, vfd_mac_list_store); +static struct kobj_attribute promisc_attribute = + __ATTR(promisc, 0644, vfd_promisc_show, vfd_promisc_store); +static struct kobj_attribute vlan_strip_attribute = + __ATTR(vlan_strip, 0644, vfd_vlan_strip_show, vfd_vlan_strip_store); +static struct kobj_attribute link_state_attribute = + __ATTR(link_state, 0644, vfd_link_state_show, vfd_link_state_store); +static struct kobj_attribute max_tx_rate_attribute = + __ATTR(max_tx_rate, 0644, vfd_max_tx_rate_show, vfd_max_tx_rate_store); +static struct kobj_attribute min_tx_rate_attribute = + __ATTR(min_tx_rate, 0644, vfd_min_tx_rate_show, vfd_min_tx_rate_store); +static struct kobj_attribute spoofcheck_attribute = + __ATTR(spoofcheck, 0644, vfd_spoofcheck_show, vfd_spoofcheck_store); +static struct kobj_attribute trust_attribute = + __ATTR(trust, 0644, vfd_trust_show, vfd_trust_store); +static struct kobj_attribute reset_stats_attribute = + __ATTR(reset_stats, 0200, NULL, vfd_reset_stats_store); +static struct kobj_attribute enable_attribute = + __ATTR(enable, 0644, vfd_enable_show, vfd_enable_store); +static struct kobj_attribute num_queues_attribute = + __ATTR(num_queues, 0644, vfd_num_queues_show, vfd_num_queues_store); + +static struct attribute *s_attrs[] = { + &trunk_attribute.attr, + &vlan_mirror_attribute.attr, + &egress_mirror_attribute.attr, + &ingress_mirror_attribute.attr, + &mac_anti_spoof_attribute.attr, + &vlan_anti_spoof_attribute.attr, + &allow_untagged_attribute.attr, + &loopback_attribute.attr, + &mac_attribute.attr, + &mac_list_attribute.attr, + &promisc_attribute.attr, + &vlan_strip_attribute.attr, + &link_state_attribute.attr, + &max_tx_rate_attribute.attr, + &min_tx_rate_attribute.attr, + &spoofcheck_attribute.attr, + &trust_attribute.attr, + &reset_stats_attribute.attr, + &enable_attribute.attr, + &num_queues_attribute.attr, + NULL, +}; + +static struct attribute_group vfd_group = { + .attrs = s_attrs, +}; + +static struct kobj_attribute rx_bytes_attribute = + __ATTR(rx_bytes, 0444, vfd_rx_bytes_show, NULL); +static struct kobj_attribute rx_dropped_attribute = + __ATTR(rx_dropped, 0444, vfd_rx_dropped_show, NULL); +static struct kobj_attribute rx_packets_attribute = + __ATTR(rx_packets, 0444, vfd_rx_packets_show, NULL); +static struct kobj_attribute tx_bytes_attribute = + __ATTR(tx_bytes, 0444, vfd_tx_bytes_show, NULL); +static struct kobj_attribute tx_dropped_attribute = + __ATTR(tx_dropped, 0444, vfd_tx_dropped_show, NULL); +static struct kobj_attribute tx_packets_attribute = + __ATTR(tx_packets, 0444, vfd_tx_packets_show, NULL); +static struct kobj_attribute tx_spoofed_attribute = + __ATTR(tx_spoofed, 0444, vfd_tx_spoofed_show, NULL); +static struct kobj_attribute tx_errors_attribute = + __ATTR(tx_errors, 0444, vfd_tx_errors_show, NULL); + +static struct attribute *stats_attrs[] = { + &rx_bytes_attribute.attr, + &rx_dropped_attribute.attr, + &rx_packets_attribute.attr, + &tx_bytes_attribute.attr, + &tx_dropped_attribute.attr, + &tx_packets_attribute.attr, + &tx_spoofed_attribute.attr, + &tx_errors_attribute.attr, + NULL, +}; + +static struct attribute_group stats_group = { + .name = "stats", + .attrs = stats_attrs, +}; + +static struct kobj_attribute share_attribute = + __ATTR(share, 0644, qos_share_show, qos_share_store); + +static struct attribute *qos_attrs[] = { + &share_attribute.attr, + NULL, +}; + +static struct attribute_group qos_group = { + .name = "qos", + .attrs = qos_attrs, +}; + +static struct kobj_attribute apply_attribute = + __ATTR(apply, 0200, NULL, pf_qos_apply_store); + +static struct attribute *pf_qos_attrs[] = { + &apply_attribute.attr, + NULL, +}; + +static struct attribute_group pf_qos_group = { + .name = "qos", + .attrs = pf_qos_attrs, +}; + +static struct kobj_attribute pf_ingress_mirror_attribute = + __ATTR(ingress_mirror, 0644, pf_ingress_mirror_show, pf_ingress_mirror_store); +static struct kobj_attribute pf_egress_mirror_attribute = + __ATTR(egress_mirror, 0644, pf_egress_mirror_show, pf_egress_mirror_store); +static struct kobj_attribute pf_tpid_attribute = + __ATTR(tpid, 0644, pf_tpid_show, pf_tpid_store); + +static struct attribute *pf_attrs[] = { + &pf_ingress_mirror_attribute.attr, + &pf_egress_mirror_attribute.attr, + &pf_tpid_attribute.attr, + NULL, +}; + +static struct attribute_group pf_attr_group = { + .attrs = pf_attrs, +}; + +/** + * create_vfs_sysfs - create sysfs hierarchy for VF + * @pdev: PCI device information struct + * @vfd_obj: VF-d kobjects information struct + * + * Creates a kobject for Virtual Function and assigns attributes to it. + **/ +static int create_vfs_sysfs(struct pci_dev *pdev, struct vfd_objects *vfd_obj) +{ + struct kobject *vf_kobj; + char kname[4]; + int ret, i; + + for (i = 0; i < vfd_obj->num_vfs; i++) { + int length = snprintf(kname, sizeof(kname), "%d", i); + + if (length >= sizeof(kname)) { + dev_err(&pdev->dev, + "cannot request %d vfs, try again with smaller number of vfs\n", + i); + --i; + ret = -EINVAL; + goto err_vfs_sysfs; + } + + vf_kobj = kobject_create_and_add(kname, vfd_obj->sriov_kobj); + if (!vf_kobj) { + dev_err(&pdev->dev, + "failed to create VF kobj: %s\n", kname); + i--; + ret = -ENOMEM; + goto err_vfs_sysfs; + } + dev_info(&pdev->dev, "created VF %s sysfs", vf_kobj->name); + vfd_obj->vf_kobj[i] = vf_kobj; + + /* create VF sys attr */ + ret = sysfs_create_group(vfd_obj->vf_kobj[i], &vfd_group); + if (ret) { + dev_err(&pdev->dev, "failed to create VF sys attribute: %d", i); + goto err_vfs_sysfs; + } + + /* create VF stats sys attr */ + ret = sysfs_create_group(vfd_obj->vf_kobj[i], &stats_group); + if (ret) { + dev_err(&pdev->dev, "failed to create VF stats attribute: %d", i); + goto err_vfs_sysfs; + } + + /* create VF qos sys attr */ + ret = sysfs_create_group(vfd_obj->vf_kobj[i], &qos_group); + if (ret) { + dev_err(&pdev->dev, "failed to create VF qos attribute: %d", i); + goto err_vfs_sysfs; + } + } + + return 0; + +err_vfs_sysfs: + for (; i >= 0; i--) + kobject_put(vfd_obj->vf_kobj[i]); + return ret; +} + +/** + * create_vfd_sysfs - create sysfs hierarchy used by VF-d + * @pdev: PCI device information struct + * @num_alloc_vfs: number of VFs to allocate + * + * If the kobjects were not able to be created, NULL will be returned. + **/ +struct vfd_objects *create_vfd_sysfs(struct pci_dev *pdev, int num_alloc_vfs) +{ + struct vfd_objects *vfd_obj; + int ret; + + vfd_obj = kzalloc(sizeof(*vfd_obj) + + sizeof(struct kobject *)*num_alloc_vfs, GFP_KERNEL); + if (!vfd_obj) + return NULL; + + vfd_obj->num_vfs = num_alloc_vfs; + + vfd_obj->sriov_kobj = kobject_create_and_add("sriov", &pdev->dev.kobj); + if (!vfd_obj->sriov_kobj) + goto err_sysfs; + + dev_info(&pdev->dev, "created %s sysfs", vfd_obj->sriov_kobj->name); + + ret = create_vfs_sysfs(pdev, vfd_obj); + if (ret) + goto err_sysfs; + + /* create PF qos sys attr */ + ret = sysfs_create_group(vfd_obj->sriov_kobj, &pf_qos_group); + if (ret) { + dev_err(&pdev->dev, "failed to create PF qos sys attribute"); + goto err_sysfs; + } + + /* create PF attrs */ + ret = sysfs_create_group(vfd_obj->sriov_kobj, &pf_attr_group); + if (ret) { + dev_err(&pdev->dev, "failed to create PF attr sys attribute"); + goto err_sysfs; + } + return vfd_obj; + +err_sysfs: + kobject_put(vfd_obj->sriov_kobj); + kfree(vfd_obj); + return NULL; +} + +/** + * destroy_vfd_sysfs - destroy sysfs hierarchy used by VF-d + * @pdev: PCI device information struct + * @vfd_obj: VF-d kobjects information struct + **/ +void destroy_vfd_sysfs(struct pci_dev *pdev, struct vfd_objects *vfd_obj) +{ + int i; + + for (i = 0; i < vfd_obj->num_vfs; i++) { + dev_info(&pdev->dev, "deleting VF %s sysfs", + vfd_obj->vf_kobj[i]->name); + kobject_put(vfd_obj->vf_kobj[i]); + } + + dev_info(&pdev->dev, "deleting %s sysfs", vfd_obj->sriov_kobj->name); + kobject_put(vfd_obj->sriov_kobj); + kfree(vfd_obj); +} diff --git a/drivers/net/ethernet/intel/i40e/kcompat_vfd.h b/drivers/net/ethernet/intel/i40e/kcompat_vfd.h new file mode 100644 index 000000000..894cd2607 --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/kcompat_vfd.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ + +#ifndef _KCOMPAT_VFD_H_ +#define _KCOMPAT_VFD_H_ + +#define VFD_PROMISC_OFF 0x00 +#define VFD_PROMISC_UNICAST 0x01 +#define VFD_PROMISC_MULTICAST 0x02 + +#define VFD_LINKSTATE_OFF 0x00 +#define VFD_LINKSTATE_ON 0x01 +#define VFD_LINKSTATE_AUTO 0x02 + +#define VFD_EGRESS_MIRROR_OFF -1 +#define VFD_INGRESS_MIRROR_OFF -1 + +/** + * struct vfd_objects - VF-d kobjects information struct + * @num_vfs: number of VFs allocated + * @sriov_kobj: pointer to the top sriov kobject + * @vf_kobj: array of pointer to each VF's kobjects + */ +struct vfd_objects { + int num_vfs; + struct kobject *sriov_kobj; + struct kobject *vf_kobj[0]; +}; + +struct vfd_macaddr { + u8 mac[ETH_ALEN]; + struct list_head list; +}; + +#define VFD_LINK_SPEED_2_5GB_SHIFT 0x0 +#define VFD_LINK_SPEED_100MB_SHIFT 0x1 +#define VFD_LINK_SPEED_1GB_SHIFT 0x2 +#define VFD_LINK_SPEED_10GB_SHIFT 0x3 +#define VFD_LINK_SPEED_40GB_SHIFT 0x4 +#define VFD_LINK_SPEED_20GB_SHIFT 0x5 +#define VFD_LINK_SPEED_25GB_SHIFT 0x6 +#define VFD_LINK_SPEED_5GB_SHIFT 0x7 + + +enum vfd_link_speed { + VFD_LINK_SPEED_UNKNOWN = 0, + VFD_LINK_SPEED_100MB = BIT(VFD_LINK_SPEED_100MB_SHIFT), + VFD_LINK_SPEED_1GB = BIT(VFD_LINK_SPEED_1GB_SHIFT), + VFD_LINK_SPEED_2_5GB = BIT(VFD_LINK_SPEED_2_5GB_SHIFT), + VFD_LINK_SPEED_5GB = BIT(VFD_LINK_SPEED_5GB_SHIFT), + VFD_LINK_SPEED_10GB = BIT(VFD_LINK_SPEED_10GB_SHIFT), + VFD_LINK_SPEED_40GB = BIT(VFD_LINK_SPEED_40GB_SHIFT), + VFD_LINK_SPEED_20GB = BIT(VFD_LINK_SPEED_20GB_SHIFT), + VFD_LINK_SPEED_25GB = BIT(VFD_LINK_SPEED_25GB_SHIFT), +}; + +struct vfd_ops { + int (*get_trunk)(struct pci_dev *pdev, int vf_id, unsigned long *buff); + int (*set_trunk)(struct pci_dev *pdev, int vf_id, + const unsigned long *buff); + int (*get_vlan_mirror)(struct pci_dev *pdev, int vf_id, + unsigned long *buff); + int (*set_vlan_mirror)(struct pci_dev *pdev, int vf_id, + const unsigned long *buff); + int (*get_egress_mirror)(struct pci_dev *pdev, int vf_id, int *data); + int (*set_egress_mirror)(struct pci_dev *pdev, int vf_id, + const int data); + int (*get_ingress_mirror)(struct pci_dev *pdev, int vf_id, int *data); + int (*set_ingress_mirror)(struct pci_dev *pdev, int vf_id, + const int data); + int (*get_mac_anti_spoof)(struct pci_dev *pdev, int vf_id, bool *data); + int (*set_mac_anti_spoof)(struct pci_dev *pdev, int vf_id, + const bool data); + int (*get_vlan_anti_spoof)(struct pci_dev *pdev, int vf_id, bool *data); + int (*set_vlan_anti_spoof)(struct pci_dev *pdev, int vf_id, + const bool data); + int (*get_allow_untagged)(struct pci_dev *pdev, int vf_id, bool *data); + int (*set_allow_untagged)(struct pci_dev *pdev, int vf_id, + const bool data); + int (*get_loopback)(struct pci_dev *pdev, int vf_id, bool *data); + int (*set_loopback)(struct pci_dev *pdev, int vf_id, const bool data); + int (*get_mac)(struct pci_dev *pdev, int vf_id, u8 *macaddr); + int (*set_mac)(struct pci_dev *pdev, int vf_id, const u8 *macaddr); + int (*get_mac_list)(struct pci_dev *pdev, int vf_id, + struct list_head *mac_list); + int (*add_macs_to_list)(struct pci_dev *pdev, int vf_id, + struct list_head *mac_list); + int (*rem_macs_from_list)(struct pci_dev *pdev, int vf_id, + struct list_head *mac_list); + int (*get_promisc)(struct pci_dev *pdev, int vf_id, u8 *data); + int (*set_promisc)(struct pci_dev *pdev, int vf_id, const u8 data); + int (*get_vlan_strip)(struct pci_dev *pdev, int vf_id, bool *data); + int (*set_vlan_strip)(struct pci_dev *pdev, int vf_id, const bool data); + int (*get_link_state)(struct pci_dev *pdev, int vf_id, bool *enabled, + enum vfd_link_speed *link_speed); + int (*set_link_state)(struct pci_dev *pdev, int vf_id, const u8 data); + int (*get_max_tx_rate)(struct pci_dev *pdev, int vf_id, + unsigned int *max_tx_rate); + int (*set_max_tx_rate)(struct pci_dev *pdev, int vf_id, + unsigned int *max_tx_rate); + int (*get_min_tx_rate)(struct kobject *, + struct kobj_attribute *, char *); + int (*set_min_tx_rate)(struct kobject *, struct kobj_attribute *, + const char *, size_t); + int (*get_spoofcheck)(struct kobject *, + struct kobj_attribute *, char *); + int (*set_spoofcheck)(struct kobject *, struct kobj_attribute *, + const char *, size_t); + int (*get_trust)(struct kobject *, + struct kobj_attribute *, char *); + int (*set_trust)(struct kobject *, struct kobj_attribute *, + const char *, size_t); + int (*get_vf_enable)(struct pci_dev *pdev, int vf_id, bool *data); + int (*set_vf_enable)(struct pci_dev *pdev, int vf_id, const bool data); + int (*get_rx_bytes) (struct pci_dev *pdev, int vf_id, u64 *data); + int (*get_rx_dropped)(struct pci_dev *pdev, int vf_id, u64 *data); + int (*get_rx_packets)(struct pci_dev *pdev, int vf_id, u64 *data); + int (*get_tx_bytes) (struct pci_dev *pdev, int vf_id, u64 *data); + int (*get_tx_dropped)(struct pci_dev *pdev, int vf_id, u64 *data); + int (*get_tx_packets)(struct pci_dev *pdev, int vf_id, u64 *data); + int (*get_tx_spoofed)(struct pci_dev *pdev, int vf_id, u64 *data); + int (*get_tx_errors)(struct pci_dev *pdev, int vf_id, u64 *data); + int (*reset_stats)(struct pci_dev *pdev, int vf_id); + int (*set_vf_bw_share)(struct pci_dev *pdev, int vf_id, u8 bw_share); + int (*get_vf_bw_share)(struct pci_dev *pdev, int vf_id, u8 *bw_share); + int (*set_pf_qos_apply)(struct pci_dev *pdev); + int (*get_pf_ingress_mirror)(struct pci_dev *pdev, int *data); + int (*set_pf_ingress_mirror)(struct pci_dev *pdev, const int data); + int (*get_pf_egress_mirror)(struct pci_dev *pdev, int *data); + int (*set_pf_egress_mirror)(struct pci_dev *pdev, const int data); + int (*get_pf_tpid)(struct pci_dev *pdev, u16 *data); + int (*set_pf_tpid)(struct pci_dev *pdev, const u16 data); + int (*get_num_queues)(struct pci_dev *pdev, int vf_id, int *num_queues); + int (*set_num_queues)(struct pci_dev *pdev, int vf_id, const int num_queues); + int (*get_trust_state)(struct pci_dev *pdev, int vf_id, bool *data); + int (*set_trust_state)(struct pci_dev *pdev, int vf_id, bool data); +}; + +extern const struct vfd_ops *vfd_ops; + +#endif /* _KCOMPAT_VFD_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/virtchnl.h b/drivers/net/ethernet/intel/i40e/virtchnl.h new file mode 100644 index 000000000..c1b1ab3b7 --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/virtchnl.h @@ -0,0 +1,949 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2013 - 2020 Intel Corporation. */ + +#ifndef _VIRTCHNL_H_ +#define _VIRTCHNL_H_ + +/* Description: + * This header file describes the VF-PF communication protocol used + * by the drivers for all devices starting from our 40G product line + * + * Admin queue buffer usage: + * desc->opcode is always aqc_opc_send_msg_to_pf + * flags, retval, datalen, and data addr are all used normally. + * The Firmware copies the cookie fields when sending messages between the + * PF and VF, but uses all other fields internally. Due to this limitation, + * we must send all messages as "indirect", i.e. using an external buffer. + * + * All the VSI indexes are relative to the VF. Each VF can have maximum of + * three VSIs. All the queue indexes are relative to the VSI. Each VF can + * have a maximum of sixteen queues for all of its VSIs. + * + * The PF is required to return a status code in v_retval for all messages + * except RESET_VF, which does not require any response. The return value + * is of status_code type, defined in the shared type.h. + * + * In general, VF driver initialization should roughly follow the order of + * these opcodes. The VF driver must first validate the API version of the + * PF driver, then request a reset, then get resources, then configure + * queues and interrupts. After these operations are complete, the VF + * driver may start its queues, optionally add MAC and VLAN filters, and + * process traffic. + */ + +/* START GENERIC DEFINES + * Need to ensure the following enums and defines hold the same meaning and + * value in current and future projects + */ + +/* Error Codes */ +enum virtchnl_status_code { + VIRTCHNL_STATUS_SUCCESS = 0, + VIRTCHNL_STATUS_ERR_PARAM = -5, + VIRTCHNL_STATUS_ERR_NO_MEMORY = -18, + VIRTCHNL_STATUS_ERR_OPCODE_MISMATCH = -38, + VIRTCHNL_STATUS_ERR_CQP_COMPL_ERROR = -39, + VIRTCHNL_STATUS_ERR_INVALID_VF_ID = -40, + VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR = -53, + VIRTCHNL_STATUS_ERR_NOT_SUPPORTED = -64, +}; + +/* Backward compatibility */ +#define VIRTCHNL_ERR_PARAM VIRTCHNL_STATUS_ERR_PARAM +#define VIRTCHNL_STATUS_NOT_SUPPORTED VIRTCHNL_STATUS_ERR_NOT_SUPPORTED + +#define VIRTCHNL_LINK_SPEED_2_5GB_SHIFT 0x0 +#define VIRTCHNL_LINK_SPEED_100MB_SHIFT 0x1 +#define VIRTCHNL_LINK_SPEED_1000MB_SHIFT 0x2 +#define VIRTCHNL_LINK_SPEED_10GB_SHIFT 0x3 +#define VIRTCHNL_LINK_SPEED_40GB_SHIFT 0x4 +#define VIRTCHNL_LINK_SPEED_20GB_SHIFT 0x5 +#define VIRTCHNL_LINK_SPEED_25GB_SHIFT 0x6 +#define VIRTCHNL_LINK_SPEED_5GB_SHIFT 0x7 + +enum virtchnl_link_speed { + VIRTCHNL_LINK_SPEED_UNKNOWN = 0, + VIRTCHNL_LINK_SPEED_100MB = BIT(VIRTCHNL_LINK_SPEED_100MB_SHIFT), + VIRTCHNL_LINK_SPEED_1GB = BIT(VIRTCHNL_LINK_SPEED_1000MB_SHIFT), + VIRTCHNL_LINK_SPEED_10GB = BIT(VIRTCHNL_LINK_SPEED_10GB_SHIFT), + VIRTCHNL_LINK_SPEED_40GB = BIT(VIRTCHNL_LINK_SPEED_40GB_SHIFT), + VIRTCHNL_LINK_SPEED_20GB = BIT(VIRTCHNL_LINK_SPEED_20GB_SHIFT), + VIRTCHNL_LINK_SPEED_25GB = BIT(VIRTCHNL_LINK_SPEED_25GB_SHIFT), + VIRTCHNL_LINK_SPEED_2_5GB = BIT(VIRTCHNL_LINK_SPEED_2_5GB_SHIFT), + VIRTCHNL_LINK_SPEED_5GB = BIT(VIRTCHNL_LINK_SPEED_5GB_SHIFT), +}; + +/* for hsplit_0 field of Rx HMC context */ +/* deprecated with AVF 1.0 */ +enum virtchnl_rx_hsplit { + VIRTCHNL_RX_HSPLIT_NO_SPLIT = 0, + VIRTCHNL_RX_HSPLIT_SPLIT_L2 = 1, + VIRTCHNL_RX_HSPLIT_SPLIT_IP = 2, + VIRTCHNL_RX_HSPLIT_SPLIT_TCP_UDP = 4, + VIRTCHNL_RX_HSPLIT_SPLIT_SCTP = 8, +}; + +/* END GENERIC DEFINES */ + +/* Opcodes for VF-PF communication. These are placed in the v_opcode field + * of the virtchnl_msg structure. + */ +enum virtchnl_ops { +/* The PF sends status change events to VFs using + * the VIRTCHNL_OP_EVENT opcode. + * VFs send requests to the PF using the other ops. + * Use of "advanced opcode" features must be negotiated as part of capabilities + * exchange and are not considered part of base mode feature set. + */ + VIRTCHNL_OP_UNKNOWN = 0, + VIRTCHNL_OP_VERSION = 1, /* must ALWAYS be 1 */ + VIRTCHNL_OP_RESET_VF = 2, + VIRTCHNL_OP_GET_VF_RESOURCES = 3, + VIRTCHNL_OP_CONFIG_TX_QUEUE = 4, + VIRTCHNL_OP_CONFIG_RX_QUEUE = 5, + VIRTCHNL_OP_CONFIG_VSI_QUEUES = 6, + VIRTCHNL_OP_CONFIG_IRQ_MAP = 7, + VIRTCHNL_OP_ENABLE_QUEUES = 8, + VIRTCHNL_OP_DISABLE_QUEUES = 9, + VIRTCHNL_OP_ADD_ETH_ADDR = 10, + VIRTCHNL_OP_DEL_ETH_ADDR = 11, + VIRTCHNL_OP_ADD_VLAN = 12, + VIRTCHNL_OP_DEL_VLAN = 13, + VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE = 14, + VIRTCHNL_OP_GET_STATS = 15, + VIRTCHNL_OP_RSVD = 16, + VIRTCHNL_OP_EVENT = 17, /* must ALWAYS be 17 */ + /* opcode 19 is reserved */ + VIRTCHNL_OP_IWARP = 20, /* advanced opcode */ + VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP = 21, /* advanced opcode */ + VIRTCHNL_OP_RELEASE_IWARP_IRQ_MAP = 22, /* advanced opcode */ + VIRTCHNL_OP_CONFIG_RSS_KEY = 23, + VIRTCHNL_OP_CONFIG_RSS_LUT = 24, + VIRTCHNL_OP_GET_RSS_HENA_CAPS = 25, + VIRTCHNL_OP_SET_RSS_HENA = 26, + VIRTCHNL_OP_ENABLE_VLAN_STRIPPING = 27, + VIRTCHNL_OP_DISABLE_VLAN_STRIPPING = 28, + VIRTCHNL_OP_REQUEST_QUEUES = 29, + VIRTCHNL_OP_ENABLE_CHANNELS = 30, + VIRTCHNL_OP_DISABLE_CHANNELS = 31, + VIRTCHNL_OP_ADD_CLOUD_FILTER = 32, + VIRTCHNL_OP_DEL_CLOUD_FILTER = 33, + /* opcodes 34, 35, 36, 37 and 38 are reserved */ +}; + +/* These macros are used to generate compilation errors if a structure/union + * is not exactly the correct length. It gives a divide by zero error if the + * structure/union is not of the correct size, otherwise it creates an enum + * that is never used. + */ +#define VIRTCHNL_CHECK_STRUCT_LEN(n, X) enum virtchnl_static_assert_enum_##X \ + { virtchnl_static_assert_##X = (n)/((sizeof(struct X) == (n)) ? 1 : 0) } +#define VIRTCHNL_CHECK_UNION_LEN(n, X) enum virtchnl_static_asset_enum_##X \ + { virtchnl_static_assert_##X = (n)/((sizeof(union X) == (n)) ? 1 : 0) } + +/* Virtual channel message descriptor. This overlays the admin queue + * descriptor. All other data is passed in external buffers. + */ + +struct virtchnl_msg { + u8 pad[8]; /* AQ flags/opcode/len/retval fields */ + enum virtchnl_ops v_opcode; /* avoid confusion with desc->opcode */ + enum virtchnl_status_code v_retval; /* ditto for desc->retval */ + u32 vfid; /* used by PF when sending to VF */ +}; + +VIRTCHNL_CHECK_STRUCT_LEN(20, virtchnl_msg); + +/* Message descriptions and data structures. */ + +/* VIRTCHNL_OP_VERSION + * VF posts its version number to the PF. PF responds with its version number + * in the same format, along with a return code. + * Reply from PF has its major/minor versions also in param0 and param1. + * If there is a major version mismatch, then the VF cannot operate. + * If there is a minor version mismatch, then the VF can operate but should + * add a warning to the system log. + * + * This enum element MUST always be specified as == 1, regardless of other + * changes in the API. The PF must always respond to this message without + * error regardless of version mismatch. + */ +#define VIRTCHNL_VERSION_MAJOR 1 +#define VIRTCHNL_VERSION_MINOR 1 +#define VIRTCHNL_VERSION_MINOR_NO_VF_CAPS 0 + +struct virtchnl_version_info { + u32 major; + u32 minor; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl_version_info); + +#define VF_IS_V10(_v) (((_v)->major == 1) && ((_v)->minor == 0)) +#define VF_IS_V11(_ver) (((_ver)->major == 1) && ((_ver)->minor == 1)) + +/* VIRTCHNL_OP_RESET_VF + * VF sends this request to PF with no parameters + * PF does NOT respond! VF driver must delay then poll VFGEN_RSTAT register + * until reset completion is indicated. The admin queue must be reinitialized + * after this operation. + * + * When reset is complete, PF must ensure that all queues in all VSIs associated + * with the VF are stopped, all queue configurations in the HMC are set to 0, + * and all MAC and VLAN filters (except the default MAC address) on all VSIs + * are cleared. + */ + +/* VSI types that use VIRTCHNL interface for VF-PF communication. VSI_SRIOV + * vsi_type should always be 6 for backward compatibility. Add other fields + * as needed. + */ +enum virtchnl_vsi_type { + VIRTCHNL_VSI_TYPE_INVALID = 0, + VIRTCHNL_VSI_SRIOV = 6, +}; + +/* VIRTCHNL_OP_GET_VF_RESOURCES + * Version 1.0 VF sends this request to PF with no parameters + * Version 1.1 VF sends this request to PF with u32 bitmap of its capabilities + * PF responds with an indirect message containing + * virtchnl_vf_resource and one or more + * virtchnl_vsi_resource structures. + */ + +struct virtchnl_vsi_resource { + u16 vsi_id; + u16 num_queue_pairs; + enum virtchnl_vsi_type vsi_type; + u16 qset_handle; + u8 default_mac_addr[ETH_ALEN]; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_vsi_resource); + +/* VF capability flags + * VIRTCHNL_VF_OFFLOAD_L2 flag is inclusive of base mode L2 offloads including + * TX/RX Checksum offloading and TSO for non-tunnelled packets. + */ +#define VIRTCHNL_VF_OFFLOAD_L2 0x00000001 +#define VIRTCHNL_VF_OFFLOAD_IWARP 0x00000002 +#define VIRTCHNL_VF_OFFLOAD_RSVD 0x00000004 +#define VIRTCHNL_VF_OFFLOAD_RSS_AQ 0x00000008 +#define VIRTCHNL_VF_OFFLOAD_RSS_REG 0x00000010 +#define VIRTCHNL_VF_OFFLOAD_WB_ON_ITR 0x00000020 +#define VIRTCHNL_VF_OFFLOAD_REQ_QUEUES 0x00000040 +#define VIRTCHNL_VF_OFFLOAD_CRC 0x00000080 +#define VIRTCHNL_VF_OFFLOAD_VLAN 0x00010000 +#define VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000 +#define VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 0x00040000 +#define VIRTCHNL_VF_OFFLOAD_RSS_PF 0X00080000 +#define VIRTCHNL_VF_OFFLOAD_ENCAP 0X00100000 +#define VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM 0X00200000 +#define VIRTCHNL_VF_OFFLOAD_RX_ENCAP_CSUM 0X00400000 +#define VIRTCHNL_VF_OFFLOAD_ADQ 0X00800000 +#define VIRTCHNL_VF_OFFLOAD_ADQ_V2 0X01000000 +#define VIRTCHNL_VF_OFFLOAD_USO 0X02000000 + /* 0X80000000 is reserved */ + +/* Define below the capability flags that are not offloads */ +#define VIRTCHNL_VF_CAP_ADV_LINK_SPEED 0x00000080 +#define VF_BASE_MODE_OFFLOADS (VIRTCHNL_VF_OFFLOAD_L2 | \ + VIRTCHNL_VF_OFFLOAD_VLAN | \ + VIRTCHNL_VF_OFFLOAD_RSS_PF) + +struct virtchnl_vf_resource { + u16 num_vsis; + u16 num_queue_pairs; + u16 max_vectors; + u16 max_mtu; + + u32 vf_cap_flags; + u32 rss_key_size; + u32 rss_lut_size; + + struct virtchnl_vsi_resource vsi_res[1]; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(36, virtchnl_vf_resource); + +/* VIRTCHNL_OP_CONFIG_TX_QUEUE + * VF sends this message to set up parameters for one TX queue. + * External data buffer contains one instance of virtchnl_txq_info. + * PF configures requested queue and returns a status code. + */ + +/* Tx queue config info */ +struct virtchnl_txq_info { + u16 vsi_id; + u16 queue_id; + u16 ring_len; /* number of descriptors, multiple of 8 */ + u16 headwb_enabled; /* deprecated with AVF 1.0 */ + u64 dma_ring_addr; + u64 dma_headwb_addr; /* deprecated with AVF 1.0 */ +}; + +VIRTCHNL_CHECK_STRUCT_LEN(24, virtchnl_txq_info); + +/* VIRTCHNL_OP_CONFIG_RX_QUEUE + * VF sends this message to set up parameters for one RX queue. + * External data buffer contains one instance of virtchnl_rxq_info. + * PF configures requested queue and returns a status code. The + * crc_disable flag disables CRC stripping on the VF. Setting + * the crc_disable flag to 1 will disable CRC stripping for each + * queue in the VF where the flag is set. The VIRTCHNL_VF_OFFLOAD_CRC + * offload must have been set prior to sending this info or the PF + * will ignore the request. This flag should be set the same for + * all of the queues for a VF. + */ + +/* Rx queue config info */ +struct virtchnl_rxq_info { + u16 vsi_id; + u16 queue_id; + u32 ring_len; /* number of descriptors, multiple of 32 */ + u16 hdr_size; + u16 splithdr_enabled; /* deprecated with AVF 1.0 */ + u32 databuffer_size; + u32 max_pkt_size; + u8 crc_disable; + u8 pad1[3]; + u64 dma_ring_addr; + enum virtchnl_rx_hsplit rx_split_pos; /* deprecated with AVF 1.0 */ + u32 pad2; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(40, virtchnl_rxq_info); + +/* VIRTCHNL_OP_CONFIG_VSI_QUEUES + * VF sends this message to set parameters for active TX and RX queues + * associated with the specified VSI. + * PF configures queues and returns status. + * If the number of queues specified is greater than the number of queues + * associated with the VSI, an error is returned and no queues are configured. + * NOTE: The VF is not required to configure all queues in a single request. + * It may send multiple messages. PF drivers must correctly handle all VF + * requests. + */ +struct virtchnl_queue_pair_info { + /* NOTE: vsi_id and queue_id should be identical for both queues. */ + struct virtchnl_txq_info txq; + struct virtchnl_rxq_info rxq; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(64, virtchnl_queue_pair_info); + +struct virtchnl_vsi_queue_config_info { + u16 vsi_id; + u16 num_queue_pairs; + u32 pad; + struct virtchnl_queue_pair_info qpair[1]; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl_vsi_queue_config_info); + +/* VIRTCHNL_OP_REQUEST_QUEUES + * VF sends this message to request the PF to allocate additional queues to + * this VF. Each VF gets a guaranteed number of queues on init but asking for + * additional queues must be negotiated. This is a best effort request as it + * is possible the PF does not have enough queues left to support the request. + * If the PF cannot support the number requested it will respond with the + * maximum number it is able to support. If the request is successful, PF will + * then reset the VF to institute required changes. + */ + +/* VF resource request */ +struct virtchnl_vf_res_request { + u16 num_queue_pairs; +}; + +/* VIRTCHNL_OP_CONFIG_IRQ_MAP + * VF uses this message to map vectors to queues. + * The rxq_map and txq_map fields are bitmaps used to indicate which queues + * are to be associated with the specified vector. + * The "other" causes are always mapped to vector 0. The VF may not request + * that vector 0 be used for traffic. + * PF configures interrupt mapping and returns status. + * NOTE: due to hardware requirements, all active queues (both TX and RX) + * should be mapped to interrupts, even if the driver intends to operate + * only in polling mode. In this case the interrupt may be disabled, but + * the ITR timer will still run to trigger writebacks. + */ +struct virtchnl_vector_map { + u16 vsi_id; + u16 vector_id; + u16 rxq_map; + u16 txq_map; + u16 rxitr_idx; + u16 txitr_idx; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(12, virtchnl_vector_map); + +struct virtchnl_irq_map_info { + u16 num_vectors; + struct virtchnl_vector_map vecmap[1]; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(14, virtchnl_irq_map_info); + +/* VIRTCHNL_OP_ENABLE_QUEUES + * VIRTCHNL_OP_DISABLE_QUEUES + * VF sends these message to enable or disable TX/RX queue pairs. + * The queues fields are bitmaps indicating which queues to act upon. + * (Currently, we only support 16 queues per VF, but we make the field + * u32 to allow for expansion.) + * PF performs requested action and returns status. + * NOTE: The VF is not required to enable/disable all queues in a single + * request. It may send multiple messages. + * PF drivers must correctly handle all VF requests. + */ +struct virtchnl_queue_select { + u16 vsi_id; + u16 pad; + u32 rx_queues; + u32 tx_queues; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(12, virtchnl_queue_select); + +/* VIRTCHNL_OP_ADD_ETH_ADDR + * VF sends this message in order to add one or more unicast or multicast + * address filters for the specified VSI. + * PF adds the filters and returns status. + */ + +/* VIRTCHNL_OP_DEL_ETH_ADDR + * VF sends this message in order to remove one or more unicast or multicast + * filters for the specified VSI. + * PF removes the filters and returns status. + */ + +struct virtchnl_ether_addr { + u8 addr[ETH_ALEN]; + u8 pad[2]; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl_ether_addr); + +struct virtchnl_ether_addr_list { + u16 vsi_id; + u16 num_elements; + struct virtchnl_ether_addr list[1]; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(12, virtchnl_ether_addr_list); + +/* VIRTCHNL_OP_ADD_VLAN + * VF sends this message to add one or more VLAN tag filters for receives. + * PF adds the filters and returns status. + * If a port VLAN is configured by the PF, this operation will return an + * error to the VF. + */ + +/* VIRTCHNL_OP_DEL_VLAN + * VF sends this message to remove one or more VLAN tag filters for receives. + * PF removes the filters and returns status. + * If a port VLAN is configured by the PF, this operation will return an + * error to the VF. + */ + +struct virtchnl_vlan_filter_list { + u16 vsi_id; + u16 num_elements; + u16 vlan_id[1]; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(6, virtchnl_vlan_filter_list); + +/* VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE + * VF sends VSI id and flags. + * PF returns status code in retval. + * Note: we assume that broadcast accept mode is always enabled. + */ +struct virtchnl_promisc_info { + u16 vsi_id; + u16 flags; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(4, virtchnl_promisc_info); + +#define FLAG_VF_UNICAST_PROMISC 0x00000001 +#define FLAG_VF_MULTICAST_PROMISC 0x00000002 + +/* VIRTCHNL_OP_GET_STATS + * VF sends this message to request stats for the selected VSI. VF uses + * the virtchnl_queue_select struct to specify the VSI. The queue_id + * field is ignored by the PF. + * + * PF replies with struct virtchnl_eth_stats in an external buffer. + */ + +struct virtchnl_eth_stats { + u64 rx_bytes; /* received bytes */ + u64 rx_unicast; /* received unicast pkts */ + u64 rx_multicast; /* received multicast pkts */ + u64 rx_broadcast; /* received broadcast pkts */ + u64 rx_discards; + u64 rx_unknown_protocol; + u64 tx_bytes; /* transmitted bytes */ + u64 tx_unicast; /* transmitted unicast pkts */ + u64 tx_multicast; /* transmitted multicast pkts */ + u64 tx_broadcast; /* transmitted broadcast pkts */ + u64 tx_discards; + u64 tx_errors; +}; + +/* VIRTCHNL_OP_CONFIG_RSS_KEY + * VIRTCHNL_OP_CONFIG_RSS_LUT + * VF sends these messages to configure RSS. Only supported if both PF + * and VF drivers set the VIRTCHNL_VF_OFFLOAD_RSS_PF bit during + * configuration negotiation. If this is the case, then the RSS fields in + * the VF resource struct are valid. + * Both the key and LUT are initialized to 0 by the PF, meaning that + * RSS is effectively disabled until set up by the VF. + */ +struct virtchnl_rss_key { + u16 vsi_id; + u16 key_len; + u8 key[1]; /* RSS hash key, packed bytes */ +}; + +VIRTCHNL_CHECK_STRUCT_LEN(6, virtchnl_rss_key); + +struct virtchnl_rss_lut { + u16 vsi_id; + u16 lut_entries; + u8 lut[1]; /* RSS lookup table */ +}; + +VIRTCHNL_CHECK_STRUCT_LEN(6, virtchnl_rss_lut); + +/* VIRTCHNL_OP_GET_RSS_HENA_CAPS + * VIRTCHNL_OP_SET_RSS_HENA + * VF sends these messages to get and set the hash filter enable bits for RSS. + * By default, the PF sets these to all possible traffic types that the + * hardware supports. The VF can query this value if it wants to change the + * traffic types that are hashed by the hardware. + */ +struct virtchnl_rss_hena { + u64 hena; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl_rss_hena); + +/* This is used by PF driver to enforce how many channels can be supported. + * When ADQ_V2 capability is negotiated, it will allow 16 channels otherwise + * PF driver will allow only max 4 channels + */ +#define VIRTCHNL_MAX_ADQ_CHANNELS 4 +#define VIRTCHNL_MAX_ADQ_V2_CHANNELS 16 + +/* VIRTCHNL_OP_ENABLE_CHANNELS + * VIRTCHNL_OP_DISABLE_CHANNELS + * VF sends these messages to enable or disable channels based on + * the user specified queue count and queue offset for each traffic class. + * This struct encompasses all the information that the PF needs from + * VF to create a channel. + */ +struct virtchnl_channel_info { + u16 count; /* number of queues in a channel */ + u16 offset; /* queues in a channel start from 'offset' */ + u32 pad; + u64 max_tx_rate; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_channel_info); + +struct virtchnl_tc_info { + u32 num_tc; + u32 pad; + struct virtchnl_channel_info list[1]; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(24, virtchnl_tc_info); + +/* VIRTCHNL_ADD_CLOUD_FILTER + * VIRTCHNL_DEL_CLOUD_FILTER + * VF sends these messages to add or delete a cloud filter based on the + * user specified match and action filters. These structures encompass + * all the information that the PF needs from the VF to add/delete a + * cloud filter. + */ + +struct virtchnl_l4_spec { + u8 src_mac[ETH_ALEN]; + u8 dst_mac[ETH_ALEN]; + /* vlan_prio is part of this 16 bit field even from OS perspective + * vlan_id:12 is actual vlan_id, then vlanid:bit14..12 is vlan_prio + * in future, when decided to offload vlan_prio, pass that information + * as part of the "vlan_id" field, Bit14..12 + */ + __be16 vlan_id; + __be16 pad; /* reserved for future use */ + __be32 src_ip[4]; + __be32 dst_ip[4]; + __be16 src_port; + __be16 dst_port; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(52, virtchnl_l4_spec); + +union virtchnl_flow_spec { + struct virtchnl_l4_spec tcp_spec; + u8 buffer[128]; /* reserved for future use */ +}; + +VIRTCHNL_CHECK_UNION_LEN(128, virtchnl_flow_spec); + +enum virtchnl_action { + /* action types */ + VIRTCHNL_ACTION_DROP = 0, + VIRTCHNL_ACTION_TC_REDIRECT, +}; + +enum virtchnl_flow_type { + /* flow types */ + VIRTCHNL_TCP_V4_FLOW = 0, + VIRTCHNL_TCP_V6_FLOW, + VIRTCHNL_UDP_V4_FLOW, + VIRTCHNL_UDP_V6_FLOW, +}; + +struct virtchnl_filter { + union virtchnl_flow_spec data; + union virtchnl_flow_spec mask; + enum virtchnl_flow_type flow_type; + enum virtchnl_action action; + u32 action_meta; + u8 field_flags; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(272, virtchnl_filter); + +/* VIRTCHNL_OP_EVENT + * PF sends this message to inform the VF driver of events that may affect it. + * No direct response is expected from the VF, though it may generate other + * messages in response to this one. + */ +enum virtchnl_event_codes { + VIRTCHNL_EVENT_UNKNOWN = 0, + VIRTCHNL_EVENT_LINK_CHANGE, + VIRTCHNL_EVENT_RESET_IMPENDING, + VIRTCHNL_EVENT_PF_DRIVER_CLOSE, +}; + +#define PF_EVENT_SEVERITY_INFO 0 +#define PF_EVENT_SEVERITY_CERTAIN_DOOM 255 + +struct virtchnl_pf_event { + enum virtchnl_event_codes event; + union { + /* If the PF driver does not support the new speed reporting + * capabilities then use link_event else use link_event_adv to + * get the speed and link information. The ability to understand + * new speeds is indicated by setting the capability flag + * VIRTCHNL_VF_CAP_ADV_LINK_SPEED in vf_cap_flags parameter + * in virtchnl_vf_resource struct and can be used to determine + * which link event struct to use below. + */ + struct { + enum virtchnl_link_speed link_speed; + u8 link_status; + } link_event; + struct { + /* link_speed provided in Mbps */ + u32 link_speed; + u8 link_status; + } link_event_adv; + } event_data; + + int severity; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_pf_event); + +/* VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP + * VF uses this message to request PF to map IWARP vectors to IWARP queues. + * The request for this originates from the VF IWARP driver through + * a client interface between VF LAN and VF IWARP driver. + * A vector could have an AEQ and CEQ attached to it although + * there is a single AEQ per VF IWARP instance in which case + * most vectors will have an INVALID_IDX for aeq and valid idx for ceq. + * There will never be a case where there will be multiple CEQs attached + * to a single vector. + * PF configures interrupt mapping and returns status. + */ +struct virtchnl_iwarp_qv_info { + u32 v_idx; /* msix_vector */ + u16 ceq_idx; + u16 aeq_idx; + u8 itr_idx; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(12, virtchnl_iwarp_qv_info); + +struct virtchnl_iwarp_qvlist_info { + u32 num_vectors; + struct virtchnl_iwarp_qv_info qv_info[1]; +}; + +VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_iwarp_qvlist_info); + +/* Since VF messages are limited by u16 size, precalculate the maximum possible + * values of nested elements in virtchnl structures that virtual channel can + * possibly handle in a single message. + */ +enum virtchnl_vector_limits { + VIRTCHNL_OP_CONFIG_VSI_QUEUES_MAX = + ((u16)(~0) - sizeof(struct virtchnl_vsi_queue_config_info)) / + sizeof(struct virtchnl_queue_pair_info), + + VIRTCHNL_OP_CONFIG_IRQ_MAP_MAX = + ((u16)(~0) - sizeof(struct virtchnl_irq_map_info)) / + sizeof(struct virtchnl_vector_map), + + VIRTCHNL_OP_ADD_DEL_ETH_ADDR_MAX = + ((u16)(~0) - sizeof(struct virtchnl_ether_addr_list)) / + sizeof(struct virtchnl_ether_addr), + + VIRTCHNL_OP_ADD_DEL_VLAN_MAX = + ((u16)(~0) - sizeof(struct virtchnl_vlan_filter_list)) / + sizeof(u16), + + VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP_MAX = + ((u16)(~0) - sizeof(struct virtchnl_iwarp_qvlist_info)) / + sizeof(struct virtchnl_iwarp_qv_info), + + VIRTCHNL_OP_ENABLE_CHANNELS_MAX = + ((u16)(~0) - sizeof(struct virtchnl_tc_info)) / + sizeof(struct virtchnl_channel_info), +}; + +/* VF reset states - these are written into the RSTAT register: + * VFGEN_RSTAT on the VF + * When the PF initiates a reset, it writes 0 + * When the reset is complete, it writes 1 + * When the PF detects that the VF has recovered, it writes 2 + * VF checks this register periodically to determine if a reset has occurred, + * then polls it to know when the reset is complete. + * If either the PF or VF reads the register while the hardware + * is in a reset state, it will return DEADBEEF, which, when masked + * will result in 3. + */ +enum virtchnl_vfr_states { + VIRTCHNL_VFR_INPROGRESS = 0, + VIRTCHNL_VFR_COMPLETED, + VIRTCHNL_VFR_VFACTIVE, +}; + +/** + * virtchnl_vc_validate_vf_msg + * @ver: Virtchnl version info + * @v_opcode: Opcode for the message + * @msg: pointer to the msg buffer + * @msglen: msg length + * + * validate msg format against struct for each opcode + */ +static inline int +virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode, + u8 *msg, u16 msglen) +{ + bool err_msg_format = false; + int valid_len = 0; + + /* Validate message length. */ + switch (v_opcode) { + case VIRTCHNL_OP_VERSION: + valid_len = sizeof(struct virtchnl_version_info); + break; + case VIRTCHNL_OP_RESET_VF: + break; + case VIRTCHNL_OP_GET_VF_RESOURCES: + if (VF_IS_V11(ver)) + valid_len = sizeof(u32); + break; + case VIRTCHNL_OP_CONFIG_TX_QUEUE: + valid_len = sizeof(struct virtchnl_txq_info); + break; + case VIRTCHNL_OP_CONFIG_RX_QUEUE: + valid_len = sizeof(struct virtchnl_rxq_info); + break; + case VIRTCHNL_OP_CONFIG_VSI_QUEUES: + valid_len = sizeof(struct virtchnl_vsi_queue_config_info); + if (msglen >= valid_len) { + struct virtchnl_vsi_queue_config_info *vqc = + (struct virtchnl_vsi_queue_config_info *)msg; + + if (vqc->num_queue_pairs == 0 || vqc->num_queue_pairs > + VIRTCHNL_OP_CONFIG_VSI_QUEUES_MAX) { + err_msg_format = true; + break; + } + + valid_len += (vqc->num_queue_pairs * + sizeof(struct + virtchnl_queue_pair_info)); + } + break; + case VIRTCHNL_OP_CONFIG_IRQ_MAP: + valid_len = sizeof(struct virtchnl_irq_map_info); + if (msglen >= valid_len) { + struct virtchnl_irq_map_info *vimi = + (struct virtchnl_irq_map_info *)msg; + + if (vimi->num_vectors == 0 || vimi->num_vectors > + VIRTCHNL_OP_CONFIG_IRQ_MAP_MAX) { + err_msg_format = true; + break; + } + + valid_len += (vimi->num_vectors * + sizeof(struct virtchnl_vector_map)); + } + break; + case VIRTCHNL_OP_ENABLE_QUEUES: + case VIRTCHNL_OP_DISABLE_QUEUES: + valid_len = sizeof(struct virtchnl_queue_select); + break; + case VIRTCHNL_OP_ADD_ETH_ADDR: + case VIRTCHNL_OP_DEL_ETH_ADDR: + valid_len = sizeof(struct virtchnl_ether_addr_list); + if (msglen >= valid_len) { + struct virtchnl_ether_addr_list *veal = + (struct virtchnl_ether_addr_list *)msg; + + if (veal->num_elements == 0 || veal->num_elements > + VIRTCHNL_OP_ADD_DEL_ETH_ADDR_MAX) { + err_msg_format = true; + break; + } + + valid_len += veal->num_elements * + sizeof(struct virtchnl_ether_addr); + } + break; + case VIRTCHNL_OP_ADD_VLAN: + case VIRTCHNL_OP_DEL_VLAN: + valid_len = sizeof(struct virtchnl_vlan_filter_list); + if (msglen >= valid_len) { + struct virtchnl_vlan_filter_list *vfl = + (struct virtchnl_vlan_filter_list *)msg; + + if (vfl->num_elements == 0 || vfl->num_elements > + VIRTCHNL_OP_ADD_DEL_VLAN_MAX) { + err_msg_format = true; + break; + } + + valid_len += vfl->num_elements * sizeof(u16); + } + break; + case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE: + valid_len = sizeof(struct virtchnl_promisc_info); + break; + case VIRTCHNL_OP_GET_STATS: + valid_len = sizeof(struct virtchnl_queue_select); + break; + case VIRTCHNL_OP_IWARP: + /* These messages are opaque to us and will be validated in + * the RDMA client code. We just need to check for nonzero + * length. The firmware will enforce max length restrictions. + */ + if (msglen) + valid_len = msglen; + else + err_msg_format = true; + break; + case VIRTCHNL_OP_RELEASE_IWARP_IRQ_MAP: + break; + case VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP: + valid_len = sizeof(struct virtchnl_iwarp_qvlist_info); + if (msglen >= valid_len) { + struct virtchnl_iwarp_qvlist_info *qv = + (struct virtchnl_iwarp_qvlist_info *)msg; + + if (qv->num_vectors == 0 || qv->num_vectors > + VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP_MAX) { + err_msg_format = true; + break; + } + + valid_len += ((qv->num_vectors - 1) * + sizeof(struct virtchnl_iwarp_qv_info)); + } + break; + case VIRTCHNL_OP_CONFIG_RSS_KEY: + valid_len = sizeof(struct virtchnl_rss_key); + if (msglen >= valid_len) { + struct virtchnl_rss_key *vrk = + (struct virtchnl_rss_key *)msg; + + if (vrk->key_len == 0) { + /* zero length is allowed as input */ + break; + } + + valid_len += vrk->key_len - 1; + } + break; + case VIRTCHNL_OP_CONFIG_RSS_LUT: + valid_len = sizeof(struct virtchnl_rss_lut); + if (msglen >= valid_len) { + struct virtchnl_rss_lut *vrl = + (struct virtchnl_rss_lut *)msg; + + if (vrl->lut_entries == 0) { + /* zero entries is allowed as input */ + break; + } + + valid_len += vrl->lut_entries - 1; + } + break; + case VIRTCHNL_OP_GET_RSS_HENA_CAPS: + break; + case VIRTCHNL_OP_SET_RSS_HENA: + valid_len = sizeof(struct virtchnl_rss_hena); + break; + case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING: + case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING: + break; + case VIRTCHNL_OP_REQUEST_QUEUES: + valid_len = sizeof(struct virtchnl_vf_res_request); + break; + case VIRTCHNL_OP_ENABLE_CHANNELS: + valid_len = sizeof(struct virtchnl_tc_info); + if (msglen >= valid_len) { + struct virtchnl_tc_info *vti = + (struct virtchnl_tc_info *)msg; + + if (vti->num_tc == 0 || vti->num_tc > + VIRTCHNL_OP_ENABLE_CHANNELS_MAX) { + err_msg_format = true; + break; + } + + valid_len += (vti->num_tc - 1) * + sizeof(struct virtchnl_channel_info); + } + break; + case VIRTCHNL_OP_DISABLE_CHANNELS: + break; + case VIRTCHNL_OP_ADD_CLOUD_FILTER: + case VIRTCHNL_OP_DEL_CLOUD_FILTER: + valid_len = sizeof(struct virtchnl_filter); + break; + /* These are always errors coming from the VF. */ + case VIRTCHNL_OP_EVENT: + case VIRTCHNL_OP_UNKNOWN: + default: + return VIRTCHNL_STATUS_ERR_PARAM; + } + /* few more checks */ + if (err_msg_format || valid_len != msglen) + return VIRTCHNL_STATUS_ERR_OPCODE_MISMATCH; + + return 0; +} +#endif /* _VIRTCHNL_H_ */ -- 2.20.1