rename vpp-dpdk-dev to vpp-ext-deps

We need to have new tenants in the development package.
This is first of series of patches which will allow us to have multiple
external libs and tools packaged for developer's convenience.

Change-Id: I884bd75fba96005bbf8cea92774682b2228e0e22
Signed-off-by: Damjan Marion <damarion@cisco.com>
diff --git a/build/external/Makefile b/build/external/Makefile
new file mode 100644
index 0000000..201cf12
--- /dev/null
+++ b/build/external/Makefile
@@ -0,0 +1,488 @@
+# Copyright (c) 2015 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Scripts require non-POSIX parts of bash
+SHELL := /bin/bash
+
+DPDK_BUILD_DIR        ?= $(CURDIR)/_build
+DPDK_INSTALL_DIR      ?= $(CURDIR)/_install
+DPDK_PKTMBUF_HEADROOM ?= 128
+DPDK_CACHE_LINE_SIZE  ?= 64
+DPDK_DOWNLOAD_DIR     ?= $(HOME)/Downloads
+DPDK_DEBUG            ?= n
+DPDK_MLX4_PMD         ?= n
+DPDK_MLX5_PMD         ?= n
+DPDK_TAP_PMD          ?= n
+DPDK_FAILSAFE_PMD     ?= n
+
+B := $(DPDK_BUILD_DIR)
+I := $(DPDK_INSTALL_DIR)
+DPDK_VERSION ?= 18.08
+PKG_VERSION ?= $(shell git describe --abbrev=0 | cut -d- -f1 | cut -dv -f2)
+PKG_SUFFIX ?= $(shell git log --oneline $$(git describe --abbrev=0).. . | wc -l)
+DPDK_BASE_URL ?= http://fast.dpdk.org/rel
+DPDK_TARBALL := dpdk-$(DPDK_VERSION).tar.xz
+DPDK_TAR_URL := $(DPDK_BASE_URL)/$(DPDK_TARBALL)
+DPDK_18.05_TARBALL_MD5_CKSUM := 9fc86367cd9407ff6a8dfea56c4eddc4
+DPDK_18.08_TARBALL_MD5_CKSUM := da5e7fb25ab063c47e53929fb8c58be5
+MACHINE=$(shell uname -m)
+
+# replace dot with space, and if 3rd word exists we deal with stable dpdk rel
+ifeq ($(word 3,$(subst ., ,$(DPDK_VERSION))),)
+DPDK_SOURCE := $(B)/dpdk-$(DPDK_VERSION)
+else
+DPDK_SOURCE := $(B)/dpdk-stable-$(DPDK_VERSION)
+endif
+
+NASM_BASE_URL := http://www.nasm.us/pub/nasm/releasebuilds
+NASM_VER := 2.13.03
+NASM_TARBALL := nasm-$(NASM_VER).tar.xz
+NASM_TAR_URL := $(NASM_BASE_URL)/$(NASM_VER)/$(NASM_TARBALL)
+NASM_SOURCE := $(B)/nasm-$(NASM_VER)
+
+ifneq (,$(findstring 18.02,$(DPDK_VERSION)))
+IPSEC_MB_VER ?= 0.48
+else
+IPSEC_MB_VER ?= 0.49
+endif
+
+ifeq ($(MACHINE),$(filter $(MACHINE),x86_64))
+  AESNI ?= y
+$(info Building IPSec-MB $(IPSEC_MB_VER) library)
+else
+  AESNI ?= N
+endif
+
+AESNIMB_LIB_TARBALL := v$(IPSEC_MB_VER).tar.gz
+AESNIMB_LIB_TARBALL_URL := http://github.com/01org/intel-ipsec-mb/archive/$(AESNIMB_LIB_TARBALL)
+AESNIMB_LIB_SOURCE := $(B)/intel-ipsec-mb-$(IPSEC_MB_VER)
+
+ifneq (,$(findstring clang,$(CC)))
+DPDK_CC=clang
+else ifneq (,$(findstring icc,$(CC)))
+DPDK_CC=icc
+else
+DPDK_CC=gcc
+endif
+
+##############################################################################
+# Intel x86
+##############################################################################
+ifeq ($(MACHINE),$(filter $(MACHINE),x86_64 i686))
+DPDK_TARGET           ?= $(MACHINE)-native-linuxapp-$(DPDK_CC)
+DPDK_MACHINE          ?= nhm
+DPDK_TUNE             ?= core-avx2
+
+##############################################################################
+# ARM64
+##############################################################################
+else ifeq ($(MACHINE),aarch64)
+CROSS :=
+export CROSS
+DPDK_TARGET           ?= arm64-armv8a-linuxapp-$(DPDK_CC)
+DPDK_MACHINE          ?= armv8a
+DPDK_TUNE             ?= generic
+
+CPU_IMP_ARM                     = 0x41
+CPU_IMP_CAVIUM                  = 0x43
+
+CPU_PART_ARM_CORTEX_A53         = 0xd03
+CPU_PART_ARM_CORTEX_A57         = 0xd07
+CPU_PART_ARM_CORTEX_A72         = 0xd08
+CPU_PART_ARM_CORTEX_A73         = 0xd09
+
+CPU_PART_CAVIUM_THUNDERX        = 0x0a1
+CPU_PART_CAVIUM_THUNDERX_81XX   = 0x0a2
+CPU_PART_CAVIUM_THUNDERX_83XX   = 0x0a3
+
+MIDR_IMPLEMENTER=$(shell awk '/implementer/ {print $$4;exit}' /proc/cpuinfo)
+MIDR_PARTNUM=$(shell awk '/part/ {print $$4;exit}' /proc/cpuinfo)
+
+ifeq ($(MIDR_IMPLEMENTER),$(CPU_IMP_ARM))
+##############################################################################
+# Arm Cortex
+##############################################################################
+CPU_PART_ARM_TUNE := $(CPU_PART_ARM_CORTEX_A53)/cortex-a53 \
+		     $(CPU_PART_ARM_CORTEX_A57)/cortex-a57 \
+		     $(CPU_PART_ARM_CORTEX_A72)/cortex-a72 \
+		     $(CPU_PART_ARM_CORTEX_A73)/cortex-a73
+CPU_TUNE = $(notdir $(filter $(MIDR_PARTNUM)/%,$(CPU_PART_ARM_TUNE)))
+ifneq ($(CPU_TUNE),)
+DPDK_TUNE             = $(CPU_TUNE)
+else
+$(warning Unknown Arm CPU)
+endif
+
+else ifeq ($(MIDR_IMPLEMENTER),$(CPU_IMP_CAVIUM))
+##############################################################################
+# Cavium ThunderX
+##############################################################################
+ifneq (,$(findstring $(MIDR_PARTNUM),$(CPU_PART_CAVIUM_THUNDERX) \
+	$(CPU_PART_CAVIUM_THUNDERX_81XX) $(CPU_PART_CAVIUM_THUNDERX_83XX)))
+DPDK_TARGET           = arm64-thunderx-linuxapp-$(DPDK_CC)
+DPDK_MACHINE          = thunderx
+DPDK_CACHE_LINE_SIZE := 128
+else
+$(warning Unknown Cavium CPU)
+endif
+endif
+
+##############################################################################
+# Unknown platform
+##############################################################################
+else
+$(error Unknown platform)
+endif
+
+# /proc/cpuinfo does not exist on platforms without a /proc and on some
+# platforms, notably inside containers, it has no content. In those cases
+# we assume there's 1 processor; we use 2*ncpu for the -j option.
+# NB: GNU Make 4.2 will let us use '$(file </proc/cpuinfo)' to both test
+# for file presence and content; for now this will have to do.
+JOBS := $(if $(shell [ -f /proc/cpuinfo ] && head /proc/cpuinfo),\
+	$(shell grep -c ^processor /proc/cpuinfo), 2)
+
+# compiler/linker custom arguments
+ifeq ($(DPDK_CC),clang)
+DPDK_CPU_CFLAGS := -fPIE -fPIC
+else
+DPDK_CPU_CFLAGS := -pie -fPIC
+endif
+
+ifeq ($(DPDK_DEBUG),n)
+DPDK_EXTRA_CFLAGS := -g -mtune=$(DPDK_TUNE)
+else
+DPDK_EXTRA_CFLAGS := -g -O0
+endif
+
+# -Wimplicit-fallthrough was introduced starting from GCC 7,
+# and it requires newer version of ccache.
+# Disable fallthrough warning for old ccache version.
+ifeq ($(DPDK_CC),gcc)
+GCC_VER_V = "7.0.0"
+CCACHE_VER_V = "3.4.1"
+GCC_VER = $(shell gcc --version | grep ^gcc | sed 's/^.* //g')
+CCACHE_VER = $(shell ccache --version | grep ^ccache | sed 's/^.* //g')
+ifeq ($(shell expr "$(GCC_VER)" ">=" "$(GCC_VER_V)"),1)
+ifeq ($(shell expr "$(CCACHE_VER)" "<" "$(CCACHE_VER_V)"),1)
+DPDK_EXTRA_CFLAGS += -Wimplicit-fallthrough=0
+endif
+endif
+endif
+
+ifeq ($(AESNI),y)
+IPSEC_MB_BUILD_PATH := $(B)/intel-ipsec-mb-$(IPSEC_MB_VER)
+DPDK_EXTRA_CFLAGS += -L$(IPSEC_MB_BUILD_PATH) -I$(IPSEC_MB_BUILD_PATH)
+endif
+
+DPDK_MAKE_EXTRA_ARGS += AESNI_MULTI_BUFFER_LIB_PATH=$(AESNIMB_LIB_SOURCE)
+
+# assemble DPDK make arguments
+DPDK_MAKE_ARGS := -C $(DPDK_SOURCE) -j $(JOBS) \
+	T=$(DPDK_TARGET) \
+	RTE_CONFIG_TEMPLATE=../custom-config \
+	EXTRA_CFLAGS="$(DPDK_EXTRA_CFLAGS)" \
+	EXTRA_LDFLAGS="$(DPDK_EXTRA_LDFLAGS)" \
+	CPU_CFLAGS="$(DPDK_CPU_CFLAGS)" \
+	DESTDIR=$(I) \
+        $(DPDK_MAKE_EXTRA_ARGS)
+
+define set
+@if grep -q CONFIG_$1 $@ ; \
+	then sed -i -e 's/.*\(CONFIG_$1=\).*/\1$2/' $@ ; \
+	else echo CONFIG_$1=$2 >> $@ ; \
+fi
+endef
+
+all: build
+
+$(B)/custom-config: $(B)/.patch.ok Makefile
+	@echo --- generating custom config from $(DPDK_SOURCE)/config/defconfig_$(DPDK_TARGET) ---
+	@cpp -undef -ffreestanding -x assembler-with-cpp $(DPDK_SOURCE)/config/defconfig_$(DPDK_TARGET) $@
+	$(call set,RTE_MACHINE,$(DPDK_MACHINE))
+	@# modify options
+	$(call set,RTE_MAX_LCORE,256)
+	$(call set,RTE_PKTMBUF_HEADROOM,$(DPDK_PKTMBUF_HEADROOM))
+	$(call set,RTE_CACHE_LINE_SIZE,$(DPDK_CACHE_LINE_SIZE))
+	$(call set,RTE_LIBEAL_USE_HPET,y)
+	$(call set,RTE_BUILD_COMBINE_LIBS,y)
+	$(call set,RTE_PCI_CONFIG,y)
+	$(call set,RTE_PCI_EXTENDED_TAG,"on")
+	$(call set,RTE_PCI_MAX_READ_REQUEST_SIZE,4096)
+	@# enable debug init for device drivers
+	$(call set,RTE_LIBRTE_I40E_DEBUG_INIT,$(DPDK_DEBUG))
+	$(call set,RTE_LIBRTE_IXGBE_DEBUG_INIT,$(DPDK_DEBUG))
+	$(call set,RTE_LIBRTE_E1000_DEBUG_INIT,$(DPDK_DEBUG))
+	$(call set,RTE_LIBRTE_VIRTIO_DEBUG_INIT,$(DPDK_DEBUG))
+	$(call set,RTE_LIBRTE_VMXNET3_DEBUG_INIT,$(DPDK_DEBUG))
+	$(call set,RTE_LIBRTE_PMD_BOND,y)
+	$(call set,RTE_LIBRTE_IP_FRAG,y)
+	$(call set,RTE_LIBRTE_PMD_QAT,y)
+	$(call set,RTE_LIBRTE_PMD_AESNI_MB,$(AESNI))
+	$(call set,RTE_LIBRTE_PMD_AESNI_GCM,$(AESNI))
+	$(call set,RTE_LIBRTE_MLX4_PMD,$(DPDK_MLX4_PMD))
+	$(call set,RTE_LIBRTE_MLX5_PMD,$(DPDK_MLX5_PMD))
+	$(call set,RTE_LIBRTE_PMD_SOFTNIC,n)
+	$(call set,RTE_LIBRTE_MLX4_DLOPEN_DEPS,$(DPDK_MLX4_PMD))
+	$(call set,RTE_LIBRTE_MLX5_DLOPEN_DEPS,$(DPDK_MLX5_PMD))
+	$(call set,RTE_LIBRTE_PMD_TAP,$(DPDK_TAP_PMD))
+	$(call set,RTE_LIBRTE_PMD_FAILSAFE,$(DPDK_FAILSAFE_PMD))
+	@# not needed
+	$(call set,RTE_LIBRTE_CFGFILE,n)
+	$(call set,RTE_LIBRTE_LPM,n)
+	$(call set,RTE_LIBRTE_ACL,n)
+	$(call set,RTE_LIBRTE_POWER,n)
+	$(call set,RTE_LIBRTE_DISTRIBUTOR,n)
+	$(call set,RTE_LIBRTE_PORT,n)
+	$(call set,RTE_LIBRTE_TABLE,n)
+	$(call set,RTE_LIBRTE_PIPELINE,n)
+	$(call set,RTE_LIBRTE_FLOW_CLASSIFY,n)
+	$(call set,RTE_KNI_KMOD,n)
+	$(call set,RTE_EAL_IGB_UIO,n)
+	@# currently broken in 18.02
+	$(call set,RTE_LIBRTE_DPAA_BUS,n)
+	$(call set,RTE_LIBRTE_DPAA_MEMPOOL,n)
+	$(call set,RTE_LIBRTE_DPAA_PMD,n)
+	$(call set,RTE_LIBRTE_PMD_DPAA_SEC,n)
+	$(call set,RTE_LIBRTE_PMD_DPAA_EVENTDEV,n)
+	@rm -f .config.ok
+
+$(CURDIR)/$(DPDK_TARBALL):
+	@if [ -e $(DPDK_DOWNLOAD_DIR)/$(DPDK_TARBALL) ] ; \
+		then cp $(DPDK_DOWNLOAD_DIR)/$(DPDK_TARBALL) $(CURDIR) ; \
+		else curl -o $(CURDIR)/$(DPDK_TARBALL) -LO $(DPDK_TAR_URL) ; \
+	fi
+	@rm -f $(B)/.download.ok
+
+$(CURDIR)/$(NASM_TARBALL):
+	@if [ -e $(DPDK_DOWNLOAD_DIR)/$(NASM_TARBALL) ] ; \
+		then cp $(DPDK_DOWNLOAD_DIR)/$(NASM_TARBALL) $(CURDIR) ; \
+		else curl -o $(CURDIR)/$(NASM_TARBALL) -LO $(NASM_TAR_URL) ; \
+	fi
+
+$(CURDIR)/$(AESNIMB_LIB_TARBALL):
+	@if [ -e $(DPDK_DOWNLOAD_DIR)/$(AESNIMB_LIB_TARBALL) ] ; \
+		then cp $(DPDK_DOWNLOAD_DIR)/$(AESNIMB_LIB_TARBALL) $(CURDIR) ; \
+		else curl -o $@ -LO $(AESNIMB_LIB_TARBALL_URL) ; \
+	fi
+
+DPDK_DOWNLOADS = $(CURDIR)/$(DPDK_TARBALL)
+ifeq ($(AESNI),y)
+DPDK_DOWNLOADS += $(CURDIR)/$(NASM_TARBALL)
+DPDK_DOWNLOADS += $(CURDIR)/$(AESNIMB_LIB_TARBALL)
+endif
+
+$(B)/.download.ok: $(DPDK_DOWNLOADS)
+	@mkdir -p $(B)
+	@openssl md5 $< | cut -f 2 -d " " - > $(B)/$(DPDK_TARBALL).md5sum
+	@([ "$$(<$(B)/$(DPDK_TARBALL).md5sum)" = "$(DPDK_$(DPDK_VERSION)_TARBALL_MD5_CKSUM)" ] || \
+	( echo "Bad Checksum! Please remove $< and retry" && \
+		rm $(B)/$(DPDK_TARBALL).md5sum && false ))
+	@touch $@
+
+.PHONY: download
+download: $(B)/.download.ok
+
+$(B)/.extract.ok: $(B)/.download.ok
+	@echo --- extracting $(DPDK_TARBALL) ---
+	@tar --directory $(B) --extract --file $(CURDIR)/$(DPDK_TARBALL)
+ifeq ($(AESNI),y)
+	@echo --- extracting $(NASM_TARBALL) ---
+	@tar --directory $(B) --extract --file $(CURDIR)/$(NASM_TARBALL)
+	@echo --- extracting $(AESNIMB_LIB_TARBALL) ---
+	@tar --directory $(B) --extract --file $(CURDIR)/$(AESNIMB_LIB_TARBALL)
+endif
+	@touch $@
+
+.PHONY: extract
+extract: $(B)/.extract.ok
+
+$(B)/.patch.ok: $(B)/.extract.ok
+ifneq ($(wildcard $(CURDIR)/dpdk-$(DPDK_VERSION)_patches/*.patch),)
+	@echo --- patching ---
+	@for f in $(CURDIR)/dpdk-$(DPDK_VERSION)_patches/*.patch ; do \
+		echo Applying patch: $$(basename $$f) ; \
+		patch -p1 -d $(DPDK_SOURCE) < $$f ; \
+	done
+endif
+	@touch $@
+
+.PHONY: patch
+patch: $(B)/.patch.ok
+
+$(B)/.config.ok: $(B)/.patch.ok $(B)/custom-config
+	@make $(DPDK_MAKE_ARGS) config
+	@touch $@
+
+.PHONY: config
+config: $(B)/.config.ok
+
+.PHONY: build-nasm
+build-nasm:
+	cd $(NASM_SOURCE) && sh configure && make -j
+
+.PHONY: build-ipsec-mb
+build-ipsec-mb:
+	mkdir -p $(I)/lib/
+	# Do not build GCM stuff if we are building ISA_L
+	make -C $(AESNIMB_LIB_SOURCE) -j SHARED=n \
+	  EXTRA_CFLAGS=-fPIC NASM=$(NASM_SOURCE)/nasm
+	cp $(AESNIMB_LIB_SOURCE)/libIPSec_MB.a $(I)/lib/
+
+.PHONY: build-dpdk
+build-dpdk:
+	@if [ ! -e $(B)/.config.ok ] ; then echo 'Please run "make config" first' && false ; fi
+	@make $(DPDK_MAKE_ARGS) install
+
+# Order matters
+ifeq ($(AESNI),y)
+BUILD_TARGETS += build-nasm
+BUILD_TARGETS += build-ipsec-mb
+endif
+BUILD_TARGETS += build-dpdk
+
+$(B)/.build.ok: $(BUILD_TARGETS)
+	@touch $@
+
+.PHONY: build
+build: $(B)/.build.ok
+
+.PHONY: install
+install: $(B)/.build.ok
+
+.PHONY: clean
+clean:
+	@rm -rf $(B) $(I)
+
+##############################################################################
+# .deb packaging
+##############################################################################
+
+DEB_VER := $(PKG_VERSION)
+DEB_ARCH=$(shell dpkg --print-architecture 2> /dev/null)
+DEV_DEB=vpp-ext-deps_$(DEB_VER)-$(PKG_SUFFIX)_$(DEB_ARCH).deb
+INSTALLED_VER=$(shell dpkg-query --showformat='$${Version}' --show vpp-ext-deps 2> /dev/null)
+
+.PHONY: build-deb install-deb check-deb
+
+deb/debian/changelog: Makefile
+	@echo "vpp-ext-deps ($(DEB_VER)-$(PKG_SUFFIX)) unstable; urgency=low" > $@
+	@echo "" >> $@
+	@echo "  * Version $(DEB_VER)" >> $@
+	@echo "" >> $@
+	@echo " -- VPP Dev <vpp-dev@lists.fd.io>  $(shell date -R)" >> $@
+
+$(DEV_DEB): deb/debian/changelog
+	@cd deb && dpkg-buildpackage -b -uc -us
+	git clean -fdx deb
+
+build-deb: $(DEV_DEB)
+
+install-deb:
+ifneq ($(INSTALLED_VER),$(DEB_VER)-$(PKG_SUFFIX))
+	@echo "=========================================================="
+	@echo " Out of date vpp-ext-deps package installed."
+	@echo " Installed: $(INSTALLED_VER)"
+	@echo " Needed: $(DEB_VER)-$(PKG_SUFFIX)"
+	@echo "=========================================================="
+	@make $(DEV_DEB)
+	@sudo dpkg -i $(DEV_DEB)
+else
+	@echo "=========================================================="
+	@echo " Up-to-date vpp-ext-deps package already installed"
+	@echo "=========================================================="
+endif
+
+check-deb:
+ifneq ($(INSTALLED_VER),$(DEB_VER)-$(PKG_SUFFIX))
+	@echo "=========================================================="
+	@echo " Outdated DPDK package detected:"
+	@echo "  Installed: vpp-ext-deps $(INSTALLED_VER)"
+	@echo "  Current:   vpp-ext-deps $(DEB_VER)-$(PKG_SUFFIX)"
+	@echo ""
+	@echo " Please upgrade by invoking 'make install-ext-deps'"
+	@echo " from the top level directory."
+	@echo "=========================================================="
+endif
+
+##############################################################################
+# .rpm packaging
+##############################################################################
+
+RPM_VER := $(PKG_VERSION)
+RPM_ARCH=$(shell rpm --eval "%{_arch}" 2> /dev/null)
+DEV_RPM=vpp-ext-deps-$(RPM_VER)-$(PKG_SUFFIX).$(RPM_ARCH).rpm
+INSTALLED_RPM_VER=$(shell rpm -q --queryformat '%{VERSION}-%{RELEASE}' vpp-ext-deps 2> /dev/null | grep -v "not inst")
+
+.PHONY: build-rpm install-rpm check-rpm
+
+$(DEV_RPM): Makefile rpm/vpp-ext-deps.spec
+	@rpmbuild -bb \
+	  --define "_topdir $(CURDIR)/rpm" \
+	  --define "_version $(RPM_VER)" \
+	  --define "_release $(PKG_SUFFIX)" \
+	  $(CURDIR)/rpm/vpp-ext-deps.spec
+	mv rpm/RPMS/$(RPM_ARCH)/*.rpm .
+	git clean -fdx rpm
+
+build-rpm: $(DEV_RPM)
+
+install-rpm:
+ifneq ($(INSTALLED_RPM_VER),$(PRM_VER)-$(PKG_SUFFIX))
+	@$(MAKE) $(DEV_RPM)
+	sudo rpm -Uih $(DEV_RPM)
+else
+	@echo "=========================================================="
+	@echo " Up-to-date DPDK package already installed"
+	@echo "=========================================================="
+endif
+
+check-rpm:
+ifneq ($(INSTALLED_RPM_VER),$(RPM_VER)-$(PKG_SUFFIX))
+	@echo "=========================================================="
+	@echo " Outdated DPDK package detected:"
+	@echo "  Installed: vpp-ext-deps $(INSTALLED_RPM_VER)"
+	@echo "  Current:   vpp-ext-deps $(RPM_VER)-$(PKG_SUFFIX)"
+	@echo ""
+	@echo " Please upgrade by invoking 'make install-ext-deps'"
+	@echo " from the top level directory."
+	@echo "=========================================================="
+endif
+
+##############################################################################
+# ebuild support
+##############################################################################
+
+.PHONY: ebuild-build ebuild-install
+
+ebuild-build:
+ifeq ($(INSTALLED_VER)$(INSTALLED_RPM_VER),)
+	@echo "=========================================================="
+	@echo "Building DPDK from source. Consider installing development"
+	@echo "package by invoking 'make install-ext-deps' from the"
+	@echo "top level directory"
+	@echo "=========================================================="
+	make config
+else
+ifneq ($(INSTALLED_VER),)
+	make check-deb
+endif
+ifneq ($(INSTALLED_RPM_VER),)
+	make check-rpm
+endif
+endif
+
+ebuild-install:
+ifeq ($(INSTALLED_VER)$(INSTALLED_RPM_VER),)
+	make install
+endif
diff --git a/build/external/README b/build/external/README
new file mode 100644
index 0000000..95154dc
--- /dev/null
+++ b/build/external/README
@@ -0,0 +1,34 @@
+
+Changes needed to DPDK are stored here as git patch files. Maintaining these
+files using “git format-patch” and “git am” will make it simpler to manage
+these changes. Patches made to DPDK should only be temporary until they are
+accepted upstream and made available in the next DPDK release.
+
+The following is the method used to generate these patches:
+
+1. Git clone the DPDK to a new directory:
+ # git clone http://dpdk.org/git/dpdk dpdk
+
+2. Create a branch based on the DPDK release you wish to patch.
+Note, “git tag” will show you all the release tags. The following example is
+for DPDK release tag “v2.2.0” and will create a branch named “two_dot_two”.
+ # cd dpdk
+ # git checkout -b two_dot_two v2.2.0
+
+3. Apply all the existing VPP DPDK patches to this new branch.
+ # git am <VPP directory>/dpdk/dpdk-2.2.0_patches/*
+
+4. Make your changes and commit your change to your DPDK repository.
+ # <edit files>
+ # git commit -s
+
+5. Create the patch files with format-patch. This creates all the patch files
+for your branch (two_dot_two), with your latest commits as the last ones.
+ # git format-patch master..two_dot_two
+
+6. Copy, add and commit the new patches into the VPP patches directory.
+ # cp <new patch files> <VPP directory>/dpdk/dpdk-2.2.0_patches
+ # cd <VPP directory>
+ # git add dpdk/dpdk-2.2.0_patches/<new patch files>
+ # git commit -s
+
diff --git a/build/external/deb/debian/compat b/build/external/deb/debian/compat
new file mode 100755
index 0000000..ec63514
--- /dev/null
+++ b/build/external/deb/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/build/external/deb/debian/control b/build/external/deb/debian/control
new file mode 100644
index 0000000..80b81b8
--- /dev/null
+++ b/build/external/deb/debian/control
@@ -0,0 +1,13 @@
+Source: vpp-ext-deps
+Section: net
+Priority: extra
+Maintainer: vpp-dev@lists.fd.io
+Build-Depends: debhelper (>= 9), dkms
+Standards-Version: 3.9.4
+
+Package: vpp-ext-deps
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: VPP developer package containing dependencies
+Replaces: vpp-dpdk-dev
+Conflicts: vpp-dpdk-dev
diff --git a/build/external/deb/debian/dkms/Makefile b/build/external/deb/debian/dkms/Makefile
new file mode 100644
index 0000000..452c7c2
--- /dev/null
+++ b/build/external/deb/debian/dkms/Makefile
@@ -0,0 +1,10 @@
+obj-m:=igb_uio.o
+
+CONFIG_MODULE_SIG=n
+
+EXTRA_CFLAGS += -Winline -I$(PWD)
+
+default:
+	    $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
+clean:
+	    $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
diff --git a/build/external/deb/debian/rules b/build/external/deb/debian/rules
new file mode 100755
index 0000000..6ffefc1
--- /dev/null
+++ b/build/external/deb/debian/rules
@@ -0,0 +1,27 @@
+#!/usr/bin/make -f
+DH_VERBOSE = 1
+PKG=vpp-ext-deps
+
+VERSION = $(shell dpkg-parsechangelog | sed -nr '/^Version:/s/Version: //p')
+BASE_VER = $(word 1, $(subst -, ,$(VERSION)))
+
+export DPDK_BUILD_DIR=$(CURDIR)/_build
+export DPDK_INSTALL_DIR=$(CURDIR)/debian/tmp/opt/vpp/external/$(shell uname -m)/
+
+MAKE_ARGS=-C ..
+
+include /usr/share/dpkg/default.mk
+
+%:
+	dh $@
+
+override_dh_clean:
+	make $(MAKE_ARGS) clean
+
+override_dh_auto_configure:
+	make $(MAKE_ARGS) config
+
+override_dh_install:
+	make $(MAKE_ARGS) install
+	dh_install -p$(PKG) --autodest /opt
+
diff --git a/build/external/dpdk-18.02.1_patches/0001-ixgbe-link-wait-longer.patch b/build/external/dpdk-18.02.1_patches/0001-ixgbe-link-wait-longer.patch
new file mode 100644
index 0000000..fb71e94
--- /dev/null
+++ b/build/external/dpdk-18.02.1_patches/0001-ixgbe-link-wait-longer.patch
@@ -0,0 +1,13 @@
+diff --git a/drivers/net/ixgbe/base/ixgbe_common.c b/drivers/net/ixgbe/base/ixgbe_common.c
+index e7e9256e5..2fb0a072c 100644
+--- a/drivers/net/ixgbe/base/ixgbe_common.c
++++ b/drivers/net/ixgbe/base/ixgbe_common.c
+@@ -5296,7 +5296,7 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
+ 		 * Section 73.10.2, we may have to wait up to 500ms if KR is
+ 		 * attempted.  82599 uses the same timing for 10g SFI.
+ 		 */
+-		for (i = 0; i < 5; i++) {
++		for (i = 0; i < 10; i++) {
+ 			/* Wait for the link partner to also set speed */
+ 			msec_delay(100);
+ 
diff --git a/build/external/dpdk-18.02_patches/0001-assign-QAT-cryptodev-to-correct-NUMA-node.patch b/build/external/dpdk-18.02_patches/0001-assign-QAT-cryptodev-to-correct-NUMA-node.patch
new file mode 100644
index 0000000..32d2c78
--- /dev/null
+++ b/build/external/dpdk-18.02_patches/0001-assign-QAT-cryptodev-to-correct-NUMA-node.patch
@@ -0,0 +1,29 @@
+From b01857dfdb4e46b8c7d306c608c58ebbabc252ec Mon Sep 17 00:00:00 2001
+From: Lee Roberts <lee.roberts@hpe.com>
+Date: Fri, 9 Mar 2018 10:49:34 -0700
+Subject: [PATCH] assign QAT cryptodev to correct NUMA node
+
+rte_cryptodev_pmd_init_params should use NUMA node of the QAT device
+for its socket_id rather than the socket_id of the initializing process.
+
+Signed-off-by: Lee Roberts <lee.roberts@hpe.com>
+---
+ drivers/crypto/qat/rte_qat_cryptodev.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/crypto/qat/rte_qat_cryptodev.c b/drivers/crypto/qat/rte_qat_cryptodev.c
+index bf83740..82641aa 100644
+--- a/drivers/crypto/qat/rte_qat_cryptodev.c
++++ b/drivers/crypto/qat/rte_qat_cryptodev.c
+@@ -130,7 +130,7 @@ static int crypto_qat_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
+ {
+ 	struct rte_cryptodev_pmd_init_params init_params = {
+ 		.name = "",
+-		.socket_id = rte_socket_id(),
++		.socket_id = pci_dev->device.numa_node,
+ 		.private_data_size = sizeof(struct qat_pmd_private),
+ 		.max_nb_sessions = RTE_QAT_PMD_MAX_NB_SESSIONS
+ 	};
+-- 
+1.9.1
+
diff --git a/build/external/dpdk-18.05_patches/0001-i40evf-don-t-reset-device_info-data.patch b/build/external/dpdk-18.05_patches/0001-i40evf-don-t-reset-device_info-data.patch
new file mode 100644
index 0000000..b737461
--- /dev/null
+++ b/build/external/dpdk-18.05_patches/0001-i40evf-don-t-reset-device_info-data.patch
@@ -0,0 +1,28 @@
+From 65a8641604212d58defd71491c900d84d662a086 Mon Sep 17 00:00:00 2001
+From: Damjan Marion <damarion@cisco.com>
+Date: Wed, 6 Jun 2018 21:57:58 +0200
+Subject: [PATCH] i40evf: don't reset device_info data
+
+At this point valid data is already set by rte_eth_get_device_info.
+device field becomes zero and consumer is not able retrieve pci data.
+
+Signed-off-by: Damjan Marion <damarion@cisco.com>
+---
+ drivers/net/i40e/i40e_ethdev_vf.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/drivers/net/i40e/i40e_ethdev_vf.c b/drivers/net/i40e/i40e_ethdev_vf.c
+index 804e44530..86b38d202 100644
+--- a/drivers/net/i40e/i40e_ethdev_vf.c
++++ b/drivers/net/i40e/i40e_ethdev_vf.c
+@@ -2182,7 +2182,6 @@ i40evf_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
+ {
+ 	struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
+ 
+-	memset(dev_info, 0, sizeof(*dev_info));
+ 	dev_info->max_rx_queues = vf->vsi_res->num_queue_pairs;
+ 	dev_info->max_tx_queues = vf->vsi_res->num_queue_pairs;
+ 	dev_info->min_rx_bufsize = I40E_BUF_SIZE_MIN;
+-- 
+2.17.1
+
diff --git a/build/external/dpdk-18.05_patches/0002-ixgbe-link-wait-longer.patch b/build/external/dpdk-18.05_patches/0002-ixgbe-link-wait-longer.patch
new file mode 100644
index 0000000..fb71e94
--- /dev/null
+++ b/build/external/dpdk-18.05_patches/0002-ixgbe-link-wait-longer.patch
@@ -0,0 +1,13 @@
+diff --git a/drivers/net/ixgbe/base/ixgbe_common.c b/drivers/net/ixgbe/base/ixgbe_common.c
+index e7e9256e5..2fb0a072c 100644
+--- a/drivers/net/ixgbe/base/ixgbe_common.c
++++ b/drivers/net/ixgbe/base/ixgbe_common.c
+@@ -5296,7 +5296,7 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
+ 		 * Section 73.10.2, we may have to wait up to 500ms if KR is
+ 		 * attempted.  82599 uses the same timing for 10g SFI.
+ 		 */
+-		for (i = 0; i < 5; i++) {
++		for (i = 0; i < 10; i++) {
+ 			/* Wait for the link partner to also set speed */
+ 			msec_delay(100);
+ 
diff --git a/build/external/dpdk-18.08_patches/0001-net-mlx5-support-externally-allocated-mempool.patch b/build/external/dpdk-18.08_patches/0001-net-mlx5-support-externally-allocated-mempool.patch
new file mode 100644
index 0000000..87c9cf9
--- /dev/null
+++ b/build/external/dpdk-18.08_patches/0001-net-mlx5-support-externally-allocated-mempool.patch
@@ -0,0 +1,270 @@
+From bd42c77c457146bede32333558b4e0414b30683e Mon Sep 17 00:00:00 2001
+From: Yongseok Koh <yskoh@mellanox.com>
+Date: Fri, 24 Aug 2018 16:46:49 -0700
+Subject: [PATCH] net/mlx5: support externally allocated mempool
+
+When MLX PMD registers memory for DMA, it accesses the global memseg list
+of DPDK to maximize the range of registration so that LKey search can be
+more efficient. Granularity of MR registration is per page.
+
+Externally allocated memory shouldn't be used for DMA because it can't be
+searched in the memseg list and free event can't be tracked by DPDK.
+However, if the external memory is static (allocated on startup and never
+freed), such memory can also be registered by little tweak in the code.
+
+Signed-off-by: Yongseok Koh <yskoh@mellanox.com>
+---
+ drivers/net/mlx5/mlx5_mr.c   | 155 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/net/mlx5/mlx5_rxtx.h |  35 +++++++++-
+ 2 files changed, 189 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/mlx5/mlx5_mr.c b/drivers/net/mlx5/mlx5_mr.c
+index 08105a443..876622e91 100644
+--- a/drivers/net/mlx5/mlx5_mr.c
++++ b/drivers/net/mlx5/mlx5_mr.c
+@@ -277,6 +277,23 @@ mr_find_next_chunk(struct mlx5_mr *mr, struct mlx5_mr_cache *entry,
+ 	uintptr_t end = 0;
+ 	uint32_t idx = 0;
+ 
++	/* MR for external memory doesn't have memseg list. */
++	if (mr->msl == NULL) {
++		struct ibv_mr *ibv_mr = mr->ibv_mr;
++
++		assert(mr->ms_bmp_n == 1);
++		assert(mr->ms_n == 1);
++		assert(base_idx == 0);
++		/*
++		 * Can't search it from memseg list but get it directly from
++		 * verbs MR as there's only one chunk.
++		 */
++		entry->start = (uintptr_t)ibv_mr->addr;
++		entry->end = (uintptr_t)ibv_mr->addr + mr->ibv_mr->length;
++		entry->lkey = rte_cpu_to_be_32(mr->ibv_mr->lkey);
++		/* Returning 1 ends iteration. */
++		return 1;
++	}
+ 	for (idx = base_idx; idx < mr->ms_bmp_n; ++idx) {
+ 		if (rte_bitmap_get(mr->ms_bmp, idx)) {
+ 			const struct rte_memseg_list *msl;
+@@ -818,6 +835,7 @@ mlx5_mr_mem_event_free_cb(struct rte_eth_dev *dev, const void *addr, size_t len)
+ 		mr = mr_lookup_dev_list(dev, &entry, start);
+ 		if (mr == NULL)
+ 			continue;
++		assert(mr->msl); /* Can't be external memory. */
+ 		ms = rte_mem_virt2memseg((void *)start, msl);
+ 		assert(ms != NULL);
+ 		assert(msl->page_sz == ms->hugepage_sz);
+@@ -1070,6 +1088,139 @@ mlx5_mr_flush_local_cache(struct mlx5_mr_ctrl *mr_ctrl)
+ 		(void *)mr_ctrl, mr_ctrl->cur_gen);
+ }
+ 
++/**
++ * Called during rte_mempool_mem_iter() by mlx5_mr_update_ext_mp().
++ *
++ * Externally allocated chunk is registered and a MR is created for the chunk.
++ * The MR object is added to the global list. If memseg list of a MR object
++ * (mr->msl) is null, the MR object can be regarded as externally allocated
++ * memory.
++ *
++ * Once external memory is registered, it should be static. If the memory is
++ * freed and the virtual address range has different physical memory mapped
++ * again, it may cause crash on device due to the wrong translation entry. PMD
++ * can't track the free event of the external memory for now.
++ */
++static void
++mlx5_mr_update_ext_mp_cb(struct rte_mempool *mp, void *opaque,
++			 struct rte_mempool_memhdr *memhdr,
++			 unsigned mem_idx __rte_unused)
++{
++	struct mr_update_mp_data *data = opaque;
++	struct rte_eth_dev *dev = data->dev;
++	struct priv *priv = dev->data->dev_private;
++	struct mlx5_mr_ctrl *mr_ctrl = data->mr_ctrl;
++	struct mlx5_mr *mr = NULL;
++	uintptr_t addr = (uintptr_t)memhdr->addr;
++	size_t len = memhdr->len;
++	struct mlx5_mr_cache entry;
++	uint32_t lkey;
++
++	/* If already registered, it should return. */
++	rte_rwlock_read_lock(&priv->mr.rwlock);
++	lkey = mr_lookup_dev(dev, &entry, addr);
++	rte_rwlock_read_unlock(&priv->mr.rwlock);
++	if (lkey != UINT32_MAX)
++		return;
++	mr = rte_zmalloc_socket(NULL,
++				RTE_ALIGN_CEIL(sizeof(*mr),
++					       RTE_CACHE_LINE_SIZE),
++				RTE_CACHE_LINE_SIZE, mp->socket_id);
++	if (mr == NULL) {
++		DRV_LOG(WARNING,
++			"port %u unable to allocate memory for a new MR of"
++			" mempool (%s).",
++			dev->data->port_id, mp->name);
++		data->ret = -1;
++		return;
++	}
++	DRV_LOG(DEBUG, "port %u register MR for chunk #%d of mempool (%s)",
++		dev->data->port_id, mem_idx, mp->name);
++	mr->ibv_mr = mlx5_glue->reg_mr(priv->pd, (void *)addr, len,
++				       IBV_ACCESS_LOCAL_WRITE);
++	if (mr->ibv_mr == NULL) {
++		DRV_LOG(WARNING,
++			"port %u fail to create a verbs MR for address (%p)",
++			dev->data->port_id, (void *)addr);
++		rte_free(mr);
++		data->ret = -1;
++		return;
++	}
++	mr->msl = NULL; /* Mark it is external memory. */
++	mr->ms_bmp = NULL;
++	mr->ms_n = 1;
++	mr->ms_bmp_n = 1;
++	rte_rwlock_write_lock(&priv->mr.rwlock);
++	LIST_INSERT_HEAD(&priv->mr.mr_list, mr, mr);
++	DRV_LOG(DEBUG,
++		"port %u MR CREATED (%p) for external memory %p:\n"
++		"  [0x%" PRIxPTR ", 0x%" PRIxPTR "),"
++		" lkey=0x%x base_idx=%u ms_n=%u, ms_bmp_n=%u",
++		dev->data->port_id, (void *)mr, (void *)addr,
++		addr, addr + len, rte_cpu_to_be_32(mr->ibv_mr->lkey),
++		mr->ms_base_idx, mr->ms_n, mr->ms_bmp_n);
++	/* Insert to the global cache table. */
++	mr_insert_dev_cache(dev, mr);
++	rte_rwlock_write_unlock(&priv->mr.rwlock);
++	/* Insert to the local cache table */
++	mlx5_mr_addr2mr_bh(dev, mr_ctrl, addr);
++}
++
++/**
++ * Register MR for entire memory chunks in a Mempool having externally allocated
++ * memory and fill in local cache.
++ *
++ * @param dev
++ *   Pointer to Ethernet device.
++ * @param mr_ctrl
++ *   Pointer to per-queue MR control structure.
++ * @param mp
++ *   Pointer to registering Mempool.
++ *
++ * @return
++ *   0 on success, -1 on failure.
++ */
++static uint32_t
++mlx5_mr_update_ext_mp(struct rte_eth_dev *dev, struct mlx5_mr_ctrl *mr_ctrl,
++		      struct rte_mempool *mp)
++{
++	struct mr_update_mp_data data = {
++		.dev = dev,
++		.mr_ctrl = mr_ctrl,
++		.ret = 0,
++	};
++
++	rte_mempool_mem_iter(mp, mlx5_mr_update_ext_mp_cb, &data);
++	return data.ret;
++}
++
++/**
++ * Register MR entire memory chunks in a Mempool having externally allocated
++ * memory and search LKey of the address to return.
++ *
++ * @param dev
++ *   Pointer to Ethernet device.
++ * @param addr
++ *   Search key.
++ * @param mp
++ *   Pointer to registering Mempool where addr belongs.
++ *
++ * @return
++ *   LKey for address on success, UINT32_MAX on failure.
++ */
++uint32_t
++mlx5_tx_update_ext_mp(struct mlx5_txq_data *txq, uintptr_t addr,
++		      struct rte_mempool *mp)
++{
++	struct mlx5_txq_ctrl *txq_ctrl =
++		container_of(txq, struct mlx5_txq_ctrl, txq);
++	struct mlx5_mr_ctrl *mr_ctrl = &txq->mr_ctrl;
++	struct priv *priv = txq_ctrl->priv;
++
++	mlx5_mr_update_ext_mp(ETH_DEV(priv), mr_ctrl, mp);
++	return mlx5_tx_addr2mr_bh(txq, addr);
++}
++
+ /* Called during rte_mempool_mem_iter() by mlx5_mr_update_mp(). */
+ static void
+ mlx5_mr_update_mp_cb(struct rte_mempool *mp __rte_unused, void *opaque,
+@@ -1113,6 +1264,10 @@ mlx5_mr_update_mp(struct rte_eth_dev *dev, struct mlx5_mr_ctrl *mr_ctrl,
+ 	};
+ 
+ 	rte_mempool_mem_iter(mp, mlx5_mr_update_mp_cb, &data);
++	if (data.ret < 0 && rte_errno == ENXIO) {
++		/* Mempool may have externally allocated memory. */
++		return mlx5_mr_update_ext_mp(dev, mr_ctrl, mp);
++	}
+ 	return data.ret;
+ }
+ 
+diff --git a/drivers/net/mlx5/mlx5_rxtx.h b/drivers/net/mlx5/mlx5_rxtx.h
+index f53bb43c3..b61c23b33 100644
+--- a/drivers/net/mlx5/mlx5_rxtx.h
++++ b/drivers/net/mlx5/mlx5_rxtx.h
+@@ -347,6 +347,8 @@ uint16_t mlx5_rx_burst_vec(void *dpdk_txq, struct rte_mbuf **pkts,
+ void mlx5_mr_flush_local_cache(struct mlx5_mr_ctrl *mr_ctrl);
+ uint32_t mlx5_rx_addr2mr_bh(struct mlx5_rxq_data *rxq, uintptr_t addr);
+ uint32_t mlx5_tx_addr2mr_bh(struct mlx5_txq_data *txq, uintptr_t addr);
++uint32_t mlx5_tx_update_ext_mp(struct mlx5_txq_data *txq, uintptr_t addr,
++			       struct rte_mempool *mp);
+ 
+ #ifndef NDEBUG
+ /**
+@@ -534,6 +536,24 @@ mlx5_tx_complete(struct mlx5_txq_data *txq)
+ }
+ 
+ /**
++ * Get Memory Pool (MP) from mbuf. If mbuf is indirect, the pool from which the
++ * cloned mbuf is allocated is returned instead.
++ *
++ * @param buf
++ *   Pointer to mbuf.
++ *
++ * @return
++ *   Memory pool where data is located for given mbuf.
++ */
++static struct rte_mempool *
++mlx5_mb2mp(struct rte_mbuf *buf)
++{
++	if (unlikely(RTE_MBUF_INDIRECT(buf)))
++		return rte_mbuf_from_indirect(buf)->pool;
++	return buf->pool;
++}
++
++/**
+  * Query LKey from a packet buffer for Rx. No need to flush local caches for Rx
+  * as mempool is pre-configured and static.
+  *
+@@ -591,7 +611,20 @@ mlx5_tx_addr2mr(struct mlx5_txq_data *txq, uintptr_t addr)
+ 	return mlx5_tx_addr2mr_bh(txq, addr);
+ }
+ 
+-#define mlx5_tx_mb2mr(rxq, mb) mlx5_tx_addr2mr(rxq, (uintptr_t)((mb)->buf_addr))
++static __rte_always_inline uint32_t
++mlx5_tx_mb2mr(struct mlx5_txq_data *txq, struct rte_mbuf *mb)
++{
++	uintptr_t addr = (uintptr_t)mb->buf_addr;
++	uint32_t lkey = mlx5_tx_addr2mr(txq, addr);
++
++	if (likely(lkey != UINT32_MAX))
++		return lkey;
++	if (rte_errno == ENXIO) {
++		/* Mempool may have externally allocated memory. */
++		lkey = mlx5_tx_update_ext_mp(txq, addr, mlx5_mb2mp(mb));
++	}
++	return lkey;
++}
+ 
+ /**
+  * Ring TX queue doorbell and flush the update if requested.
+-- 
+2.11.0
+
diff --git a/build/external/dpdk-18.08_patches/0002-mlx4-support-externally-allocated-mempool.patch b/build/external/dpdk-18.08_patches/0002-mlx4-support-externally-allocated-mempool.patch
new file mode 100644
index 0000000..b328623
--- /dev/null
+++ b/build/external/dpdk-18.08_patches/0002-mlx4-support-externally-allocated-mempool.patch
@@ -0,0 +1,250 @@
+From c947fd2ec67e9bbacb8b106f320f6e6bae5a9731 Mon Sep 17 00:00:00 2001
+From: Matthew Smith <mgsmith@netgate.com>
+Date: Tue, 28 Aug 2018 13:21:04 -0500
+Subject: [PATCH] mlx4: support externally allocated mempool
+
+Port Mellanox mlx5 PMD patch to work for mlx4 PMD.
+
+Signed-off-by: Matthew Smith <mgsmith@netgate.com>
+---
+ drivers/net/mlx4/mlx4_mr.c   | 150 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/net/mlx4/mlx4_rxtx.h |  35 +++++++++-
+ 2 files changed, 184 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/mlx4/mlx4_mr.c b/drivers/net/mlx4/mlx4_mr.c
+index d23d3c613..55e5555ce 100644
+--- a/drivers/net/mlx4/mlx4_mr.c
++++ b/drivers/net/mlx4/mlx4_mr.c
+@@ -289,6 +289,23 @@ mr_find_next_chunk(struct mlx4_mr *mr, struct mlx4_mr_cache *entry,
+ 	uintptr_t end = 0;
+ 	uint32_t idx = 0;
+ 
++	/* MR for external memory doesn't have memseg list. */
++	if (mr->msl == NULL) {
++		struct ibv_mr *ibv_mr = mr->ibv_mr;
++
++		assert(mr->ms_bmp_n == 1);
++		assert(mr->ms_n == 1);
++		assert(base_idx == 0);
++		/*
++		 * Can't search it from memseg list but get it directly from
++		 * verbs MR as there's only one chunk.
++		 */
++		entry->start = (uintptr_t)ibv_mr->addr;
++		entry->end = (uintptr_t)ibv_mr->addr + mr->ibv_mr->length;
++		entry->lkey = rte_cpu_to_be_32(mr->ibv_mr->lkey);
++		/* Returning 1 ends iteration. */
++		return 1;
++	}
+ 	for (idx = base_idx; idx < mr->ms_bmp_n; ++idx) {
+ 		if (rte_bitmap_get(mr->ms_bmp, idx)) {
+ 			const struct rte_memseg_list *msl;
+@@ -809,6 +826,7 @@ mlx4_mr_mem_event_free_cb(struct rte_eth_dev *dev, const void *addr, size_t len)
+ 		mr = mr_lookup_dev_list(dev, &entry, start);
+ 		if (mr == NULL)
+ 			continue;
++		assert(mr->msl); /* Can't be external memory. */
+ 		ms = rte_mem_virt2memseg((void *)start, msl);
+ 		assert(ms != NULL);
+ 		assert(msl->page_sz == ms->hugepage_sz);
+@@ -1055,6 +1073,134 @@ mlx4_mr_flush_local_cache(struct mlx4_mr_ctrl *mr_ctrl)
+ 	      (void *)mr_ctrl, mr_ctrl->cur_gen);
+ }
+ 
++/**
++ * Called during rte_mempool_mem_iter() by mlx4_mr_update_ext_mp().
++ *
++ * Externally allocated chunk is registered and a MR is created for the chunk.
++ * The MR object is added to the global list. If memseg list of a MR object
++ * (mr->msl) is null, the MR object can be regarded as externally allocated
++ * memory.
++ *
++ * Once external memory is registered, it should be static. If the memory is
++ * freed and the virtual address range has different physical memory mapped
++ * again, it may cause crash on device due to the wrong translation entry. PMD
++ * can't track the free event of the external memory for now.
++ */
++static void
++mlx4_mr_update_ext_mp_cb(struct rte_mempool *mp, void *opaque,
++			 struct rte_mempool_memhdr *memhdr,
++			 unsigned mem_idx __rte_unused)
++{
++	struct mr_update_mp_data *data = opaque;
++	struct rte_eth_dev *dev = data->dev;
++	struct priv *priv = dev->data->dev_private;
++	struct mlx4_mr_ctrl *mr_ctrl = data->mr_ctrl;
++	struct mlx4_mr *mr = NULL;
++	uintptr_t addr = (uintptr_t)memhdr->addr;
++	size_t len = memhdr->len;
++	struct mlx4_mr_cache entry;
++	uint32_t lkey;
++
++	/* If already registered, it should return. */
++	rte_rwlock_read_lock(&priv->mr.rwlock);
++	lkey = mr_lookup_dev(dev, &entry, addr);
++	rte_rwlock_read_unlock(&priv->mr.rwlock);
++	if (lkey != UINT32_MAX)
++		return;
++	mr = rte_zmalloc_socket(NULL,
++				RTE_ALIGN_CEIL(sizeof(*mr),
++					       RTE_CACHE_LINE_SIZE),
++				RTE_CACHE_LINE_SIZE, mp->socket_id);
++	if (mr == NULL) {
++		WARN("port %u unable to allocate memory for a new MR of"
++		     " mempool (%s).",
++		     dev->data->port_id, mp->name);
++		data->ret = -1;
++		return;
++	}
++	DEBUG("port %u register MR for chunk #%d of mempool (%s)",
++	      dev->data->port_id, mem_idx, mp->name);
++	mr->ibv_mr = mlx4_glue->reg_mr(priv->pd, (void *)addr, len,
++				       IBV_ACCESS_LOCAL_WRITE);
++	if (mr->ibv_mr == NULL) {
++		WARN("port %u fail to create a verbs MR for address (%p)",
++		     dev->data->port_id, (void *)addr);
++		rte_free(mr);
++		data->ret = -1;
++		return;
++	}
++	mr->msl = NULL; /* Mark it is external memory. */
++	mr->ms_bmp = NULL;
++	mr->ms_n = 1;
++	mr->ms_bmp_n = 1;
++	rte_rwlock_write_lock(&priv->mr.rwlock);
++	LIST_INSERT_HEAD(&priv->mr.mr_list, mr, mr);
++	DEBUG("port %u MR CREATED (%p) for external memory %p:\n"
++	      "  [0x%" PRIxPTR ", 0x%" PRIxPTR "),"
++	      " lkey=0x%x base_idx=%u ms_n=%u, ms_bmp_n=%u",
++	      dev->data->port_id, (void *)mr, (void *)addr,
++	      addr, addr + len, rte_cpu_to_be_32(mr->ibv_mr->lkey),
++	      mr->ms_base_idx, mr->ms_n, mr->ms_bmp_n);
++	/* Insert to the global cache table. */
++	mr_insert_dev_cache(dev, mr);
++	rte_rwlock_write_unlock(&priv->mr.rwlock);
++	/* Insert to the local cache table */
++	mlx4_mr_addr2mr_bh(dev, mr_ctrl, addr);
++}
++
++/**
++ * Register MR for entire memory chunks in a Mempool having externally allocated
++ * memory and fill in local cache.
++ *
++ * @param dev
++ *   Pointer to Ethernet device.
++ * @param mr_ctrl
++ *   Pointer to per-queue MR control structure.
++ * @param mp
++ *   Pointer to registering Mempool.
++ *
++ * @return
++ *   0 on success, -1 on failure.
++ */
++static uint32_t
++mlx4_mr_update_ext_mp(struct rte_eth_dev *dev, struct mlx4_mr_ctrl *mr_ctrl,
++		      struct rte_mempool *mp)
++{
++	struct mr_update_mp_data data = {
++		.dev = dev,
++		.mr_ctrl = mr_ctrl,
++		.ret = 0,
++	};
++
++	rte_mempool_mem_iter(mp, mlx4_mr_update_ext_mp_cb, &data);
++	return data.ret;
++}
++
++/**
++ * Register MR entire memory chunks in a Mempool having externally allocated
++ * memory and search LKey of the address to return.
++ *
++ * @param dev
++ *   Pointer to Ethernet device.
++ * @param addr
++ *   Search key.
++ * @param mp
++ *   Pointer to registering Mempool where addr belongs.
++ *
++ * @return
++ *   LKey for address on success, UINT32_MAX on failure.
++ */
++uint32_t
++mlx4_tx_update_ext_mp(struct txq *txq, uintptr_t addr,
++		      struct rte_mempool *mp)
++{
++	struct mlx4_mr_ctrl *mr_ctrl = &txq->mr_ctrl;
++	struct priv *priv = txq->priv;
++
++	mlx4_mr_update_ext_mp(priv->dev, mr_ctrl, mp);
++	return mlx4_tx_addr2mr_bh(txq, addr);
++}
++
+ /* Called during rte_mempool_mem_iter() by mlx4_mr_update_mp(). */
+ static void
+ mlx4_mr_update_mp_cb(struct rte_mempool *mp __rte_unused, void *opaque,
+@@ -1098,6 +1244,10 @@ mlx4_mr_update_mp(struct rte_eth_dev *dev, struct mlx4_mr_ctrl *mr_ctrl,
+ 	};
+ 
+ 	rte_mempool_mem_iter(mp, mlx4_mr_update_mp_cb, &data);
++	if (data.ret < 0 && rte_errno == ENXIO) {
++		/* Mempool may have externally allocated memory. */
++		return mlx4_mr_update_ext_mp(dev, mr_ctrl, mp);
++	}
+ 	return data.ret;
+ }
+ 
+diff --git a/drivers/net/mlx4/mlx4_rxtx.h b/drivers/net/mlx4/mlx4_rxtx.h
+index ffa8abfca..1be060cda 100644
+--- a/drivers/net/mlx4/mlx4_rxtx.h
++++ b/drivers/net/mlx4/mlx4_rxtx.h
+@@ -163,6 +163,26 @@ void mlx4_tx_queue_release(void *dpdk_txq);
+ void mlx4_mr_flush_local_cache(struct mlx4_mr_ctrl *mr_ctrl);
+ uint32_t mlx4_rx_addr2mr_bh(struct rxq *rxq, uintptr_t addr);
+ uint32_t mlx4_tx_addr2mr_bh(struct txq *txq, uintptr_t addr);
++uint32_t mlx4_tx_update_ext_mp(struct txq *txq, uintptr_t addr,
++			       struct rte_mempool *mp);
++
++/**
++ * Get Memory Pool (MP) from mbuf. If mbuf is indirect, the pool from which the
++ * cloned mbuf is allocated is returned instead.
++ *
++ * @param buf
++ *   Pointer to mbuf.
++ *
++ * @return
++ *   Memory pool where data is located for given mbuf.
++ */
++static struct rte_mempool *
++mlx4_mb2mp(struct rte_mbuf *buf)
++{
++	if (unlikely(RTE_MBUF_INDIRECT(buf)))
++		return rte_mbuf_from_indirect(buf)->pool;
++	return buf->pool;
++}
+ 
+ /**
+  * Query LKey from a packet buffer for Rx. No need to flush local caches for Rx
+@@ -222,6 +242,19 @@ mlx4_tx_addr2mr(struct txq *txq, uintptr_t addr)
+ 	return mlx4_tx_addr2mr_bh(txq, addr);
+ }
+ 
+-#define mlx4_tx_mb2mr(rxq, mb) mlx4_tx_addr2mr(rxq, (uintptr_t)((mb)->buf_addr))
++static __rte_always_inline uint32_t
++mlx4_tx_mb2mr(struct txq *txq, struct rte_mbuf *mb)
++{
++	uintptr_t addr = (uintptr_t)mb->buf_addr;
++	uint32_t lkey = mlx4_tx_addr2mr(txq, addr);
++
++	if (likely(lkey != UINT32_MAX))
++		return lkey;
++	if (rte_errno == ENXIO) {
++		/* Mempool may have externally allocated memory. */
++		lkey = mlx4_tx_update_ext_mp(txq, addr, mlx4_mb2mp(mb));
++	}
++	return lkey;
++}
+ 
+ #endif /* MLX4_RXTX_H_ */
+-- 
+2.15.2 (Apple Git-101.1)
+
diff --git a/build/external/dpdk-18.08_patches/0003-ixgbe-wait-longer-for-link-after-fiber-MAC-setup.patch b/build/external/dpdk-18.08_patches/0003-ixgbe-wait-longer-for-link-after-fiber-MAC-setup.patch
new file mode 100644
index 0000000..93d1601
--- /dev/null
+++ b/build/external/dpdk-18.08_patches/0003-ixgbe-wait-longer-for-link-after-fiber-MAC-setup.patch
@@ -0,0 +1,39 @@
+From ba9b381c532fe57c726752b7db0ab45ab7726c90 Mon Sep 17 00:00:00 2001
+From: Matthew Smith <mgsmith@netgate.com>
+Date: Fri, 13 Jul 2018 16:35:57 -0500
+Subject: [PATCH] ixgbe: wait longer for link after fiber MAC setup
+
+After setting up the link on a fiber port, the maximum wait time for
+the link to come up is 500 ms in ixgbe_setup_mac_link_multispeed_fiber().
+On an x550 SFP+ port, this is often not sufficiently long for the link
+to come up. This can result in never being able to retrieve accurate
+link status for the port using rte_eth_link_get_nowait().
+
+Increase the maximum wait time in ixgbe_setup_mac_link_multispeed_fiber()
+to 1 s.
+
+Bugzilla ID: 69
+Fixes: f3430431abaf ("ixgbe/base: add SFP+ dual-speed support")
+Cc: stable@dpdk.org
+
+Signed-off-by: Matthew Smith <mgsmith@netgate.com>
+---
+ drivers/net/ixgbe/base/ixgbe_common.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ixgbe/base/ixgbe_common.c b/drivers/net/ixgbe/base/ixgbe_common.c
+index e7e9256e5..2fb0a072c 100644
+--- a/drivers/net/ixgbe/base/ixgbe_common.c
++++ b/drivers/net/ixgbe/base/ixgbe_common.c
+@@ -5296,7 +5296,7 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
+ 		 * Section 73.10.2, we may have to wait up to 500ms if KR is
+ 		 * attempted.  82599 uses the same timing for 10g SFI.
+ 		 */
+-		for (i = 0; i < 5; i++) {
++		for (i = 0; i < 10; i++) {
+ 			/* Wait for the link partner to also set speed */
+ 			msec_delay(100);
+ 
+-- 
+2.15.2 (Apple Git-101.1)
+
diff --git a/build/external/rpm/vpp-ext-deps.spec b/build/external/rpm/vpp-ext-deps.spec
new file mode 100644
index 0000000..c79ae12
--- /dev/null
+++ b/build/external/rpm/vpp-ext-deps.spec
@@ -0,0 +1,18 @@
+%define _install_dir	/opt/vpp/external/%(uname -m)
+%define _make_args	-C ../.. DPDK_BUILD_DIR=%{_topdir}/tmp DPDK_INSTALL_DIR=%{buildroot}/%{_install_dir}
+
+Name:		vpp-ext-deps
+Version:	%{_version}
+Release:	%{_release}
+Summary:	VPP development package with external dependencies
+License:	BSD
+
+%description
+VPP development package with external dependencies
+
+%install
+make %{_make_args} config
+make %{_make_args} install
+
+%files
+%{_install_dir}