From 9159176013f053a8294ce512b6408a8235871f0d Mon Sep 17 00:00:00 2001 From: Adrian Stratulat Date: Tue, 16 Jun 2020 08:34:41 +0200 Subject: Ampere/aarch64 bring-up changes * Add machine description for emag8180. * Disable syslinux build: syslinux is not compatible with ARM targets, but other recipes force it as a dependency anyway. * Add recipe for kernel linux-ampere_4.14 (used by emag8180 and qemuarm64 targets). * Upgrade i40e drivers to newer version due to some warnings generated by OVS-DPDK * Add OVMF support for AARCH64. Change-Id: I4cbc09ef83d717b39abf0981b80569a4a694cb0d Signed-off-by: Adrian Stratulat --- conf/layer.conf | 8 +- conf/machine/emag8180.conf | 13 + recipes-core/ovmf/ovmf_git.bbappend | 112 + recipes-devtools/syslinux/syslinux_%.bbappend | 13 + recipes-kernel/linux/linux-ampere-guest_4.14.bb | 43 + .../0001-Upgrade-i40e-drivers-to-2.11.29.patch | 63158 +++++++++++++++++++ recipes-kernel/linux/linux-ampere_4.14.bb | 80 + recipes-kernel/linux/linux-ampere_4.14.inc | 46 + 8 files changed, 63469 insertions(+), 4 deletions(-) create mode 100644 conf/machine/emag8180.conf create mode 100644 recipes-core/ovmf/ovmf_git.bbappend create mode 100644 recipes-devtools/syslinux/syslinux_%.bbappend create mode 100644 recipes-kernel/linux/linux-ampere-guest_4.14.bb create mode 100644 recipes-kernel/linux/linux-ampere/0001-Upgrade-i40e-drivers-to-2.11.29.patch create mode 100644 recipes-kernel/linux/linux-ampere_4.14.bb create mode 100644 recipes-kernel/linux/linux-ampere_4.14.inc diff --git a/conf/layer.conf b/conf/layer.conf index 2ae610a..50199be 100644 --- a/conf/layer.conf +++ b/conf/layer.conf @@ -6,7 +6,7 @@ BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \ ${LAYERDIR}/packagegroups/* \ ${LAYERDIR}/recipes-*/*/*.bbappend" -BBFILE_COLLECTIONS += "enea-bsp-arm" -BBFILE_PATTERN_enea-bsp-arm = "^${LAYERDIR}/" -BBFILE_PRIORITY_enea-bsp-arm = "6" -LAYERDEPENDS_enea-bsp-arm = "enea-bsp-common" +BBFILE_COLLECTIONS += "nfv-access-bsp-arm" +BBFILE_PATTERN_nfv-access-bsp-arm = "^${LAYERDIR}/" +BBFILE_PRIORITY_nfv-access-bsp-arm = "6" +LAYERDEPENDS_nfv-access-bsp-arm = "nfv-access-bsp-common" diff --git a/conf/machine/emag8180.conf b/conf/machine/emag8180.conf new file mode 100644 index 0000000..bc4b73b --- /dev/null +++ b/conf/machine/emag8180.conf @@ -0,0 +1,13 @@ +#@TYPE: Machine +#@NAME: Ampere eMAG 8180 64-bit Arm Processor +#@DESCRIPTION: Ampere eMAG 8180 processor based on ARMv8 architecture + +require conf/machine/include/arm/arch-armv8.inc + +PREFERRED_PROVIDER_virtual/kernel ?= "linux-ampere" + +MACHINE_FEATURES += " efi" +DPDK_TARGET_MACHINE ?= "armv8a" + +SERIAL_CONSOLES = "115200;ttyAMA0" +KERNEL_IMAGETYPE = "Image" diff --git a/recipes-core/ovmf/ovmf_git.bbappend b/recipes-core/ovmf/ovmf_git.bbappend new file mode 100644 index 0000000..9fc7cb7 --- /dev/null +++ b/recipes-core/ovmf/ovmf_git.bbappend @@ -0,0 +1,112 @@ +# Overwrite the "do_compile" step in order to support AARCH64 targets. + +COMPATIBLE_HOST='(i.86|x86_64|aarch64).*' + +do_compile_class-target() { + export LFLAGS="${LDFLAGS}" + PARALLEL_JOBS="${@ '${PARALLEL_MAKE}'.replace('-j', '-n ')}" + OVMF_ARCH="X64" + if [ "${TARGET_ARCH}" != "x86_64" ] ; then + OVMF_ARCH="IA32" + fi + if [ "${TARGET_ARCH}" = "aarch64" ] ; then + OVMF_ARCH="AARCH64" + fi + + # The build for the target uses BaseTools/Conf/tools_def.template + # from ovmf-native to find the compiler, which depends on + # exporting HOST_PREFIX. + export HOST_PREFIX="${HOST_PREFIX}" + + # BaseTools/Conf gets copied to Conf, but only if that does not + # exist yet. To ensure that an updated template gets used during + # incremental builds, we need to remove the copy before we start. + rm -f `ls ${S}/Conf/*.txt | grep -v ReadMe.txt` + + # ${WORKDIR}/ovmf is a well-known location where do_install and + # do_deploy will be able to find the files. + rm -rf ${WORKDIR}/ovmf + mkdir ${WORKDIR}/ovmf + OVMF_DIR_SUFFIX="X64" + if [ "${TARGET_ARCH}" != "x86_64" ] ; then + OVMF_DIR_SUFFIX="Ia32" # Note the different capitalization + fi + if [ "${TARGET_ARCH}" = "aarch64" ] ; then + OVMF_DIR_SUFFIX="AArch64" # Note the different capitalization + fi + + FIXED_GCCVER=$(fixup_target_tools ${GCC_VER}) + bbnote FIXED_GCCVER is ${FIXED_GCCVER} + + if [ "${TARGET_ARCH}" = "aarch64" ] ; then + build_dir="${S}/Build/ArmVirtQemu-AARCH64/RELEASE_${FIXED_GCCVER}" + bbnote "Building without Secure Boot." + rm -rf ${S}/Build/ArmVirtQemu-AARCH64 + + # If the local BaseTools directory is not found, the EDK_TOOLS_PATH + # variable is used to determine the location of the toolchain + rm -rf ${S}/BaseTools + export EDK_TOOLS_PATH=${WORKDIR}/recipe-sysroot-native/usr/bin/edk2_basetools/BaseTools + bash -c "(source ${S}/edksetup.sh; GCC5_AARCH64_PREFIX=aarch64-poky-linux- build -a AARCH64 -p ArmVirtPkg/ArmVirtQemu.dsc -t GCC5 -b RELEASE)" + else + build_dir="${S}/Build/Ovmf$OVMF_DIR_SUFFIX/RELEASE_${FIXED_GCCVER}" + bbnote "Building without Secure Boot." + rm -rf ${S}/Build/Ovmf$OVMF_DIR_SUFFIX + + ${S}/OvmfPkg/build.sh $PARALLEL_JOBS -a $OVMF_ARCH -b RELEASE -t ${FIXED_GCCVER} + ln ${build_dir}/FV/OVMF.fd ${WORKDIR}/ovmf/ovmf.fd + ln ${build_dir}/FV/OVMF_CODE.fd ${WORKDIR}/ovmf/ovmf.code.fd + ln ${build_dir}/FV/OVMF_VARS.fd ${WORKDIR}/ovmf/ovmf.vars.fd + fi + + ln ${build_dir}/${OVMF_ARCH}/Shell.efi ${WORKDIR}/ovmf/ + + if ${@bb.utils.contains('PACKAGECONFIG', 'secureboot', 'true', 'false', d)}; then + # See CryptoPkg/Library/OpensslLib/Patch-HOWTO.txt and + # https://src.fedoraproject.org/cgit/rpms/edk2.git/tree/ for + # building with Secure Boot enabled. + bbnote "Building with Secure Boot." + rm -rf ${S}/Build/Ovmf$OVMF_DIR_SUFFIX + ln -sf ${OPENSSL_RELEASE} ${S}/CryptoPkg/Library/OpensslLib/openssl + ${S}/OvmfPkg/build.sh $PARALLEL_JOBS -a $OVMF_ARCH -b RELEASE -t ${FIXED_GCCVER} ${OVMF_SECURE_BOOT_FLAGS} + ln ${build_dir}/FV/OVMF.fd ${WORKDIR}/ovmf/ovmf.secboot.fd + ln ${build_dir}/FV/OVMF_CODE.fd ${WORKDIR}/ovmf/ovmf.secboot.code.fd + ln ${build_dir}/${OVMF_ARCH}/EnrollDefaultKeys.efi ${WORKDIR}/ovmf/ + fi +} + +do_install_class-target() { + if [ "${TARGET_ARCH}" = "aarch64" ] ; then + FIXED_GCCVER=$(fixup_target_tools ${GCC_VER}) + build_dir="${S}/Build/ArmVirtQemu-AARCH64/RELEASE_${FIXED_GCCVER}" + + install -d ${D}/usr/share/qemu + install -m 766 ${build_dir}/FV/QEMU_EFI.fd ${D}/usr/share/qemu/AAVMF_CODE.fd + install -m 766 ${build_dir}/FV/QEMU_VARS.fd ${D}/usr/share/qemu/AAVMF_VARS.fd + else + # Content for UEFI shell iso. We install the EFI shell as + # bootx64/ia32.efi because then it can be started even when the + # firmware itself does not contain it. + install -d ${D}/efi/boot + install ${WORKDIR}/ovmf/Shell.efi ${D}/efi/boot/boot${@ "ia32" if "${TARGET_ARCH}" != "x86_64" else "x64"}.efi + if ${@bb.utils.contains('PACKAGECONFIG', 'secureboot', 'true', 'false', d)}; then + install ${WORKDIR}/ovmf/EnrollDefaultKeys.efi ${D} + fi + fi +} + +FILES_${PN}_class-target_aarch64 += "/usr/share/qemu/" + +do_deploy_class-target() { + if [ "${TARGET_ARCH}" != "aarch64" ] ; then + # For use with "runqemu ovmf". + for i in \ + ovmf \ + ovmf.code \ + ovmf.vars \ + ${@bb.utils.contains('PACKAGECONFIG', 'secureboot', 'ovmf.secboot ovmf.secboot.code', '', d)} \ + ; do + qemu-img convert -f raw -O qcow2 ${WORKDIR}/ovmf/$i.fd ${DEPLOYDIR}/$i.qcow2 + done + fi +} diff --git a/recipes-devtools/syslinux/syslinux_%.bbappend b/recipes-devtools/syslinux/syslinux_%.bbappend new file mode 100644 index 0000000..045830a --- /dev/null +++ b/recipes-devtools/syslinux/syslinux_%.bbappend @@ -0,0 +1,13 @@ +# Syslinux is not supposed to be available on ARM. +# Some recipes/image-classes mark it as a dependency even if it's not used. + +COMPATIBLE_HOST = '(x86_64|i.86|aarch64).*-(linux|freebsd.*)' + +do_fetch[noexec] = "1" +do_unpack[noexec] = "1" +do_patch[noexec] = "1" +do_populate_lic[noexec] = "1" +do_configure[noexec] = "1" +do_compile[noexec] = "1" +do_install[noexec] = "1" +do_populate_sysroot[noexec] = "1" diff --git a/recipes-kernel/linux/linux-ampere-guest_4.14.bb b/recipes-kernel/linux/linux-ampere-guest_4.14.bb new file mode 100644 index 0000000..8782972 --- /dev/null +++ b/recipes-kernel/linux/linux-ampere-guest_4.14.bb @@ -0,0 +1,43 @@ +require linux-ampere_4.14.inc + +################# meta-enea-virtualization/.../linux-intel-guest_4.14.bbappend ############ + +# Disable Virtualization(host) support +KERNEL_FEATURES_append = " features/kvm/host_n.scc" + +# KVM Guest +KERNEL_FEATURES_append = " features/kvm/guest_kvm_y.scc" +KERNEL_FEATURES_append = " features/kvm/virtio_y.scc" +# PCI Legasy(required for /dev/vda) +KERNEL_FEATURES_append = " features/kvm/virtio_pci_legacy_y.scc" + +# Full no Hz - nohz_full kernel param required +KERNEL_FEATURES_append = " features/full_nohz/full_nohz-enable.scc" + +# Enable HPET, UIO, HUGETLB, PCI_MSI +KERNEL_FEATURES_append = " features/intel-dpdk/intel-dpdk.scc" +KERNEL_MODULE_AUTOLOAD += "uio" + +# VFIO/IOMMU +KERNEL_FEATURES_append = " features/vfio/vfio_m.scc" + +# Low Latency kernel +KERNEL_FEATURES_append = " features/lowlatency/lowlatency_y.scc" + +# CPU isolation +KERNEL_FEATURES_append = " features/cgroups/cpusets.scc" +KERNEL_FEATURES_append = " features/rcu/rcu_nocb_y.scc" + +# Enable E1000 and IXGBE drivers as built-in +KERNEL_FEATURES_append = " features/intel-e1xxxx/e1xxxx_m.scc" +KERNEL_FEATURES_append = " features/ixgbe/ixgbe_m.scc" + +# Enable PCI IOV +KERNEL_FEATURES_append = " features/pci/pci_iov_y.scc" + +# Enable printk messages +KERNEL_FEATURES_append = " features/printk/printk_y.scc" + +# Enable CD-ROM support for Cloud-init +KERNEL_FEATURES_append = " features/isofs/isofs.scc" +KERNEL_FEATURES_append = " features/cdrom/cdrom_m.scc" diff --git a/recipes-kernel/linux/linux-ampere/0001-Upgrade-i40e-drivers-to-2.11.29.patch b/recipes-kernel/linux/linux-ampere/0001-Upgrade-i40e-drivers-to-2.11.29.patch new file mode 100644 index 0000000..88c4182 --- /dev/null +++ b/recipes-kernel/linux/linux-ampere/0001-Upgrade-i40e-drivers-to-2.11.29.patch @@ -0,0 +1,63158 @@ +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 + diff --git a/recipes-kernel/linux/linux-ampere_4.14.bb b/recipes-kernel/linux/linux-ampere_4.14.bb new file mode 100644 index 0000000..db3cdee --- /dev/null +++ b/recipes-kernel/linux/linux-ampere_4.14.bb @@ -0,0 +1,80 @@ +require linux-ampere_4.14.inc +include kernel-aufs.inc + +################# meta-enea-virtualization/.../linux-intel-host.inc ############ + +# Generic functionality for containers +KERNEL_FEATURES_append = " features/vxlan/vxlan_y.scc" +KERNEL_FEATURES_append = " features/overlayfs/overlayfs_m.scc" +KERNEL_FEATURES_append = " features/aufs/aufs-enable.scc" + +# Docker +KERNEL_FEATURES_append = " features/netfilter/netfilter_y.scc" +KERNEL_FEATURES_append = " cfg/net/ip_nf_y.scc" + +# LXC +KERNEL_FEATURES_append = " features/lxc/lxc_y.scc" + +# Use Linux kernel tree OVS module (since the OVS tree module does not support 4.14 kernel) +KERNEL_FEATURES_append = " features/openvswitch/openvswitch_support.scc" +KERNEL_FEATURES_append = " features/openvswitch/openvswitch.scc" +KERNEL_MODULE_AUTOLOAD += "tun" + +# VFIO/IOMMU +KERNEL_FEATURES_append = " features/vfio/vfio_m.scc" + +# KVM Host +KERNEL_FEATURES_append = " features/kvm/qemu-kvm-arm64_y.scc" +KERNEL_FEATURES_append = " features/kvm/guest_n.scc" +KERNEL_FEATURES_append = " features/vhost/vhost_m.scc" +KERNEL_FEATURES_append = " features/pci/pci_iov_m.scc" + +# KSM (change it to y for high density of VMs) +KERNEL_FEATURES_append = " features/ksm/ksm_n.scc" + +# Full no Hz +KERNEL_FEATURES_append = " features/full_nohz/full_nohz-enable.scc" + +# Enable HPET, UIO, HUGETLB, PCI_MSI +KERNEL_FEATURES_append = " features/intel-dpdk/intel-dpdk.scc" +KERNEL_MODULE_AUTOLOAD += "uio" + +# Low Latency kernel +KERNEL_FEATURES_append = " features/lowlatency/lowlatency_y.scc" + +# CPU isolation +KERNEL_FEATURES_append = " features/cgroups/cpusets.scc" +KERNEL_FEATURES_append = " features/rcu/rcu_nocb_y.scc" + +# Enable printk and earlyprintk +KERNEL_FEATURES_append = " features/printk/printk_y.scc" + +# Add USB, IDE, ISO FS and SCSI support to boot off +# of USB sticks and CDROMs +KERNEL_FEATURES_append = " features/ide/ide_m.scc" +KERNEL_FEATURES_append = " features/usb/usb_storage_m.scc" +KERNEL_FEATURES_append = " features/usb/ohci_m.scc" +KERNEL_FEATURES_append = " features/cdrom/isofs_m.scc" +KERNEL_FEATURES_append = " features/cdrom/cdrom_m.scc" +KERNEL_FEATURES_append = " features/scsi/scsi_y.scc" + +# extend storage support for boot +KERNEL_FEATURES_append = " features/mmc/mmc_sd_y.scc" + +# console & keyboard used by usb installer +KERNEL_FEATURES_append = " features/console/console_txt_y.scc" +KERNEL_FEATURES_append = " features/hid/keyboard_m.scc" + +# extend devices(USB, HSI,...) support +KERNEL_FEATURES_append = " features/firewire/firewire_m.scc" +KERNEL_FEATURES_append = " features/hsi/hsi_m.scc" +KERNEL_FEATURES_append = " features/usbnet/usbnet_m.scc" +KERNEL_FEATURES_append = " features/usbnet/usb_phy_m.scc" +KERNEL_FEATURES_append = " features/usbGadget/usbgadget_m.scc" + +# Gigapages support +KERNEL_FEATURES_append = " features/gigapages/gigapages_y.scc" + +# WLAN support +KERNEL_FEATURES_append = " features/wlan/wlan_y.scc" +KERNEL_FEATURES_append = " features/wlan/wlan_ath10k.scc" diff --git a/recipes-kernel/linux/linux-ampere_4.14.inc b/recipes-kernel/linux/linux-ampere_4.14.inc new file mode 100644 index 0000000..e47e3e4 --- /dev/null +++ b/recipes-kernel/linux/linux-ampere_4.14.inc @@ -0,0 +1,46 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/linux-ampere:" + +require recipes-kernel/linux/linux-yocto.inc +require recipes-kernel/linux/linux-deploy-kconfig.inc + +# board specific branches +KBRANCH_emag8180 ?= "amp-centos-7.5-kernel" +KBRANCH_qemuarm64 ?= "amp-centos-7.5-kernel" + +SRCREV_machine_emag8180 ?= "da654c67f134497fd7cf88775e8b206c7d28a67a" +SRCREV_machine_qemuarm64 ?= "da654c67f134497fd7cf88775e8b206c7d28a67a" + +SRCREV_metaenea ?= "2e53208e8c1eaf83fb6b6411f8465f2bdf1d6069" +KENEABRANCH = "ampere-4.14" +SRCREV_meta ?= "245d701df6c3691a078a268eff54009959beb842" + +SRC_URI = "git://github.com/AmpereComputing/ampere-centos-kernel.git;name=machine;branch=${KBRANCH} \ + git://git.yoctoproject.org/yocto-kernel-cache;type=kmeta;name=meta;branch=yocto-4.14;destsuffix=kernel-meta \ + git://git@git.enea.com/linux/enea-kernel-cache.git;protocol=ssh;type=kmeta;name=metaenea;branch=${KENEABRANCH};destsuffix=enea-kernel-cache \ + file://0001-Upgrade-i40e-drivers-to-2.11.29.patch \ + " + +KERNEL_CONSOLE = "ttyAMA0,115200" + +LINUX_KERNEL_TYPE = "standard" +LINUX_VERSION ?= "4.14.0" +LINUX_VERSION_EXTENSION = "-ampere-${LINUX_KERNEL_TYPE}" + +COMPATIBLE_MACHINE = "emag8180|qemuarm64" +KMACHINE_emag8180 = "ampere-emag8180" +KMACHINE_qemuarm64 = "ampere-emag8180" + +KERNEL_FEATURES_append = " features/udev/udev.scc" + +# Ramdisk boot support +KERNEL_FEATURES_append = " features/blkdev/ramdisk_blk_dev.scc" + +# Intel 10G ports(SoC) +KERNEL_FEATURES_append = " features/ixgbe/ixgbe_y.scc" +KERNEL_FEATURES_append = " features/dca/dca_y.scc" + +# NMVe SSD +KERNEL_FEATURES_append = " features/nvme/nvme.scc" + +#IPv4 waiting for carrier on +KERNEL_FEATURES_append = " patches/ipv4/ipv4wait.scc" -- cgit v1.2.3-54-g00ecf