diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..466dafb9a --- /dev/null +++ b/.clang-format @@ -0,0 +1,12 @@ +BasedOnStyle: LLVM +AllowShortIfStatementsOnASingleLine: false +AlignConsecutiveAssignments: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: TopLevelDefinitions +BreakBeforeBraces: Linux +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 4 +IndentCaseLabels: false +IndentWidth: 8 +SortIncludes: false +UseTab: ForIndentation diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..0e73629b8 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,101 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '15 11 * * 0' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: c-cpp + build-mode: autobuild + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Add any setup steps before running the `github/codeql-action/init` action. + # This includes steps like installing compilers or runtimes (`actions/setup-node` + # or others). This is typically only required for manual builds. + # - name: Setup runtime (example) + # uses: actions/setup-example@v1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - name: Run manual build steps + if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" diff --git a/.gitignore b/.gitignore index 6bfcbdefd..d2906fd00 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,9 @@ *.mod.c *.orig .*.swp +config tags +cscope.* modules.order Module.symvers .tmp_versions @@ -19,6 +21,7 @@ r8169.c get-drivers igb*/ ixgbe*/ +vmxnet*/ log/ scripts/conf virtio_net.c @@ -49,6 +52,12 @@ utils/testmmap utils/vale-ctl utils/test_nm utils/cygwin1.dll +utils/ctrl-api-test +utils/functional +utils/fd_server +utils/get_tx_rings_avail_sends +utils/get_tx_rings_max_sends +utils/extmem-example examples/pkt-gen.exe examples/pkt-gen.exe.stackdump examples/pkt-gen-b.exe @@ -70,6 +79,10 @@ read-vars.mak !LINUX/default-config.mak !LINUX/drv-subdir.mak !LINUX/read-vars.mak +!LINUX/final-patches/* LINUX/scripts/conf config.mak *.rej +utils/producer +.cache.mk +utils/sync_kloop_test diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..cc5e15c28 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,50 @@ +dist: xenial +sudo: required +language: c +env: + - KERNEL_VERSION=local ARCH=x86_64 + - KERNEL_VERSION=3.0 ARCH=x86_64 + - KERNEL_VERSION=3.1 ARCH=x86_64 + - KERNEL_VERSION=3.2 ARCH=x86_64 + - KERNEL_VERSION=3.3 ARCH=x86_64 + - KERNEL_VERSION=3.4 ARCH=x86_64 + - KERNEL_VERSION=3.5 ARCH=x86_64 + - KERNEL_VERSION=3.6 ARCH=x86_64 + - KERNEL_VERSION=3.7 ARCH=x86_64 + - KERNEL_VERSION=3.8 ARCH=x86_64 + - KERNEL_VERSION=3.9 ARCH=x86_64 + - KERNEL_VERSION=3.10.108 ARCH=x86_64 + - KERNEL_VERSION=3.11 ARCH=x86_64 + - KERNEL_VERSION=3.12 ARCH=x86_64 + - KERNEL_VERSION=3.13 ARCH=x86_64 + - KERNEL_VERSION=3.14 ARCH=x86_64 + - KERNEL_VERSION=3.15 ARCH=x86_64 + - KERNEL_VERSION=3.16 ARCH=x86_64 + - KERNEL_VERSION=3.17 ARCH=x86_64 + - KERNEL_VERSION=3.18 ARCH=x86_64 + - KERNEL_VERSION=3.19 ARCH=x86_64 + - KERNEL_VERSION=4.0 ARCH=x86_64 + - KERNEL_VERSION=4.1 ARCH=x86_64 + - KERNEL_VERSION=4.2 ARCH=x86_64 + - KERNEL_VERSION=4.3 ARCH=x86_64 + - KERNEL_VERSION=4.4 ARCH=x86_64 + - KERNEL_VERSION=4.5 ARCH=x86_64 + - KERNEL_VERSION=4.6 ARCH=x86_64 + - KERNEL_VERSION=4.7 ARCH=x86_64 + - KERNEL_VERSION=4.8 ARCH=x86_64 + - KERNEL_VERSION=4.9 ARCH=x86_64 + - KERNEL_VERSION=4.10 ARCH=x86_64 + - KERNEL_VERSION=4.11 ARCH=x86_64 + - KERNEL_VERSION=4.12 ARCH=x86_64 + - KERNEL_VERSION=4.13 ARCH=x86_64 + - KERNEL_VERSION=4.14 ARCH=x86_64 + - KERNEL_VERSION=4.15 ARCH=x86_64 + - KERNEL_VERSION=4.16 ARCH=x86_64 + - KERNEL_VERSION=4.17 ARCH=x86_64 + - KERNEL_VERSION=4.18 ARCH=x86_64 + - KERNEL_VERSION=4.19 ARCH=x86_64 + - KERNEL_VERSION=4.20 ARCH=x86_64 + - KERNEL_VERSION=3.16 ARCH=i386 +script: + - "./ci/build-linux $KERNEL_VERSION $ARCH" + - "[ ${KERNEL_VERSION} != local ] || ./ci/run-integration-tests" diff --git a/GNUmakefile b/GNUmakefile index 5b71d26b0..6427df74b 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,4 +1,4 @@ -all: netmap.mak +all: -include netmap.mak @@ -6,9 +6,14 @@ COMPAT_ARGS=$(if $(KSRC),--kernel-dir=$(KSRC),)\ $(if $(SRC),--kernel-sources=$(SRC),)\ $(if $(NODRIVERS),--no-drivers) + netmap.mak: @echo 'The new way to build netmap is to run the provided configure script first,' @echo 'followed by make.' +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(MAKECMDGOALS),distclean) @echo 'We run configure for you now, with compatible arguments, and restart make.' @echo 'Please run configure again if this is not what you want.' ./configure $(COMPAT_ARGS) +endif +endif diff --git a/LINUX/Kbuild.in b/LINUX/Kbuild.in index f4a861cc6..4d0bf7802 100644 --- a/LINUX/Kbuild.in +++ b/LINUX/Kbuild.in @@ -8,15 +8,13 @@ SRCDIR:=@SRCDIR@ # the source is not here so we need to specify a dependency $(foreach s,$(SUBSYS),$(eval CONFIG_NETMAP_$(shell echo $s|tr a-z- A-Z_)=y)) -remoteobjs-y := netmap_mem2.o netmap_mbq.o +remoteobjs-y := netmap_mem2.o netmap_mbq.o netmap_legacy.o netmap_bdg.o netmap_kloop.o remoteobjs-$(CONFIG_NETMAP_VALE) += netmap_vale.o netmap_offloadings.o remoteobjs-$(CONFIG_NETMAP_PIPE) += netmap_pipe.o remoteobjs-$(CONFIG_NETMAP_MONITOR) += netmap_monitor.o remoteobjs-$(CONFIG_NETMAP_GENERIC) += netmap_generic.o -remoteobjs-ptnetmap-$(CONFIG_NETMAP_PTNETMAP_GUEST) = netmap_pt.o -remoteobjs-ptnetmap-$(CONFIG_NETMAP_PTNETMAP_HOST) = netmap_pt.o -remoteobjs-y += $(remoteobjs-ptnetmap-y) +remoteobjs-$(CONFIG_NETMAP_NULL) += netmap_null.o define remote_template $$(obj)/$(1): %.o: $$(SRCDIR)/../sys/dev/netmap/$(2) FORCE @@ -33,7 +31,7 @@ $(obj)/netmap_linux.o: %.o: $(SRCDIR)/netmap_linux.c FORCE # all objects $(MODNAME)-objs := $(remoteobjs-y) netmap_common.o netmap_linux.o -ifdef CONFIG_NETMAP_PTNETMAP_GUEST +ifdef CONFIG_NETMAP_PTNETMAP $(obj)/netmap_ptnet.o: %.o: $(SRCDIR)/netmap_ptnet.c FORCE $(call if_changed_rule,cc_o_c) diff --git a/LINUX/README b/LINUX/README deleted file mode 100644 index 492ecd122..000000000 --- a/LINUX/README +++ /dev/null @@ -1,301 +0,0 @@ -# $Id: README 10863 2012-04-11 17:10:39Z luigi $ - -NETMAP FOR LINUX ----------------- - -This directory contains a version of the "netmap" and "VALE" code for Linux. - -Netmap is a BSD-licensed framework that supports line-rate direct packet -I/O even on 10GBit/s interfaces (14.88Mpps) with limited system load, -and includes a libpcap emulation library to port applications. - -See - - http://info.iet.unipi.it/~luigi/netmap/ - -for more details. There you can also find the latest versions -of the code and documentation as well as pre-built TinyCore -images based on linux 3.0.3 and containing the netmap modules -and some test applications. - -This version supports r8169, ixgbe, igb, i40e, e1000, e1000e and -forcedeth. - -Netmap relies on a kernel module (netmap.ko) and slightly modified -device drivers. Userspace programs can use the native API (documented -in netmap.4) or a libpcap emulation library. - -The FreeBSD and Linux versions share the same codebase, which -is located in ../sys . For Linux we use some additional glue code, -(bsd_glue.h). - -Device drivers are taken directly from either the Linux distributions, -or the Intel out-of-tree drivers, and patched using the files in -the patches/ directory. Common driver modifications are in the .h -files in this directory. Note that the patches for e1000, forcedeth, -virtio-net have been prepared on the vanilla kernel: this is usually -sufficient for Debian/Ubuntu, but it often fails on Red Hat/CentOS. -The patches for igb, e1000e, ixgbe and i40e, instead, are against -a specific version of the out-of-tree Intel drivers, and should compile -without any problem on the same systems where the original drivers do. - -Linux distributions used and tested ------------------------------------ - -Development: - Ubuntu 16.04.1 LTS (tested on kernel 4.4.0-53-generic) - Archlinux (tested on the 4.8.x vanilla kernel provided by Arch) - -Supported for general compatibility: - Ubuntu 16.10 (tested on kernel 4.8.0-30-generic) - CentOS 7 (tested on kernel 3.10.0-514.2.2.el7.x86_64) - -On Archlinux systems the netmap software is provided by the 'netmap' -AUR package (https://aur.archlinux.org/packages/netmap/). In general, -you can build and install netmap following the instruction in 'HOW TO -BUILD THE CODE'. - -HOW TO BUILD THE CODE ---------------------- - -The netmap port for linux is built and installed using the standard -./configure; make; sudo make install workflow. - -The main purpose of the configure script is to determine the features of -your kernel using simple compile tests, since just trusting the kernel -version number is unreliable. The outcomes of the tests are stored in -a set of macros in the generated netmap_linux_config.h file. - -The configure script also controls the compilation of optional netmap -features, namely: - - netmap subsystems - ----------------- - - These are optional parts of netmap that can be compiled in with - --enable-SUBSYSTEM and commented out with --disable-SUBSYSTEM. - The available subsystems the following (the starred ones are enabled - by default): - - vale (*): the VALE switch (a fast switch that uses - the netmap API). - - pipe (*): netmap pipes (pairs of netmap ports connected - back to back). - - monitor (*): netmap monitors (can monitor other netmap ports - in copy and zero-copy modes, without stopping - traffic). - - generic (*): the generic driver that is used to access - NICs without native netmap support (at reduced - performance). - - ptnetmap-guest: netmap passthrough support for guests - (including the ptnet driver). - - ptnetmap-host: netmap passthrough support for the host - - ptnetmap: shortcut to include both ptnetmap-guest and - ptnetmap-host. - - sink: a dummy drop-everything device with native netmap - support. It can emulate a link with configurable - packet rate. - - NIC drivers - -------------- - - The generic driver can be used to open in netmap mode any NIC for which - the host OS already supplies a driver. The optimal performance, however, - is only obtained with netmap-enabled NIC drivers. The configure script - implements two methods to obtain the netmap-enabled drivers: - - 1. patching the native drivers that come with your kernel; - 2. patching NIC-vendors out-of-tree drivers selected by us. - - Both methods have advantages and drawbacks and none is perfect. - In method 1 the patches we supply may fail to apply (especially on - Red Hat based distributions), but compilation of successfully patched - drivers usually works. In method 2 the patches will be guaranteed to - apply, but compilation may fail since the out-of-tree drivers may - not support your kernel. - - By default e1000e, i40e, ixgbe, ixgbevf and igb use method 2, while - e1000, r8169.c, forcedeth.c, veth.c and virtio_net.c drivers use method - 1. The list of supported drivers can be obtained by running configure - with the --show-drivers option, while --show-ext-drivers lists the - drivers that use method 2 by default. For the latter drivers you may - also choose to use method 1 using the --no-ext-drivers (use method - 1 for everything) or --no-ext-drivers= followed by a comma separated - list of drivers. - - For method 1 you need the driver sources for your kernel. - - - If you have built your own kernel, you need to tell configure - where the kernel build directory is using the --kernel-dir= - option. The build directory must have been prepared for external - modules compilation. - - - If you are using the kernel provided by your Linux distribution - you need to install the full kernel-sources package (how to do - so depends on the distribution). Note that, even when you have - installed the sources, configure will automatically find them - only if they are pointed to by /lib/modules/$(uname -r)/build - or /lib/modules/$(uname -r)/build/sources. If the sources are - anywhere else, you need to tell configure where to find them - using the --kernel-sources= options. The --kernel-dir= option - must still point to a directory where all the information for - external module compilation is available and there is typically - no need to supply it, since configure is already able to find - it in the standard place. - - The configure script selects the patch to apply based only on the - kernel version. Moreover, we only supply patches for the vanilla kernel - from the Torvalds repository (not even the stable kernels). If the - patch selected by configure for a driver fails to apply, the driver - is disabled and will not be built my make. - - For method 2 you need an Internet connection, since the external drivers - are downloaded by configure from the vendor repository. Otherwise, - follow the instructions printed by the script. The configure script - will try to build the original external driver before applying the - netmap patches: if the clean build fails then the vendor driver does - not support your kernel (yet?) and the driver is disabled. - - If you want support for additional drivers please have a look at - ixgbe_netmap_linux.h and the patches in patches/ The patch file are - named as vanilla--DRIVER--LOW--HIGH where DRIVER is the driver name - to patch, LOW and HIGH are the versions to which the patch applies - (LOW included, HIGH excluded, so vanilla--r8169.c--20638--30300 applies - from 2.6.38 to 3.3.0 (excluded). - -HOW TO USE THE CODE -------------------- - - REMEMBER - THIS IS EXPERIMENTAL CODE WHICH MAY CRASH YOUR SYSTEM. - USE IT AT YOUR OWN RISk. - -Whether you built your own modules, or are using the prebuilt -TinyCore image, the following steps can be used for initial testing: - -1. unload any modules for the network cards you want to use, e.g. - sudo rmmod ixgbe - sudo rmmod e1000 - ... - -2. load netmap and device driver module - sudo insmod ./netmap.ko - sudo insmod ./ixgbe/ixgbe.ko - sudo insmod ./e1000/e1000.ko - ... - -3. turn the interface(s) up - - sudo ifconfig eth0 up # and same for others - -4. Run test applications -- as an example, pkt-gen is a raw packet - sender/receiver which can do line rate on a 10G interface - - # send about 500 million packets of 60 bytes each. - # wait 5s before starting, so the link can go up - sudo pkt-gen -i eth0 -f tx -n 500111222 -l 60 -w 5 - # you should see about 14.88 Mpps - - sudo pkt-gen -i eth0 -f rx # act as a receiver - - -COMMON PROBLEMS ----------------- - -* switching in/out of netmap mode causes the link to go down and up. - If your card is connected to a switch with spanning tree enabled, - the switch will likely MUTE THE LINK FOR 10 SECONDS while it is - detecting the new topology. Either disable the spanning tree on - the switch or use long pauses before sending data; - -* Not all cards can do line rate no matter how fast is your software or - CPU. Several have hardware limitations that prevent reaching the peak - speed, especially for small packet sizes. Examples: - - - ixgbe cannot receive at line rate with packet sizes that are - not multiple of 64 (after CRC stripping). - This is especially evident with minimum-sized frames (-l 60 ) - - - some of the low-end 'e1000' cards can send 1.2 - 1.3Mpps instead - of the theoretical maximum (1.488Mpps) - - - the 'realtek' cards seem unable to send more than 450-500Kpps - even though they can receive at least 1.1Mpps - -* if the link is not up when the packet generator starts, you will - see frequent messages about a link reset. While we work on a fix, - use the '-w' argument on the generator to specify a longer timeout - -* the ixgbe driver (and perhaps others) is severely slowed down if the - remote party is sending flow control frames to slow down traffic. - If that happens try to use the ethtool command to disable flow control. - -* netmap does not program the NICs to perform offloadings such as TSO, - UFO, RX/TX checksum offloadings, etc. As a result, in order to let - netmap applications correctly interact with the host rings, you need - to disable these offloadings - - # ethtool -K eth0 tx off rx off gso off tso off gro off lro off - - If offloadings are not disabled, the network stack may try to send - GSO packets (up to 64KB) that are dropped by netmap (as they are - too big for the netmap buffers); or the network stack could send - unchecksummed packets that end up in the host RX ring, and if - transmitted by netmap on a NIC TX ring they will be dropped by the - destination as the checksum is wrong. - -* if you are using netmap to implement an L2 switch (e.g. using the - bridge application), you must put the NIC in promiscuous mode, - otherwise the NIC (usually) drops all the frames whose destination - MAC is different from the MAC of the NIC. - - # ip link set eth0 promisc on - -* if you are receiving VLAN-tagged packets, netmap applications (with - patched drivers) may not see the VLAN tag because receive VLAN offloading - is enabled (and so VLAN tags are stripped by the NIC). To disable it use - - # ethtool -K eth0 rxvlan off - - In emulated netmap mode (i.e. with unpatched drivers) VLAN tags are never - visible by the netmap application. - - -REVISION HISTORY ------------------ - -20120813 - updated distribution using common code for FreeBSD and Linux, - and inclusion of drivers from the linux source tree - -20120322 - fixed the 'igb' driver, now it can send and receive correctly - (the problem was in netmap_rx_irq() so it might have affected - other multiqueue cards). - Also tested the 'r8169' in transmit mode. - Added comments on switches and spanning tree. - -20120217 - initial version. Only ixgbe, e1000 and e1000e are working. - Other drivers (igb, r8169, forcedeth) are supplied only as a - proof of concept. - -DETAILS --------- -+ igb: on linux 3.2 and above the igb driver moved to split buffers, - and netmap was not updated until end of june 2013. - Symptoms were inability to receive short packets. - -+ there are reports of ixgbe and igb unable to read packets. - We are unable to reproduce the problem. - - Ubuntu 12.04 LTS 3.5.0-25-generic. igb read problems ? - - 3.2.0-32-generic with 82598 not working - -+ if_e1000_e uses regular descriptor up 3.1 at least - 3.2.32 is reported to use extended descriptors - (in my repo updated at -r 11975) - diff --git a/LINUX/README.md b/LINUX/README.md new file mode 100644 index 000000000..6c47b7577 --- /dev/null +++ b/LINUX/README.md @@ -0,0 +1,284 @@ +# Netmap for Linux + +This file contains instructions on how to build, install and use Netmap +on Linux. +This directory contains Linux-specific code to let netmap work on +Linux. +Native support is available for r8169, ixgbe, igb, i40e, e1000, e1000e, +virtio-net, and forcedeth Linux drivers. + +Netmap relies on a kernel module (`netmap.ko`) and modified +device drivers. Userspace programs can use the native API (documented +in `netmap.4`) or a libpcap emulation library. + +Most of the codebase is shared between FreeBSD and Linux, and it +is located in `sys/` in the root directory of this repository. +For Linux we use some additional glue code, (`bsd_glue.h`, in this +directory). + +Device drivers are taken directly from either the Linux distributions +or vendor-provided out-of-tree drivers, and patched using the files in +the patches/ directory. Common driver modifications are in the .h +files in this directory. Note that the patches for e1000, forcedeth, +virtio-net have been prepared on the vanilla kernel: this is usually +sufficient for Debian/Ubuntu, but it often fails on Red Hat/CentOS. +The patches for igb, e1000e, ixgbe and i40e, instead, are against +a specific version of the out-of-tree Intel drivers, and should compile +without any problem on the same systems where the original drivers do. + +## Linux distributions used and tested + +Development: + +* Ubuntu 16.04.1 LTS (tested on kernel `4.4.0-53-generic`) +* Archlinux (tested on the `4.8.x` vanilla kernel provided by Arch) + +Supported for general compatibility: + +* Ubuntu 16.10 (tested on kernel `4.8.0-30-generic`) +* CentOS 7 (tested on kernel `3.10.0-514.2.2.el7.x86_64`) + +On Archlinux systems the netmap software is provided by the 'netmap' +AUR package (https://aur.archlinux.org/packages/netmap/). In general, +you can build and install netmap on any distribution, by following the +instruction in reported in the following [section](#how-to-build-the-code). + +## How to build the code + +The netmap port for linux is built and installed using the standard +`./configure && make && sudo make install` workflow. + +The main purpose of the configure script is to determine the features of +your kernel using simple compile tests, since just trusting the kernel +version number is unreliable. The outcomes of the tests are stored in +a set of macros in the generated `netmap_linux_config.h` file. + +The configure script also controls the compilation of optional netmap +features, namely [netmap subsystems](#netmap-subsystems). + +### Netmap subsystems + +These are optional parts of netmap that can be compiled in with +`--enable-SUBSYSTEM` and ruled out with `--disable-SUBSYSTEM`. +The available subsystems the following (the starred ones are enabled +by default): + +* **vale** (\*): the VALE L2 switch (a fast switch that uses the netmap API). +* **pipe** (\*): netmap pipes (pairs of netmap virtual ports connected back to back). +* **monitor** (\*): netmap monitors (can monitor other netmap ports in copy +and zero-copy modes, without stopping traffic). +* **generic** (\*): the generic (a.k.a. emulated) netmap adapter that is +used to access NICs without native netmap support (at reduced performance). +* **ptnetmap** (\*): netmap passthrough support for guests (including the ptnet +driver). +* **sink**: a dummy drop-everything device with native netmap support. +It can emulate a link with configurable packet rate. + +### NIC drivers + +The emulated (generic) adapter can be used to open in netmap mode any NIC +for which the host OS already supplies a driver. The optimal performance, +however, is only obtained with netmap-enabled NIC drivers. The configure +script implements two methods to obtain the netmap-enabled drivers: + +1. patching the native drivers that come with your kernel; +2. patching NIC-vendors out-of-tree drivers selected by us. + +Both methods have advantages and drawbacks and none is perfect. +In method 1 the patches we supply may fail to apply (especially on +Red Hat based distributions), but compilation of successfully patched +drivers usually works. In method 2 the patches will be guaranteed to +apply, but compilation may fail since the out-of-tree drivers may +not support your kernel. + +By default `e1000e`, `i40e`, `ixgbe`, `ixgbevf` and igb use method 2, +while `e1000`, `r8169.c`, `forcedeth.c`, `veth.c` and `virtio_net.c` +drivers use method 1\. +The list of supported drivers can be obtained by running configure +with the `--show-drivers` option, while `--show-ext-drivers` lists the +drivers that use method 2 by default. For the latter drivers you may +also choose to use method 1 using the `--no-ext-drivers` (use method +1 for everything) or `--no-ext-drivers=` followed by a comma separated +list of drivers. + +For method 1 you need the driver sources for your kernel. + +* If you have built your own kernel, you need to tell configure +where the kernel build directory is using the `--kernel-dir=` +option. The build directory must have been prepared for external +modules compilation. + +* If you are using the kernel provided by your Linux distribution +you need to install the full kernel-sources package (how to do +so depends on the distribution). Note that, even when you have +installed the sources, configure will automatically find them +only if they are pointed to by /lib/modules/$(uname -r)/build +or /lib/modules/$(uname -r)/build/sources. If the sources are +anywhere else, you need to tell configure where to find them +using the `--kernel-sources=` options. The `--kernel-dir=` option +must still point to a directory where all the information for +external module compilation is available and there is typically +no need to supply it, since configure is already able to find +it in the standard place. + +The configure script selects the patch to apply based only on the +kernel version. Moreover, we only supply patches for the vanilla kernel +from the Torvalds repository (not even the stable kernels). If the +patch selected by configure for a driver fails to apply, the driver +is disabled and will not be built my make. + +For method 2 you need an Internet connection, since the external drivers +are downloaded by configure from the vendor repository. Otherwise, +follow the instructions printed by the script. The configure script +will try to build the original external driver before applying the +netmap patches: if the clean build fails then the vendor driver does +not support your kernel (yet?) and the driver is disabled. + +If you want support for additional drivers please have a look at +`ixgbe_netmap_linux.h` and the patches in patches/ The patch file are +named as `vanilla--DRIVER--LOW--HIGH` where DRIVER is the driver name +to patch, LOW and HIGH are the versions to which the patch applies +(LOW included, HIGH excluded, so `vanilla--r8169.c--20638--30300` applies +from 2.6.38 to 3.3.0 (excluded). + +The patches for the external drivers are named VENDOR--DRIVER--VERSION, +where VENDOR is just intel as of now, and VERSION is the upstream driver +version number (assigned by the VENDOR). If you want to use a different +VERSION than the default, and the patches directory contains a patch +for the version you are interest in, you can use the `--select-version` +option of configure. E.g., to select the 5.2.4 version of the ixgbe +external driver, pass `--select-version=ixgbe:5.2.4` to configure. + +## How to load netmap in your system + +Unload any modules for the network cards you want to use, e.g. + + sudo rmmod ixgbe + sudo rmmod e1000 + ... + +Load netmap and device driver module + + sudo insmod ./netmap.ko + sudo insmod ./ixgbe/ixgbe.ko + sudo insmod ./e1000/e1000.ko + ... + +Turn the interface(s) up + + sudo ifconfig eth0 up # and same for others + +Run test applications -- as an example, pkt-gen is a raw packet +sender/receiver which can do line rate on a 10G interface. + +Send about 500 million packets of 60 bytes each. +wait 5s before starting, so the link can go up + + sudo pkt-gen -i eth0 -f tx -n 500111222 -l 60 -w 5 + +On the receiver, you should see about 14.88 Mpps + + sudo pkt-gen -i eth0 -f rx # act as a receiver + + +## Common problems + +* switching in/out of netmap mode causes the link to go down and up. + If your card is connected to a switch with spanning tree enabled, + the switch will likely MUTE THE LINK FOR 10 SECONDS while it is + detecting the new topology. Either disable the spanning tree on + the switch or use long pauses before sending data; + +* Not all cards can do line rate no matter how fast is your software or + CPU. Several have hardware limitations that prevent reaching the peak + speed, especially for small packet sizes. Examples: + + - ixgbe cannot receive at line rate with packet sizes that are + not multiple of 64 (after CRC stripping). + This is especially evident with minimum-sized frames (-l 60 ) + + - some of the low-end 'e1000' cards can send 1.2 - 1.3Mpps instead + of the theoretical maximum (1.488Mpps) + + - the 'realtek' cards seem unable to send more than 450-500Kpps + even though they can receive at least 1.1Mpps + +* if the link is not up when the packet generator starts, you will + see frequent messages about a link reset. While we work on a fix, + use the '-w' argument on the generator to specify a longer timeout + +* the ixgbe driver (and perhaps others) is severely slowed down if the + remote party is sending flow control frames to slow down traffic. + If that happens try to use the ethtool command to disable flow control. + +* netmap does not program the NICs to perform offloadings such as TSO, + UFO, RX/TX checksum offloadings, etc. As a result, in order to let + netmap applications correctly interact with the host rings, you need + to disable these offloadings + + # ethtool -K eth0 tx off rx off gso off tso off gro off lro off + + If offloadings are not disabled, the network stack may try to send + GSO packets (up to 64KB) that are dropped by netmap (as they are + too big for the netmap buffers); or the network stack could send + unchecksummed packets that end up in the host RX ring, and if + transmitted by netmap on a NIC TX ring they will be dropped by the + destination as the checksum is wrong. + +* if you are using netmap to implement an L2 switch (e.g. using the + bridge application), you must put the NIC in promiscuous mode, + otherwise the NIC (usually) drops all the frames whose destination + MAC is different from the MAC of the NIC. + + # ip link set eth0 promisc on + + Some drivers (e.g. the netmap-patched i40e) may disable promiscuous + mode during the down/up cycle that happens when putting the NIC + in netmap mode. This means that it may be necessary to enable + promiscuous mode again after starting the netmap application. + If the promiscuous mode was already enabled, you may need to + disable it before enabling it again. For these drivers, start the + application first, and then execute these commands: + + # ip link set eth0 promisc off + # ip link set eth0 promisc on + + or incorporate equivalent operations in your application. + +* if you are receiving VLAN-tagged packets, netmap applications (with + patched drivers) may not see the VLAN tag because receive VLAN offloading + is enabled (and so VLAN tags are stripped by the NIC). To disable it use + + # ethtool -K eth0 rxvlan off + + In emulated netmap mode (i.e. with unpatched drivers) VLAN tags are never + visible by the netmap application. + +* When opening a veth interface in native netmap mode, the peer veth interface + must also be opened in native netmap mode, otherwise the traffic won't flow. + In other words, one cannot use native netmap mode on a veth endpoint and + use the kernel network stack on the other endpoint. This is not a missing + feature, as the native veth datapath is implemented using netmap pipes, and + it does not make sense (in terms of performance) for pipes to support + conversion between netmap buffers and skbuffs. + +* pkt-gen traffic does not flow across a Linux bridge. + Check that source MAC is not 00:00:00:00:00:00 (pkt-gen default), nor + ff:ff:ff:ff:ff:ff. See: + https://elixir.bootlin.com/linux/latest/source/net/bridge/br_input.c#L281 + + +## Additional information + +* igb: on linux 3.2 and above the igb driver moved to split buffers, + and netmap was not updated until end of june 2013. + Symptoms were inability to receive short packets. + +* there are reports of ixgbe and igb unable to read packets. + We are unable to reproduce the problem. + - Ubuntu 12.04 LTS 3.5.0-25-generic. igb read problems ? + - 3.2.0-32-generic with 82598 not working + +* e1000e uses regular descriptor up 3.1 at least + 3.2.32 is reported to use extended descriptors + (in my repo updated at -r 11975) diff --git a/LINUX/archlinux/PKGBUILD b/LINUX/archlinux/PKGBUILD index 7694796df..f3f1d1d7c 100644 --- a/LINUX/archlinux/PKGBUILD +++ b/LINUX/archlinux/PKGBUILD @@ -3,15 +3,15 @@ # Maintainer: Vincenzo Maffione pkgname=netmap -pkgver=r1324.519c07f +pkgver=r2392.86312f06 pkgrel=1 -pkgdesc="Netmap is a framework for high speed network packet I/O." +pkgdesc="A framework for high speed network packet I/O, using kernel bypass" arch=('any') url="http://info.iet.unipi.it/~luigi/netmap" license=('BSD') groups=() -depends=('linux' 'glibc') -makedepends=('git' 'sed' 'gzip' 'linux-headers' 'abs' 'pacman' 'xmlto' 'docbook-xsl') +depends=('glibc') +makedepends=('git' 'sed' 'gzip' 'linux-headers' 'abs' 'pacman' 'xmlto' 'docbook-xsl' 'patch' 'bc') provides=() conflicts=() replaces=() @@ -20,7 +20,7 @@ options=() install="netmap.install" source=("netmap.install" "git+https://github.com/luigirizzo/netmap") noextract=() -md5sums=("9f936e9fdd86c8a18babdc5848812f92" "SKIP") +md5sums=("62cbf2409535cf25cb4a185d4fa21525" "SKIP") pkgver() { cd "$srcdir/${pkgname%-git}" @@ -50,7 +50,7 @@ build() { echo "SRCPKGDEST=$SRCPKGDEST" echo "PKGDEST=$PKGDEST" echo "BUILDDIR=$BUILDDIR" - # We force some makepkg variables, trying to ovverride yaourt default behaviour, + # We force some makepkg variables, trying to override yaourt default behaviour, # which is to download sources in $srcdir/../linux instead of the place where # makepkg is invoked SRCDEST=$NESTEDDIR SRCPKGDEST=$NESTEDDIR PKGDEST=$NESTEDDIR BUILDDIR=$NESTEDDIR \ @@ -61,49 +61,30 @@ build() { # kernel sources downloaded in the previous steps to copy the NIC # drivers. Note however that the kernel modules are built against the # running kernel, and not against the downloaded sources. - msg "Starting to build netmap" - cd "$srcdir/netmap/LINUX" - ./configure --kernel-sources=$NESTEDDIR/src/linux-$KMAJVER + # We need to use --no-ext-drivers to make sure netmap does not + # download (Intel) drivers sources from the internet, we want to use the + # drivers sources provided by the Arch linux package. + # We also build and install the patched drivers with a "-netmap" suffix, + # so that they can be modprobed without conflicts/ambiguity with the + # unpatched drivers + msg "Starting to build netmap and netmap applications" + cd "$srcdir/netmap" + msg "PREFIX=$pkgdir/usr/local" + msg "INSTALL-MOD-PATH=$pkgdir" + ./configure --kernel-sources=$NESTEDDIR/src/linux-$KMAJVER \ + --no-ext-drivers \ + --driver-suffix="_netmap" \ + --install-mod-path="$pkgdir/usr" \ + --prefix="$pkgdir/usr/local" make || return 1 - # Build pkt-gen and vale-ctl - cd "$srcdir/netmap/examples" - make clean # amend for existing .o - make pkt-gen vale-ctl || return 1 msg "Build complete" } package() { - # Compute the version numbers of the running kernel - KVER1=$(uname -r) - KVER2=$(uname -r | sed 's/\.[0-9]\+-[0-9]\+//') - - # Install the netmap module into the extramodules-VERSION directory - mkdir -p "$pkgdir/usr/lib/modules/extramodules-${KVER2}" - cp "$srcdir/netmap/LINUX/netmap.ko" "$pkgdir/usr/lib/modules/extramodules-${KVER2}" - - # Install pkt-gen and valectl into /usr/bin - mkdir -p "$pkgdir/usr/bin" - cp "$srcdir/netmap/examples/pkt-gen" "$pkgdir/usr/bin" - cp "$srcdir/netmap/examples/vale-ctl" "$pkgdir/usr/bin" - - # Install the netmap public headers - mkdir -p "$pkgdir/usr/include/net" - cp "$srcdir/netmap/sys/net/netmap.h" "$srcdir/netmap/sys/net/netmap_user.h" "$pkgdir/usr/include/net" - - # Install the netmap man page - mkdir -p "$pkgdir/usr/share/man/man4" - cp "$srcdir/netmap/share/man/man4/netmap.4" "$pkgdir/usr/share/man/man4" - gzip "$pkgdir/usr/share/man/man4/netmap.4" - - #Find and install the modified NIC drivers - cd "$srcdir/netmap/LINUX" - DRIVERS=$(find . -name "*.ko" -and ! -name "netmap.ko") - if [ -n "$DRIVERS" ]; then - mkdir -p "$pkgdir/usr/lib/modules/extramodules-${KVER2}/netmap-drivers" - cp --parent $DRIVERS "$pkgdir/usr/lib/modules/extramodules-${KVER2}/netmap-drivers" - cd "$pkgdir/usr/lib/modules/extramodules-${KVER2}/netmap-drivers" - find . -name "*.ko" -exec sh -c "mv {} \$(echo {} | sed 's|\.ko|_netmap\.ko|g')" \; - fi + cd "$srcdir/netmap" + # Install netmap module, patched drivers modules, applications, headers + # and the man page + make install } # vim:set ts=2 sw=2 et: diff --git a/LINUX/archlinux/netmap.install b/LINUX/archlinux/netmap.install index 501b912e1..2959495e5 100644 --- a/LINUX/archlinux/netmap.install +++ b/LINUX/archlinux/netmap.install @@ -1,8 +1,7 @@ post_common() { depmod -a KVER2=$(uname -r | sed 's/\.[0-9]\+-[0-9]\+//') - echo ">>> Netmap patched NIC drivers have been installed into" - echo ">>> /lib/modules/extramodules-${KVER2}/netmap-drivers, with" + echo ">>> Netmap patched NIC drivers have been installed with" echo ">>> a '_netmap.ko' suffix, so that they don't replace the" echo ">>> official ones provided by the linux package." echo ">>> You should therefore manually unload the official ones and" diff --git a/LINUX/bsd_glue.h b/LINUX/bsd_glue.h index 2816cb09e..5aefb98b5 100644 --- a/LINUX/bsd_glue.h +++ b/LINUX/bsd_glue.h @@ -54,16 +54,22 @@ #include // msleep #include // skb_copy_to_linear_data_offset #include +#include #include // virt_to_phys #include +#include // kmap +#include // tags -#define printf(fmt, arg...) printk(KERN_ERR fmt, ##arg) #define KASSERT(a, b) BUG_ON(!(a)) /*----- support for compiling on older versions of linux -----*/ #include "netmap_linux_config.h" +#ifndef dma_rmb +#define dma_rmb() rmb() +#endif /* dma_rmb */ + #ifdef NETMAP_LINUX_HAVE_PAGE_REF #include #endif /* NETMAP_LINUX_HAVE_PAGE_REF */ @@ -72,6 +78,15 @@ #define HRTIMER_MODE_REL HRTIMER_REL #endif +#ifdef NETMAP_LINUX_HAVE_HRTIMER_SETUP +#define nm_hrtimer_setup hrtimer_setup +#else +#define nm_hrtimer_setup(t_, f_, c_, m_) do { \ + hrtimer_init(t_, c_, m_); \ + (t_)->function = (f_); \ +} while (0) +#endif + #ifndef NETMAP_LINUX_HAVE_SKB_COPY_LINEAR #define skb_copy_from_linear_data_offset(skb, offset, to, copy) \ memcpy(to, (skb)->data + offset, copy) @@ -87,6 +102,10 @@ #define uintptr_t unsigned long #endif +#ifdef NETMAP_LINUX_HAVE_WAIT_QUEUE_ENTRY_T +#define wait_queue_t wait_queue_entry_t +#endif + #ifndef NETMAP_LINUX_HAVE_QUEUE_MAPPING #define skb_get_queue_mapping(m) (0) #define skb_set_queue_mapping(a, b) do { (void)(a); (void)(b); } while (0) @@ -95,9 +114,9 @@ #ifndef NETMAP_LINUX_HAVE_HRTIMER_FORWARD_NOW /* Forward a hrtimer so it expires after the hrtimer's current now */ static inline u64 hrtimer_forward_now(struct hrtimer *timer, - ktime_t interval) + ktime_t interval) { - return hrtimer_forward(timer, timer->base->get_time(), interval); + return hrtimer_forward(timer, timer->base->get_time(), interval); } #endif @@ -113,8 +132,9 @@ extern struct net init_net; #define netdev_ops hard_start_xmit struct net_device_ops { int (*ndo_start_xmit)(struct sk_buff *skb, struct net_device *dev); + int (*ndo_change_mtu)(struct net_device *dev, int new_mtu); }; -#endif /* NETDEV_OPS */ +#endif /* !NETDEV_OPS */ #ifndef NETMAP_LINUX_HAVE_NETDEV_TX_T #define netdev_tx_t int @@ -138,6 +158,9 @@ struct net_device_ops { NM_SET_PAGE_COUNT(&(page)[i_], 1);\ } while (0) #endif /* HAVE_SPLIT_PAGE */ +#ifndef NETMAP_LINUX_HAVE_FOLL_SPLIT +#define FOLL_SPLIT FOLL_SPLIT_PMD +#endif #if !defined(NETMAP_LINUX_HAVE_NNITD) && !defined(netdev_notifier_info_to_dev) #define netdev_notifier_info_to_dev(ptr) (ptr) @@ -173,6 +196,10 @@ static inline int skb_checksum_start_offset(const struct sk_buff *skb) { #define NM_UNREG_NETDEV_NOTIF(nb) unregister_netdevice_notifier(nb) #endif /* NETMAP_LINUX_HAVE_REG_NOTIF_RH */ +#ifndef NETMAP_LINUX_HAVE_PAGE_TO_VIRT +#define page_to_virt(p) phys_to_virt(page_to_phys(p)) +#endif /* NETMAP_LINUX_HAVE_PAGE_TO_VIRT */ + /*----------- end of LINUX_VERSION_CODE dependencies ----------*/ /* Type redefinitions. XXX check them */ @@ -183,7 +210,7 @@ typedef int bus_size_t; typedef int bus_dma_segment_t; typedef void * bus_addr_t; #define vm_paddr_t phys_addr_t -/* XXX the 'off_t' on Linux corresponds to a 'long' */ +/* the 'off_t' on Linux corresponds to a 'long' */ #define vm_offset_t uint32_t #define vm_ooffset_t unsigned long struct thread; @@ -230,7 +257,11 @@ struct thread; #define m_nextpkt next // chain of mbufs #define m_freem(m) dev_kfree_skb_any(m) // free a sk_buff +#ifdef NETMAP_LINUX_HAVE_REFCOUNT_T +#define MBUF_REFCNT(m) refcount_read(&((m)->users)) +#else /* !NETMAP_LINUX_HAVE_REFCOUNT_T */ #define MBUF_REFCNT(m) NM_ATOMIC_READ(&((m)->users)) +#endif /* !NETMAP_LINUX_HAVE_REFCOUNT_T */ /* * on tx we force skb->queue_mapping = ring_nr, * but on rx it is the driver that sets the value, @@ -257,9 +288,11 @@ struct thread; #define m_copydata(m, o, l, b) skb_copy_bits(m, o, b, l) -#define copyin(_from, _to, _len) copy_from_user(_to, _from, _len) -#define copyout(_from, _to, _len) copy_to_user(_to, _from, _len) +#define copyin(_from, _to, _len) (copy_from_user(_to, _from, _len) ? EFAULT : 0) +#define copyout(_from, _to, _len) (copy_to_user(_to, _from, _len) ? EFAULT : 0) +/* na attach/detach routines */ +#ifdef NETMAP_LINUX_HAVE_AX25PTR /* * struct ifnet is remapped into struct net_device on linux. * ifnet has an if_softc field pointing to the device-specific struct @@ -270,16 +303,67 @@ struct thread; * * if_xname name device name * we would use "features" but it is all taken. - * XXX check for conflict in flags use. * * In netmap we use if_pspare[0] to point to the netmap_adapter, * in linux we have no spares so we overload ax25_ptr, and the detection * for netmap-capable is some magic in the area pointed by that. */ -#define WNA(_ifp) (_ifp)->ax25_ptr +#define if_setnetmapadapter(_ifp, _na) do { \ + (_ifp)->ax25_ptr = _na; \ +} while (0) +#define if_getnetmapadapter(_ifp) ((struct netmap_adapter *)(_ifp)->ax25_ptr) + +/* use the default NM_ATTACH_NA/NM_DETACH_NA defined in netmap_kernel.h */ +#else /* !NETMAP_LINUX_HAVE_AX25PTR */ +/* + * We hide behind the ethtool_ops + */ +int linux_netmap_set_ringparam(struct net_device *, struct ethtool_ringparam * +#ifdef NETMAP_LINUX_HAVE_SETRNGPRM_4ARGS + , struct kernel_ethtool_ringparam * + , struct netlink_ext_ack * +#endif /* NETMAP_LINUX_HAVE_SETRNGPRM_4ARGS */ + ); +struct netmap_linux_magic { + struct ethtool_ops eto; + const struct ethtool_ops *save_eto; +}; +#define NM_OS_MAGIC struct netmap_linux_magic +#define if_setnetmapadapter(_ifp, _na) do { \ + (_ifp)->ethtool_ops = _na; \ +} while (0) +#define if_getnetmapadapter(_ifp) ((struct netmap_adapter *)(_ifp)->ethtool_ops) +#define NM_DETACH_NA(ifp) do { \ + (ifp)->ethtool_ops = NA(ifp)->magic.save_eto; \ +} while (0) +#define NM_ATTACH_NA(ifp, na) do { \ + if ((ifp)->ethtool_ops) { \ + (na)->magic.eto = *(ifp)->ethtool_ops; \ + (na)->magic.save_eto = (ifp)->ethtool_ops; \ + } else { \ + memset(&(na)->magic, 0, sizeof((na)->magic)); \ + } \ + (na)->magic.eto.set_ringparam = linux_netmap_set_ringparam; \ + (ifp)->ethtool_ops = &(na)->magic.eto; \ +} while (0) +#define NM_RESTORE_NA(ifp, na) do { \ + if (na == NULL) { \ + NM_DETACH_NA(ifp); \ + } else { \ + (ifp)->ethtool_ops = &(na)->magic.eto; \ + } \ +} while (0) +#define NM_NA_VALID(ifp) \ + (NA(ifp) && NA(ifp)->magic.eto.set_ringparam == \ + linux_netmap_set_ringparam) +#define NM_NA_CLASH(ifp) (0) // XXX +#endif /* NETAP_LINUX_HAVE_AX25PTR */ #define ifnet net_device /* remap */ -#define if_xname name /* field ifnet-> net_device */ +typedef struct net_device* if_t; /* remap */ +#define if_name(ifp) ifp->name + +#define if_inc_counter(ifp, flags, cnt) do {} while (0) /* some other FreeBSD APIs */ struct net_device* ifunit_ref(const char *name); @@ -289,8 +373,10 @@ void if_rele(struct net_device *ifp); /* hook to send from user space */ netdev_tx_t linux_netmap_start_xmit(struct sk_buff *, struct net_device *); +/* prevent MTU changes while in netmap mode */ +int linux_netmap_change_mtu(struct net_device *dev, int new_mtu); + /* prevent ring params change while in netmap mode */ -int linux_netmap_set_ringparam(struct net_device *, struct ethtool_ringparam *); #ifdef NETMAP_LINUX_HAVE_SET_CHANNELS int linux_netmap_set_channels(struct net_device *, struct ethtool_channels *); #endif @@ -304,23 +390,23 @@ int linux_netmap_set_channels(struct net_device *, struct ethtool_channels *); * (hard) interrupt context. */ typedef struct { - spinlock_t sl; - ulong flags; + spinlock_t sl; + ulong flags; } safe_spinlock_t; static inline void mtx_lock(safe_spinlock_t *m) { - spin_lock_irqsave(&(m->sl), m->flags); + spin_lock_irqsave(&(m->sl), m->flags); } static inline void mtx_unlock(safe_spinlock_t *m) { - ulong flags = ACCESS_ONCE(m->flags); - spin_unlock_irqrestore(&(m->sl), flags); + ulong flags = *(volatile ulong *)&m->flags; + spin_unlock_irqrestore(&(m->sl), flags); } #define mtx_init(a, b, c, d) spin_lock_init(&((a)->sl)) -#define mtx_destroy(a) // XXX spin_lock_destroy(a) +#define mtx_destroy(a) do {} while(0) #define mtx_lock_spin(a) mtx_lock(a) #define mtx_unlock_spin(a) mtx_unlock(a) @@ -341,10 +427,6 @@ static inline void mtx_unlock(safe_spinlock_t *m) #define BDG_SET_VAR(lval, p) ((lval) = (p)) #define BDG_GET_VAR(lval) (lval) -// XXX do we need GPF_ZERO ? -// XXX do we need GFP_DMA for slots ? -// http://www.mjmwired.net/kernel/Documentation/DMA-API.txt - #ifndef ilog2 /* not in 2.6.18 */ static inline int ilog2(uint64_t n) { @@ -356,10 +438,13 @@ static inline int ilog2(uint64_t n) } #endif /* ilog2 */ +/* XXX do we need GFP_DMA for slots ? + * Documentation/DMA-API.txt */ + #define contigmalloc(sz, ty, flags, a, b, pgsz, c) ({ \ unsigned int order_ = \ ilog2(roundup_pow_of_two(sz)/PAGE_SIZE); \ - struct page *p_ = alloc_pages(GFP_ATOMIC | __GFP_ZERO, \ + struct page *p_ = alloc_pages(GFP_USER | __GFP_ZERO, \ order_); \ if (p_ != NULL) \ split_page(p_, order_); \ @@ -379,13 +464,28 @@ static inline int ilog2(uint64_t n) struct nm_linux_selrecord_t; #define NM_SELRECORD_T struct nm_linux_selrecord_t -#define netmap_knlist_destroy(x) // XXX todo +#define netmap_knlist_destroy(x) // TODO #define tsleep(a, b, c, t) msleep(10) -// #define wakeup(sw) // XXX double check -#define microtime do_gettimeofday // debugging +#ifndef NETMAP_LINUX_HAVE_STRUCT_TIMEVAL +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* microseconds */ +}; +#endif /* !NETMAP_LINUX_HAVE_STRUCT_TIMEVAL */ +#define microtime do_gettimeofday /* debugging */ +#ifndef NETMAP_LINUX_HAVE_DO_GETTIMEOFDAY +#define do_gettimeofday(tv_) \ + do { \ + struct timespec64 now_; \ + \ + ktime_get_real_ts64(&now_); \ + (tv_)->tv_sec = now_.tv_sec; \ + (tv_)->tv_usec = now_.tv_nsec/1000; \ + } while (0) +#endif /* !NETMAP_LINUX_HAVE_DO_GETTIMEOFDAY */ /* * The following trick is to map a struct cdev into a struct miscdevice @@ -394,17 +494,6 @@ struct nm_linux_selrecord_t; #define cdev miscdevice #define cdevsw miscdevice - -/* - * XXX to complete - the dmamap interface - */ -#define BUS_DMA_NOWAIT 0 -#define bus_dmamap_load(_1, _2, _3, _4, _5, _6, _7) -#define bus_dmamap_unload(_1, _2) - -typedef int (d_mmap_t)(struct file *f, struct vm_area_struct *vma); -typedef unsigned int (d_poll_t)(struct file * file, struct poll_table_struct *pwait); - /* * make_dev_credf() will set an error and return the first argument. * This relies on the availability of the 'error' local variable. @@ -414,7 +503,7 @@ typedef unsigned int (d_poll_t)(struct file * file, struct poll_table_struct *pw */ #define make_dev_credf(_flags, _cdev, _zero, _cred, _uid, _gid, _perm, _name) \ ({error = misc_register(_cdev); \ - D("run mknod /dev/%s c %d %d # error %d", \ + nm_prinf("run mknod /dev/%s c %d %d # returned %d", \ (_cdev)->name, MISC_MAJOR, (_cdev)->minor, error); \ _cdev; } ) #define destroy_dev(_cdev) misc_deregister(_cdev) @@ -425,6 +514,7 @@ typedef unsigned int (d_poll_t)(struct file * file, struct poll_table_struct *pw * windows: they are emulated via get/setsockopt */ #define CTLFLAG_RD 1 +#define CTLFLAG_RDTUN CTLFLAG_RD #define CTLFLAG_RW 2 struct sysctl_oid; @@ -452,16 +542,16 @@ extern struct kernel_param_ops generic_sysctl_ops; ((_mode) == CTLFLAG_RD) ? 0444: 0644 ) #define SYSCTL_INT(_base, _oid, _name, _mode, _var, _val, _desc) \ - _SYSCTL_BASE(_name, _var, int, _mode) + _SYSCTL_BASE(_name, _var, int, _mode) #define SYSCTL_LONG(_base, _oid, _name, _mode, _var, _val, _desc) \ - _SYSCTL_BASE(_name, _var, long, _mode) + _SYSCTL_BASE(_name, _var, long, _mode) #define SYSCTL_ULONG(_base, _oid, _name, _mode, _var, _val, _desc) \ - _SYSCTL_BASE(_name, _var, ulong, _mode) + _SYSCTL_BASE(_name, _var, ulong, _mode) #define SYSCTL_UINT(_base, _oid, _name, _mode, _var, _val, _desc) \ - _SYSCTL_BASE(_name, _var, uint, _mode) + _SYSCTL_BASE(_name, _var, uint, _mode) // #define TUNABLE_INT(_name, _ptr) @@ -469,16 +559,13 @@ extern struct kernel_param_ops generic_sysctl_ops; #define SYSCTL_VNET_INT SYSCTL_INT #define SYSCTL_HANDLER_ARGS \ - struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req + struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req int sysctl_handle_int(SYSCTL_HANDLER_ARGS); int sysctl_handle_long(SYSCTL_HANDLER_ARGS); #define MALLOC_DECLARE(a) #define MALLOC_DEFINE(a, b, c) -struct netmap_adapter; -int netmap_linux_config(struct netmap_adapter *na, - u_int *txr, u_int *rxr, u_int *txd, u_int *rxd); /* ---- namespaces ------ */ #ifdef CONFIG_NET_NS int netmap_bns_register(void); @@ -492,4 +579,35 @@ void netmap_bns_unregister(void); #define if_printf(ifp, fmt, ...) dev_info(&(ifp)->dev, fmt, ##__VA_ARGS__) +#ifndef BIT_ULL +#define BIT_ULL(nr) (1ULL << (nr)) +#endif /* !BIT_ULL */ + +#ifndef NETMAP_LINUX_HAVE_ONLINE_CPUS +#define get_online_cpus() cpus_read_lock() +#define put_online_cpus() cpus_read_unlock() +#endif /* NETMAP_LINUX_HAVE_ONLINE_CPUS */ + +#ifdef NETMAP_LINUX_HAVE_NAPI_POLL_WEIGHT +#define NM_NETIF_NAPI_ADD netif_napi_add_weight +#else +#define NM_NETIF_NAPI_ADD netif_napi_add +#endif /* NETMAP_LINUX_HAVE_NAPI_POLL_WEIGHT */ + +#ifdef NETMAP_LINUX_HAVE_DEV_ADDR_SET +#define NM_DEV_ADDR_SET(a_, m_) dev_addr_set(a_, m_) +#else +#define NM_DEV_ADDR_SET(a_, m_) memcpy((a_)->dev_addr, m_, (a_)->addr_len) +#endif /* NETMAP_LINUX_HAVE_DEV_ADDR_SET */ + +#ifdef NETMAP_LINUX_HAVE_STRSCPY +#define strlcpy strscpy +#endif /* NETMAP_LINUX_HAVE_STRSCPY */ + +#ifdef NETMAP_LINUX_HAVE_EVENTFD_SIG_2ARGS +#define NM_EVENTFD_SIGNAL(c_) eventfd_signal(c_, 1) +#else +#define NM_EVENTFD_SIGNAL eventfd_signal +#endif /* NETMAP_LINUX_HAVE_EVENTFD_SIG_2ARGS */ + #endif /* NETMAP_BSD_GLUE_H */ diff --git a/LINUX/configure b/LINUX/configure index fd4bc36d5..c6bedb209 100755 --- a/LINUX/configure +++ b/LINUX/configure @@ -1,16 +1,21 @@ #!/bin/sh +# shellcheck disable=SC3043,SC2034,SC2012,SC1091 BUILDDIR=$PWD -SRCDIR=$(cd $(dirname $0); pwd) +SRCDIR=$(cd "$(dirname "$0")" || exit; pwd) MODNAME=netmap +DEBUG=1 +UTILS=1 +DRVERRFAIL= +DMASYNC=1 # setelem2n setelem2n() { local i=1 local n - for n in $(eval echo \$$1_avail); do - if [ $n = $2 ]; then + for n in $(eval echo \$"$1"_avail); do + if [ "$n" = "$2" ]; then echo $i return fi @@ -46,7 +51,7 @@ setop() esac if [ -n "$3" ]; then - w=$(setelem2n $1 $3) + w=$(setelem2n "$1" "$3") [ -n "$w" ] || { echo "unknown $1: $3" | warning return @@ -54,14 +59,14 @@ setop() fi case "$2" in is-empty) - [ $(eval echo \$$1) -eq 0 ] + [ "$(eval echo \$"$1")" -eq 0 ] ;; disable-all) eval "$1=0" ;; enable-all) eval "$1=0" - s=$(eval echo \$$1_avail) + s=$(eval echo \$"$1"_avail) for n in $s; do eval "$1=\$(( ($1 << 1) | 1))" done @@ -74,23 +79,24 @@ setop() ;; enabled) eval "w=\$(($1 & $w))" - [ $w -ne 0 ] + [ "$w" -ne 0 ] ;; print) - s=$(eval echo \$$1_avail) + s=$(eval echo \$"$1"_avail) e= for n in $s; do - w=$(setelem2n $1 $n) + w=$(setelem2n "$1" "$n") eval "w=\$(($1 & $w))" - [ $w -ne 0 ] && e="$n $e" + [ "$w" -ne 0 ] && e="$n $e" done - echo $e + echo "$e" ;; esac } # available subsystems -subsystem_avail="vale pipe monitor generic ptnetmap-guest ptnetmap-host sink" +subsystem_avail="vale pipe monitor generic ptnetmap sink \ + extmem null" #enabled subsystems (bitfield) subsystem=0 @@ -103,10 +109,12 @@ subsys enable vale subsys enable pipe subsys enable monitor subsys enable generic +subsys enable null +subsys enable ptnetmap # available drivers -driver_avail="r8169.c virtio_net.c forcedeth.c veth.c \ - e1000 e1000e igb ixgbe ixgbevf i40e" +driver_avail="stmmac r8169.c virtio_net.c forcedeth.c veth.c \ + e1000 e1000e igb igc ixgbe ixgbevf ice i40e vmxnet3 mlx5" # enabled drivers (bitfield) driver= drv() @@ -128,6 +136,9 @@ edrv enable igb edrv enable ixgbe edrv enable ixgbevf edrv enable i40e +edrv enable ice +edrv enable mlx5 +edrv enable virtio_net.c # drivers built by patching the system drivers setop internal_driver new driver @@ -136,6 +147,15 @@ idrv() setop internal_driver "$@" } +# external drivers that don't need the pre-patch test +setop custom_driver new driver +cdrv() +{ + setop custom_driver "$@" +} + +cdrv enable virtio_net.c + update_drivers() { edrv intersect driver idrv copy driver @@ -145,7 +165,7 @@ update_drivers() { update_drivers # available apps -application_avail="pkt-gen bridge lb tlem nmreplay vale-ctl" +application_avail="pkt-gen bridge lb tlem nmreplay vale-ctl dedup" application=0 app() { @@ -160,7 +180,7 @@ ld=ld # with : success iff is available with() { - type $1 >/dev/null 2>&1 + type "$1" >/dev/null 2>&1 } print_realpath() { @@ -174,19 +194,20 @@ print_realpath() { decode_version() { if [ -n "$1" ] && with perl; then - echo " [$($SRCDIR/scripts/vers $1 -C)]" + echo " [$("$SRCDIR"/scripts/vers "$1" -C)]" fi } # report: print the current state of the configuration variables report() { echo "kernel directory $ksrc" - print_realpath $ksrc + print_realpath "$ksrc" echo "kernel sources $src" print_realpath "$src" - local v=$lin_ver + local v="$lin_ver" [ -n "$v" ] || v="-" - local dv="$(decode_version $lin_ver)" + local dv + dv="$(decode_version "$lin_ver")" echo "linux version $v $dv" echo "module file $MODNAME.ko" echo @@ -236,11 +257,21 @@ warning() message WARNING } +drverror() { + message WARNING + if [ -n "$DRVERRFAIL" ]; then + error < - #include - #include + #include + #include + #include EOF cat # output the test code read from stdin - } > $TMPDIR/$1.c + cat < "$TMPDIR/$1".c { cat <> config.log # add the module to the running list - TESTOBJS="$1.o $TESTOBJS" + eval TESTOBJS$DRIVERTEST=\"$1.o \$TESTOBJS$DRIVERTEST\" # add the postprocess script for this test if [ -n "$2" ]; then add_file_exists_check "$1.o" "$2" "$3" @@ -394,17 +438,27 @@ EOF # explicitly naming the test. NEXTTEST=1 add_test() { - local t="__test__$NEXTTEST" + local t="${TESTPREFIX}test__$NEXTTEST" add_named_test $t "$@" - NEXTTEST=$(($NEXTTEST+1)) + NEXTTEST=$((NEXTTEST+1)) +} + +PERTESTFLAGS= +# add_test_flags flags... +# call this immediately before add_test to set special compilation flags +# for the next test +add_test_flags() { + PERTESTFLAGS="$PERTESTFLAGS +CFLAGS_test__$NEXTTEST.o := $*" } reset_tests() { - rm -rf $TMPDIR - mkdir $TMPDIR + rm -rf "$TMPDIR" + mkdir "$TMPDIR" TESTOBJS= TESTPOSTPROC= + DRIVERTEST= NEXTTEST=1 cat >> config.log < "$TMPDIR"/Makefile + NPROC=$(grep -c processor /proc/cpuinfo) + MAKE_OPT=-O + { + cat <> config.log + ( + cd "$TMPDIR" || exit + LANG=C make $MAKE_OPT -k -j "$NPROC" + ) >> config.log 2>&1 + if grep -q ": invalid option -- 'O'" config.log; then + MAKE_OPT= + # let us try again without -O + ( + cd "$TMPDIR" || exit + make -k -j "$NPROC" + ) >> config.log 2>&1 + fi + eval "$TESTPOSTPROC" + cat >> config.log < $TMPDIR/Makefile < "$TMPDIR"/Makefile <> "$TMPDIR"/Makefile <> "$TMPDIR"/Makefile <> $TMPDIR/Makefile <> "$TMPDIR"/Makefile <> $TMPDIR/Makefile + echo endif >> "$TMPDIR"/Makefile { cat <> config.log ( - cd $TMPDIR - make -k -j $(grep -c processor /proc/cpuinfo) - ) >> config.log + cd "$TMPDIR" || exit + make $MAKE_OPT -k -j "$NPROC" + ) >> config.log 2>&1 eval "$TESTPOSTPROC" cat >> config.log <> config.log +cp pre-commit .git/hooks + +appl_arch=$($cc -dumpmachine | cut -d '-' -f 1) +if [ "$appl_arch" != x86_64 ]; then + # dedup uses inline x86_64 assembly + app disable dedup +fi + ################################ # check for sane configuration ################################ +[ -n "$kernelver" ] || { + kernelver="$(uname -r)" +} + [ -n "$ksrc" ] || { # user did not provide a kernel dir, # we try to find one by ourselves - ksrc="/lib/modules/$(uname -r)/build" + ksrc="/lib/modules/${kernelver}/build" } [ -n "$src" ] || { # user did not provide the path for the full kernel sources # we try to find one by ourselves [ -d "$ksrc/source" ] && src="$ksrc/source" + [ -d "/lib/modules/${kernelver}/source" ] && src="/lib/modules/${kernelver}/source" [ -n "$src" ] || src=$ksrc } @@ -652,7 +809,7 @@ Otherwise, check that the 'build' symlink in is not broken. EOF fi -ksrc=$(cd $ksrc; pwd) +ksrc=$(cd "$ksrc" || exit; pwd) # check that ksrc has been prepared for external modules compilation # It should contain a version.h file (in one of two possible places, @@ -666,15 +823,65 @@ kernel not configured. The kernel directory must be ready for external module compilation. You may need to issue the following or equivalent commands: - cd $ksrc + cd "$ksrc" make oldconfig make modules_prepare EOF fi lin_ver=$(awk '/LINUX_VERSION_CODE/ { printf "%03x%02x", $3/256, $3%256}' "$version_hdr") -replace_vars $SRCDIR/default-config.mak.in_ > default-config.mak +################################################# +# preliminary build tests +################################################# + +reset_tests + +broken_buildsystem() { + error <> "$TMPDIR"/extra.mk + i=$((i+1)) +done + message " NOTE " <> extdrv-versions.mak +done + +replace_vars "$SRCDIR"/default-config.mak.in_ > default-config.mak +replace_vars "$SRCDIR"/intel-fix.sh_ > intel-fix.sh +replace_vars "$SRCDIR"/mlx5-config.sh_ > mlx5-config.sh +chmod +x intel-fix.sh +chmod +x mlx5-config.sh ############################################################### # Makefile creation @@ -689,13 +896,13 @@ replace_vars $SRCDIR/default-config.mak.in_ > default-config.mak mod_list= if ! drv is-empty; then - [ -d patches ] || { rm -f patches; ln -s $SRCDIR/final-patches patches; } + [ -d patches ] || { rm -f patches; ln -s "$SRCDIR"/final-patches patches; } fi -ln -s $SRCDIR/read-vars.mak 2>/dev/null || true +ln -s "$SRCDIR"/read-vars.mak 2>/dev/null . || true rm -f drivers.mak # read in all the kernel .config, we use it below -. $ksrc/.config +. "$ksrc"/.config # check for full kernel sources just once src_checked= src_found= @@ -708,19 +915,19 @@ for d in $(drv print); do drv_patch= drv_build= drv_distclean= - drv_conf="CONFIG_$(basename $d .c | tr a-z- A-Z_)" + drv_conf="CONFIG_$(basename "$d" .c | tr a-z- A-Z_)" # possibly override from config.mak - eval $(make -nrf read-vars.mak $d@vars E_DRIVERS="$e_drivers") + eval "$(make -snrf read-vars.mak "$d"@vars E_DRIVERS="$e_drivers")" # check that the original driver had been compiled as a module, otherwise # skip this driver # (we do this mainly to be sure that any module dependency has already # been taken care of) if [ -z "$drv_force" ]; then - m="$(eval echo \$$drv_conf)" + m="$(eval echo \$"$drv_conf")" [ -n "$m" ] || { echo "$drv_conf not set in $ksrc/.config, skipping $d" | warning - drv disable $d + drv disable "$d" continue } fi @@ -742,16 +949,16 @@ EOF message " NOTE " <&1 || { warning <&1 || { drverror < + + extern struct netmap_adapter *na; + void dummy(struct net_device *dev) + { + dev->ax25_ptr = na; + } +EOF + # iommu support add_test 'have IOMMU' < @@ -970,13 +1191,24 @@ EOF } EOF + # (un)use_mm is unavailable in kernel 5.8 and later + add_test 'have KTHREAD_USE_MM' < + #include + + void dummy(struct mm_struct *mm) + { + kthread_use_mm(mm); + } +EOF + # number of parameters in ndo_select_queue # (we expect at most one of these to succeed) params="NULL, NULL" for n in 2 3 4; do add_test "define SELECT_QUEUE $n" < - + u16 dummy(struct net_device_ops *ndo) { return ndo->ndo_select_queue($params); @@ -985,8 +1217,45 @@ EOF params="NULL, $params" done + # type of the 3rd param of ndo_select_queue + # defaults to void* if not defined + add_test 'define SELECT_QUEUE_PARM3 "struct net_device*"' < + + static u16 myselect(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev, select_queue_fallback_t fallback) + { + (void)dev; + (void)skb; + (void)sb_dev; + (void)fallback; + return 0; + } + struct net_device_ops ndo = { + .ndo_select_queue = myselect, + }; +EOF + + # type of the 3rd param of ndo_select_queue + # defaults to void* if not defined + add_test 'define SELECT_QUEUE_PARM3 "struct net_device*"' < + + static u16 myselect(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) + { + (void)dev; + (void)skb; + (void)sb_dev; + return 0; + } + struct net_device_ops ndo = { + .ndo_select_queue = myselect, + }; +EOF + # ethtool get_ringparam - add_test 'have GET_RINGPARAM' < #include @@ -996,6 +1265,19 @@ EOF } EOF + # ethtool extended get_ringparam + add_test 'define HAVE_GET_RINGPARAM 2' < + #include + + void + dummy(struct net_device *net, struct ethtool_ringparam *rp, + struct kernel_ethtool_ringparam *ker, + struct netlink_ext_ack *extack) { + net->ethtool_ops->get_ringparam(net, rp, ker, extack); + } +EOF + # ethtool set/get_channels add_test 'have SET_CHANNELS' < @@ -1051,6 +1333,16 @@ EOF } EOF + # check for netif_rx_ni + add_test 'have NETIF_RX_NI' < + + void dummy(struct sk_buff *skb) + { + netif_rx_ni(skb); + } +EOF + # poll_table key field for k in _key key; do add_test "define PWAIT_KEY $k" < - void dummy(struct hrtimer *timer, clockid_t which_clock) - { - hrtimer_init(timer, which_clock, HRTIMER_MODE_REL); - } + int dummy = HRTIMER_MODE_REL; EOF # check for IFF_LIVE_ADDR_CHANGE @@ -1228,17 +1517,15 @@ EOF } EOF - # arguments of qdisc_create_dflt (either 3 or 4) - add_test 'define QDISC_CREATE_DFLT_3ARGS' < + # first argument of sock_create_kern() is a 'struct net*' + add_test 'define SOCK_CREATE_KERN_NETNS' < - struct Qdisc * - dummy(struct netdev_queue *dev_queue, - struct Qdisc_ops* ops, - unsigned int parent_id) + int + dummy(void) { - return qdisc_create_dflt(dev_queue, - ops, parent_id); + struct net *net = NULL; + return sock_create_kern(net, 0, 0, 0, NULL); } EOF @@ -1360,157 +1647,789 @@ EOF } EOF - ##################################################### - # checks related to drivers # - ##################################################### - - # e1000e - if drv enabled e1000e; then - - add_file_exists_check e1000e/e1000.h true "drv_source_error e1000e" - - add_test 'have E1000E_HWADDR' <hw.hw_addr + ring->tail; - } -EOF - - add_test 'have E1000E_DOWN2' < + + int + dummy(struct net_device_ops *ops, struct net_device *dev) { + return ops->ndo_change_mtu_rh74(dev, 0); + } EOF - - add_test "grep -q '\' $TMPDIR/e1000e/netdev.c \ - && have E1000E_EXT_RXDESC" + + int + dummy(struct vm_operations_struct *ops, + struct vm_area_struct *vma, struct vm_fault *vmf) { + return ops->fault(vma, vmf); + } EOF - - add_test 'have RTL_WFQ' < + + void dummy(void) {} EOF - fi # r8169.c - - # ixgbe - if drv enabled ixgbe; then - - add_file_exists_check ixgbe/ixgbe.h true "drv_source_error ixgbe" - - # different versions of IXGBE_?X_DESC - add_test 'define IXGBE_DESC 1' < + + struct rtnl_link_stats64 * + dummy(struct net_device_ops *ops, struct net_device *dev, + struct rtnl_link_stats64 *storage) + { + return ops->ndo_get_stats64(dev, storage); + } EOF - add_test 'define IXGBE_DESC 2' < + + void + dummy(struct net_device *dev) + { + return dev->destructor(dev); + } EOF - add_test 'define IXGBE_DESC 3' < + + void * + dummy(wait_queue_entry_t *wq) { + return wq->private; + } EOF - - # array of rings or array of poiners to rings? - add_test 'define IXGBE_PTR_ARRAY' <tx_ring[0]; - } + +# pci_enable_msix or pci_alloc_irq_vectors ? + add_test 'have PCI_ENABLE_MSIX' < + + int + dummy(struct pci_dev *dev, struct msix_entry *entries, int nvec) { + return pci_enable_msix(dev, entries, nvec); + } EOF - # is ixgbe_irq_disable_queues available? - add_test "define IXGBE_HAVE_DISABLE" <users and qdisc->refcnt atomic_t or refcount_t ? + add_test 'have REFCOUNT_T' < - void - dummy(struct ixgbe_adapter *adapter, u64 qmask) { - ixgbe_irq_disable_queues(adapter, qmask); - } + unsigned int + dummy(struct sk_buff *skb) { + return refcount_read(&skb->users); + } EOF - fi # ixgbe - if drv enabled virtio_net.c; then - - add_file_exists_check virtio_net.c true "drv_source_error virtio_net.c" - - add_test 'define VIRTIO_CB_DELAYED' < - + # kernels from 4.14 onwards don't have support for UDP fragmentation + # offload. + add_test 'have UFO' < + + unsigned int + dummy(void) { + return NETIF_F_UFO; + } +EOF + +# check for get_user_pages_unlocked number of args + add_test 'have GUP_4ARGS' < + + long + dummy(unsigned long start, unsigned long nr_pages, + struct page **pages, unsigned int gup_flags) { + return get_user_pages_unlocked(start, nr_pages, pages, gup_flags); + } +EOF + + add_test 'have GUP_5ARGS' < + + long + dummy(unsigned long start, unsigned long nr_pages, + int write, int force, struct page **pages) { + return get_user_pages_unlocked(start, nr_pages, write, force, pages); + } +EOF + + add_test 'have GUP_6ARGS' < + + long + dummy(struct task_struct *tsk, struct mm_struct *mm, + unsigned long start, unsigned long nr_pages, + struct page **pages, unsigned int gup_flags) { + return get_user_pages_unlocked(tsk, mm, start, nr_pages, + pages, gup_flags); + } +EOF + + add_test 'have GUP_7ARGS' < + + long + dummy(struct task_struct *tsk, struct mm_struct *mm, + unsigned long start, unsigned long nr_pages, + int write, int force, struct page **pages) { + return get_user_pages_unlocked(tsk, mm, start, nr_pages, + write, force, pages); + } +EOF + + add_test 'have PIN_PAGES' < + + long + dummy(unsigned long start, unsigned long nr_pages, + unsigned int gup_flags, struct page **pages) { + return pin_user_pages_unlocked(start, nr_pages, pages, gup_flags); + } +EOF + + add_test 'have FOLL_SPLIT' < + + int + dummy(void) { + return FOLL_SPLIT; + } +EOF + +# check for page_to_virt + add_test 'have PAGE_TO_VIRT' < + + void * + dummy(struct page *page) { + return page_to_virt(page); + } +EOF + +# check for extack in Qdisc_ops init callback + add_test 'have QDISC_EXTACK' < + + int + dummy(struct Qdisc_ops *ops, struct netlink_ext_ack *extack) { + return ops->init(NULL, NULL, extack); + } +EOF + +# check for + add_test 'have AVERAGE_H' < + + DECLARE_EWMA(myname, 1, 64); + void + dummy(struct ewma_myname *x) { + ewma_myname_add(x, 18); + } +EOF + +# check for ethtool_validate_speed() and ethtool_validate_duplex() + add_test 'have ETHTOOL_VALIDATE' < + + int + dummy(void) { + return ethtool_validate_speed(0) + ethtool_validate_duplex(0); + } +EOF + + add_test 'have U64_STATS_IRQ' < + + unsigned int + dummy(const struct u64_stats_sync *x) { + return u64_stats_fetch_begin_irq(x); + } +EOF + + # is struct timeval defined? + add_test 'have STRUCT_TIMEVAL' << EOF + #include + + void + dummy(struct timeval *tv) { + tv->tv_sec = 0; + tv->tv_usec = 0; + return; + } +EOF + + add_test 'have DO_GETTIMEOFDAY' < + + void + dummy(struct timeval *tv) { + do_gettimeofday(tv); + } +EOF + + # is vm_fault_t defined? + add_test 'have VMFAULT_T' < + + vm_fault_t dummy(void) { + return 0; + } +EOF + + # nf_reset() or nf_reset_ct()? + add_test 'have NF_RESET_CT' < + + void dummy(struct sk_buff *skb) { + nf_reset_ct(skb); + } +EOF + + # setringparam additional parameters + add_test 'have SETRNGPRM_4ARGS' < + #include + + int + dummy(struct net_device *net, struct ethtool_ringparam *r, + struct kernel_ethtool_ringparam *k, + struct netlink_ext_ack *a) { + return net->ethtool_ops->set_ringparam(net, r, k, a); + } +EOF + + # put/get_online_cpus or cpu_read_lock/unlock? + add_test 'have ONLINE_CPUS' < + + void dummy(void) { + get_online_cpus(); + put_online_cpus(); + } +EOF + + # pollwait with 5 args (as seen in uek) + add_test 'have POLLWAIT_5ARGS' < + + void dummypoll(struct file *f, + wait_queue_head_t *w, + struct poll_table_struct *p, + unsigned long fixed_event); + + void dummy(poll_table *pt) { + init_poll_funcptr(pt, dummypoll); + } +EOF + + # netif_napi_poll_weight? + add_test 'have NAPI_POLL_WEIGHT' < + + void dummy(struct net_device *dev, + struct napi_struct *napi, + int (*poll)(struct napi_struct *, int), + int weight) + { + netif_napi_add_weight(dev, napi, poll, weight); + } +EOF + + # eth_type_vlan? + add_test 'have ETH_TYPE_VLAN' < + + bool dummy(__be16 ethertype) { + return eth_type_vlan(ethertype); + } +EOF + + # skb_vlan_tag_present? + add_test 'have SKB_VLAN_TAG_PRESENT' < + + int dummy(struct sk_buff *skb) { + return skb_vlan_tag_present(skb); + } +EOF + + # __vlan_hwaccess_push_inside? + add_test 'have VLAN_HWACCEL_PUSH_INSIDE' < + + struct sk_buff* dummy(struct sk_buff *skb) { + return __vlan_hwaccel_push_inside(skb); + } +EOF + + # skb_vlan_untag? + add_test 'have SKB_VLAN_UNTAG' < + + struct sk_buff* dummy(struct sk_buff *skb) { + return skb_vlan_untag(skb); + } +EOF + + # check for dev_addr_set() + add_test 'have DEV_ADDR_SET' < + + void dummy(struct net_device *dev, const unsigned char *addr) { + dev_addr_set(dev, addr); + } +EOF + + # check for strscpy() + add_test 'have STRSCPY' < + + ssize_t dummy(char *d, const char *s, size_t l) { + return strscpy(d, s, l); + } +EOF + + # check for 2nd param of eventfd_signal() + add_test 'have EVENTFD_SIG_2ARGS' < + + void dummy(struct eventfd_ctx *ctx) { + eventfd_signal(ctx, 1); + } +EOF + + # check for SetPageSwapBacked + add_test 'have SETPAGESWAPBACKED' < + + void dummy(struct page *page) { + SetPageSwapBacked(page); + } +EOF + + # check for NETIF_F_LLTX + add_test 'have NETIF_F_LLTX' < + + u64 dummy(void) { + return NETIF_F_LLTX; + } +EOF + + # check for hrtimer_setup + add_test 'have HRTIMER_SETUP' < + + void dummy(struct hrtimer *timer, + enum hrtimer_restart (*function)(struct hrtimer *), + clockid_t clock_id, enum hrtimer_mode mode) + { + hrtimer_setup(timer, function, clock_id, mode); + } +EOF + + # check for sockaddr_unsized + add_test 'have SOCKADDR_UNSIZED' < + + int dummy(struct socket *sock, + struct sockaddr_unsized *sa, + int sa_len) + { + return kernel_bind(sock, sa, sa_len); + } +EOF + + ##################################################### + # checks related to drivers # + ##################################################### + + # e1000e + if drv enabled e1000e; then + + add_file_exists_check e1000e/e1000.h true "drv_source_error e1000e" + + DRIVERTEST=e1000e + + add_test 'have E1000E_HWADDR' <hw.hw_addr + ring->tail; + } +EOF + + add_test 'have E1000E_DOWN2' <' '$TMPDIR'/e1000e/netdev.c \ + && have E1000E_EXT_RXDESC" tx_ring[0]; + } +EOF + + # is ixgbe_irq_disable_queues available? + add_test "define IXGBE_HAVE_DISABLE" <next_to_alloc; + } +EOF + + add_test 'have IXGBE_RX_BUFSZ' <state); + } +EOF + + fi # ixgbe + + if drv enabled ixgbevf; then + + add_file_exists_check ixgbevf/ixgbevf.h true "drv_source_error ixgbevf" + + DRIVERTEST=ixgbevf + + # IXGBEVF_* or IXGBE_*? + add_test true 'define IXGBEVF_IXGBE_MACROS' <tx_ring[0]; + } +EOF + # is next_to_alloc defined? + add_test "define IXGBEVF_HAVE_NTA" <next_to_alloc; + } +EOF + fi + + if drv enabled virtio_net.c; then + + VNETDIR="" + if edrv enabled virtio_net.c; then + VNETDIR="virtio_net.c/" + fi + + add_file_exists_check ${VNETDIR}virtio_net.c true "drv_source_error virtio_net.c" + + DRIVERTEST= + + add_test 'define VIRTIO_NET_HDR_FROM_SKB_5ARGS' < + + int + dummy(const struct sk_buff *skb, struct virtio_net_hdr *hdr, + bool little_endian, bool has_data_valid, int vlan_hlen) { + return virtio_net_hdr_from_skb(skb, hdr, little_endian, + has_data_valid, vlan_hlen); + } +EOF + + add_test 'define VIRTIO_NET_HDR_FROM_SKB_4ARGS' < + + int + dummy(const struct sk_buff *skb, struct virtio_net_hdr *hdr, + bool little_endian, bool has_data_valid) { + return virtio_net_hdr_from_skb(skb, hdr, little_endian, + has_data_valid); + } +EOF + + add_test 'define VIRTIO_NET_HDR_FROM_SKB_3ARGS' < + + int + dummy(const struct sk_buff *skb, struct virtio_net_hdr *hdr, + bool little_endian) { + return virtio_net_hdr_from_skb(skb, hdr, little_endian); + } +EOF + + add_test 'have FIND_VQS_CTX_ARG' < + + int + dummy(struct virtio_config_ops *ops, const bool *ctx, + struct irq_affinity *desc) + { + return ops->find_vqs(NULL, 0, NULL, NULL, NULL, + ctx, desc); + } +EOF + + add_test 'have FIND_VQS_IRQAFF_ARG' < + + int + dummy(struct virtio_config_ops *ops, struct irq_affinity *desc) + { + return ops->find_vqs(NULL, 0, NULL, NULL, NULL, + desc); + } +EOF + + add_test 'have VIRTIO_BYTEORDER' < + + u16 + dummy(bool little_endian, __virtio16 val) + { + return __virtio16_to_cpu(little_endian, val); + } +EOF + + add_test 'have VIRTIO_IS_LITTLE_ENDIAN' < + + bool + dummy(struct virtio_device *vdev) + { + return virtio_is_little_endian(vdev); + } +EOF + + add_test 'have VIRTIO_MEMORY_ACCESSORS' < + + u16 + dummy(struct virtio_device *vdev) + { + return virtio16_to_cpu(vdev, 0); + } +EOF + + add_test 'have VIRTIO_DRIVER_FEATURE_TABLE_LEGACY' < + + bool + dummy(struct virtio_driver *vdr) + { + return vdr->feature_table_legacy != NULL; + } +EOF + + add_test 'have VIRTIO_DEVICE_READY' < + + void + dummy(struct virtio_device *vdev) + { + virtio_device_ready(vdev); + } +EOF + + add_test 'have VIRTQUEUE_IS_BROKEN' < + + bool + dummy(struct virtqueue *vq) + { + return virtqueue_is_broken(vq); + } +EOF + + add_test 'have SKB_COALESCE_RX_FRAG' < + + void + dummy(struct sk_buff *skb, int i, int size, + unsigned int truesize) + { + skb_coalesce_rx_frag(skb, i, size, truesize); + } +EOF + + add_test 'have VIRTIO_CONFIG_ACCESSORS' < + + u16 + dummy(struct virtio_device *vdev, unsigned int offset) + { + return virtio_cread16(vdev, offset); + } +EOF + + add_test 'have U64_STATS_INIT' < + + void + dummy(struct u64_stats_sync *syncp) + { + u64_stats_init(syncp); + } +EOF + + add_test 'have NAPI_HASH_DEL' < + + void + dummy(struct napi_struct *napi) + { + napi_hash_del(napi); + } +EOF + + add_test 'define VIRTIO_CB_DELAYED' < + bool dummy(struct virtqueue *vq) { return virtqueue_enable_cb_delayed(vq); } EOF - + add_test 'define VIRTIO_GET_VRSIZE' < - + unsigned int dummy(struct virtqueue *vq) { return virtqueue_get_vring_size(vq); } EOF - + add_test 'define VIRTIO_FREE_PAGES' < - + void dummy(struct virtqueue *vq) { (void)virtqueue_kick(vq); } EOF - + for s in "" _gfp; do f="virtqueue_add_buf$s" add_test "define VIRTIO_ADD_BUF $f" < - + int dummy(struct virtqueue *vq, struct scatterlist sg[], unsigned int out_num, unsigned int in_num, @@ -1520,113 +2439,210 @@ EOF } EOF done - + add_test 'define VIRTIO_MULTI_QUEUE' <rq[0].vq; } EOF - + add_test 'define VIRTIO_RQ_NUM' <rq[0].num; } EOF - + add_test 'define VIRTIO_SG' <rx_sg; } EOF - + add_test 'define VIRTIO_NOTIFY' < - + void dummy(struct virtqueue *_vq) { (void)virtqueue_notify(_vq); } EOF - + + add_test 'have GET_LINK_KSETTINGS' < + + int + dummy(struct ethtool_ops *ops, struct ethtool_link_ksettings *l) + { + return ops->get_link_ksettings(NULL, l); + } +EOF + fi # virtio-net - + if drv enabled i40e; then + + DRIVERTEST=i40e + + add_test_flags -I\$M/i40e add_test 'define I40E_PTR_ARRAY' <tx_rings[0]; } EOF + + add_test_flags -I\$M/i40e + add_test 'define I40E_PTR_STATE' < + #pragma GCC diagnostic error "-Wincompatible-pointer-types" + + int + dummy(struct i40e_pf *pf) { + return test_and_set_bit(1, &pf->state); + } +EOF fi # i40e + if drv enabled ice; then + + DRIVERTEST=ice + + add_test_flags -I\$M/ice + add_test 'have ICE_XRINGS' <tx_rings[0]; + } + struct ice_rx_ring * + dummy2(struct ice_vsi *vsi) { + return vsi->rx_rings[0]; + } +EOF + + add_test_flags -I\$M/ice + add_test 'have ICE_VSI_DOWN' <next_to_alloc; + } +EOF + + add_test 'have IGB_STATE_INDIR' <state); } EOF fi # igb - + # END_TESTS - + # now we actually create the file - + rm -f $configh cat > $configh <> config.log <> config.log + symf="$ksrc"/Module.symvers + if [ -e "$symf" ]; then + if cut -f2 "$symf" | grep -q split_page; then + echo "found in '$symf'" >> config.log have SPLIT_PAGE else - echo "not found in $symf" >> config.log + echo "not found in '$symf'" >> config.log fi else - echo "$symf file not found" >> config.log + echo "'$symf' file not found" >> config.log fi cat >> config.log <> $configh < $(basename $f .in) +for f in "$SRCDIR"/*.in; do + replace_vars "$f" > "$(basename "$f" .in)" done # if we are in SRCDIR this will fail, since # Makefile already exists -ln -s $SRCDIR/../GNUmakefile || true -ln -s $SRCDIR/drv-subdir.mak || true +ln -s "$SRCDIR"/../GNUmakefile . || true +ln -s "$SRCDIR"/drv-subdir.mak . || true report +# create the build directory for libnetmap +mkdir -p build-libnetmap +ln -s "$SRCDIR"/../libnetmap/GNUmakefile build-libnetmap 2>/dev/null || true + # create the build directory for the examples mkdir -p build-apps for a in $(app print); do - mkdir -p build-apps/$a - ln -s $SRCDIR/../apps/$a/GNUmakefile build-apps/$a/GNUmakefile 2> /dev/null || true + mkdir -p build-apps/"$a" + ln -s "$SRCDIR"/../apps/"$a"/GNUmakefile build-apps/"$a"/GNUmakefile 2> /dev/null || true done +# create the build directory for the utils +if [ -n "$UTILS" ]; then + mkdir -p build-utils + ln -s "$SRCDIR"/../utils/GNUmakefile build-utils/GNUmakefile 2>/dev/null || true + ln -s "$SRCDIR"/../utils/tests 2>/dev/null . || true +fi + # config.status can be used to rerun configure with the # same arguments rm -f config.status diff --git a/LINUX/default-config.mak.in_ b/LINUX/default-config.mak.in_ index f26cb206b..b4f4c8d53 100644 --- a/LINUX/default-config.mak.in_ +++ b/LINUX/default-config.mak.in_ @@ -1,20 +1,152 @@ +############################################################################### +# This files defines the default values of the external-drivers variables, +# used by the configure script and the generated netmap.mak makefile. +# +# The variables are as follows: +# +# driver@v the driver version +# driver@fetch how to fetch the sources into the ext-drivers directory +# driver@src how to extract the sources +# driver@patch which patch(es) to apply (whitespace-separated) +# driver@prepare how to prepare the sources for compilation +# driver@build how to build the driver +# driver@install how to install the driver +# driver@clean how to clean the driver build directory +# driver@disclean how to completely remove the driver build directory(ies) +# driver@force configure refuses to build drivers which are not +# configured as modules in the kernel ".config". A 1 here +# forces configure to skip this check. +# +# In the configuration phase, the configure script tries to determine whether +# the external driver compiles by itself (i.e., independently of the netmap +# patch) by running the driver@fetch; driver@src; driver@prepare; driver@build +# commands (i.e., skipping the driver@patch). If this fails, a warning is +# printed and the driver is disabled. +# +# During the build phase the same commands are executed again, but this time +# the driver@patch is applied after driver@src and before driver@prepare. +# +# There should be no need to change this file if you just want to customize +# these variables for a particular build: put the overrides into a config.mak +# file in the build directory, before running configure. +# +# If you just want to select a different external-driver version, among the +# ones for which there is a patch in LINUX/final-patches, use the +# --select-version=driver:version option of configure. +# +################################################################################# + + +# default-config.mak is generated from default-config.mak_ in the LINUX directory, +# by replacing the recognized @VAR@ strings with the value of the named VAR +# in the configure scripts. The most important recognized variables are +# +# SRCDIR absolute path of the netmap/LINUX directory +# KSRC source directory of the linux kernel (headers should be sufficient +# for external drivers) +# SRC source directory of the full kernel sources +# KOPTS options intended for the linux make (accumulated via the +# --kernel-opts= configure option +# DRVSUFFIX the netmap driver suffix (--driver-suffix= configure option) +# MODPATH where to install the modules (--install-mod-path= from configure) +# TMPDIR the temporary directory where configure runs its tests (including +# the test build of the unpatched driver) +# +# In the build phase, the EXTRA_CFLAGS variable will contain the values assigned +# in the netmap.mak makefile. This is intended for options needed by the patched +# driver. During the configure phase the variable is empty. + + +# all the intel drivers are compiled in much the same way, so we factor them +# here. $(1) is the driver name, while $(2) is the driver version define intel_driver +$(1)@fetch := test -e @SRCDIR@/ext-drivers/$(1)/v$(2).zip || wget https://github.com/intel/ethernet-linux-$(1)/archive/refs/tags/v$(2).zip -P @SRCDIR@/ext-drivers/$(1) +$(1)@src := unzip -u @SRCDIR@/ext-drivers/$(1)/v$(2).zip && ln -s ethernet-linux-$(1)-$(2)/src $(1) +$(1)@patch := patches/intel--$(1)--$(2) +$(1)@build = make -C $(1) CFLAGS_EXTRA="$$($(1)@cflags) $(EXTRA_CFLAGS)" @KOPTS@ NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ KSRC=@SRC@ KBUILD_EXTRA_SYMBOLS=@BUILDDIR@/Module.symvers +$(1)@install = make -C $(1) install INSTALL_MOD_PATH=@MODPATH@ CFLAGS_EXTRA="$$($(1)@cflags) $(EXTRA_CFLAGS)" NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ KSRC=@SRC@ KBUILD_EXTRA_SYMBOLS=@BUILDDIR@/Module.symvers +$(1)@clean = if [ -d $(1) ]; then make -C $(1) clean CFLAGS_EXTRA="$$($(1)@cflags) $(EXTRA_CFLAGS)" NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ KSRC=@SRC@; fi +$(1)@distclean := rm -rf ethernet-linux-$(1)-$(2) +$(1)@force := 1 +endef + +define intel_legacy_driver $(1)@fetch := test -e @SRCDIR@/ext-drivers/$(1)-$(2).tar.gz || wget https://sourceforge.net/projects/e1000/files/$(1)%20stable/$(2)/$(1)-$(2).tar.gz -P @SRCDIR@/ext-drivers/ $(1)@src := tar xf @SRCDIR@/ext-drivers/$(1)-$(2).tar.gz && ln -s $(1)-$(2)/src $(1) $(1)@patch := patches/intel--$(1)--$(2) -$(1)@build := make -C $(1) CFLAGS_EXTRA="$($(1)@cflags) $(EXTRA_CFLAGS)" @KOPTS@ NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ KSRC=@KSRC@ -$(1)@install := make -C $(1) install INSTALL_MOD_PATH=@MODPATH@ CFLAGS_EXTRA="$($(1)@cflags) $(EXTRA_CFLAGS)" NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ KSRC=@KSRC@ -$(1)@clean := if [ -d $(1) ]; then make -C $(1) clean CFLAGS_EXTRA="$($(1)@cflags) $(EXTRA_CFLAGS)" NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ KSRC=@KSRC@; fi +$(1)@build = make -C $(1) CFLAGS_EXTRA="$$($(1)@cflags) $(EXTRA_CFLAGS)" @KOPTS@ NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ KSRC=@KSRC@ KBUILD_EXTRA_SYMBOLS=@BUILDDIR@/Module.symvers +$(1)@install = make -C $(1) install INSTALL_MOD_PATH=@MODPATH@ CFLAGS_EXTRA="$$($(1)@cflags) $(EXTRA_CFLAGS)" NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ KSRC=@KSRC@ KBUILD_EXTRA_SYMBOLS=@BUILDDIR@/Module.symvers +$(1)@clean = if [ -d $(1) ]; then make -C $(1) clean CFLAGS_EXTRA="$$($(1)@cflags) $(EXTRA_CFLAGS)" NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ KSRC=@KSRC@; fi $(1)@distclean := rm -rf $(1)-$(2) $(1)@force := 1 endef -enabled_intel_driver = $(if $(filter $(1),$(E_DRIVERS)),$(eval $(call intel_driver,$(1),$(2)))) +# set driver@v is not yet defined +define default +$(1)@v := $(if $($(1)@v),$($(1)@v),$(2)) +endef -$(call enabled_intel_driver,ixgbe,5.0.4) -$(call enabled_intel_driver,ixgbevf,4.0.3) +# set all the default versions (can be overridden by --select-version=) +$(eval $(call default,ixgbe,5.15.2)) +$(eval $(call default,ixgbevf,4.15.1)) +$(eval $(call default,e1000e,3.8.7)) +$(eval $(call default,igb,5.10.2)) +$(eval $(call default,i40e,2.19.3)) +$(eval $(call default,ice,1.9.11)) + +# some additional, driver-specific CFLAGS (used in the @build variable above) and fixes e1000e@cflags := -fno-pie -$(call enabled_intel_driver,e1000e,3.3.5.3) -igb@cflags := -DDISABLE_PACKET_SPLIT -fno-pie -$(call enabled_intel_driver,igb,5.3.5.4) -$(call enabled_intel_driver,i40e,2.0.19) +igb@cflags := -DDISABLE_PACKET_SPLIT -fno-pie $(addprefix -Wno-,@REC_DISABLED_WARNINGS@) +ixgbe@cflags := $(addprefix -Wno-,@REC_DISABLED_WARNINGS@) +i40e@cflags := $(addprefix -Wno-,@REC_DISABLED_WARNINGS@) +igb@prepare := $(if $(filter $(igb@v),5.3.5.61 5.3.6 5.4.6 5.5.2 5.7.2 5.8.5 5.9.3 5.10.2),@BUILDDIR@/intel-fix.sh igb,) +e1000e@prepare := $(if $(filter $(e1000e@v),3.8.4 3.8.7),@BUILDDIR@/intel-fix.sh e1000e,) +ixgbevf@prepare := $(if $(filter $(ixgbevf@v),4.7.1 4.8.1 4.9.3 4.10.2 4.11.1 4.12.4 4.13.3 4.14.5 4.15.1),@BUILDDIR@/intel-fix.sh ixgbevf,) +ixgbe@prepare := $(if $(filter $(ixgbe@v),5.8.1 5.9.4 5.10.2 5.11.3 5.12.5 5.13.4),@BUILDDIR@/intel-fix.sh ixgbe,) +i40e@prepare := $(if $(filter $(i40e@v),2.12.6 2.14.13 2.15.9 2.16.11 2.17.4 2.17.15 2.18.9 2.19.3),@BUILDDIR@/intel-fix.sh i40e,) +ice@prepare := $(if $(filter $(ice@v),1.7.16 1.8.8 1.8.9 1.9.7 1.9.11 1.10.1.2 1.10.1.2.2),@BUILDDIR@/intel-fix.sh ice,) + +# some additional, driver-specific configuration +stmmac@conf := CONFIG_STMMAC_ETH + +# only define the drivers that are selected after the --(no-)ext-drivers= processing (variable E_DRIVERS) +$(foreach d,$(filter ixgbe ixgbevf igb ice i40e,$(E_DRIVERS)),$(eval $(call intel_driver,$d,$($(d)@v)))) +$(foreach d,$(filter e1000e,$(E_DRIVERS)),$(eval $(call intel_legacy_driver,$d,$($(d)@v)))) + +ifneq ($(filter e1000e,$(E_DRIVERS)),) +e1000e@fetch := test -e @SRCDIR@/ext-drivers/e1000e-$(e1000e@v).tar.gz || wget https://sourceforge.net/projects/e1000/files/e1000e%20historic%20archive/$(e1000e@v)/e1000e-$(e1000e@v).tar.gz -P @SRCDIR@/ext-drivers/ +endif + +define mellanox_driver +$(1)@fetch := test -e @SRCDIR@/ext-drivers/MLNX_EN_SRC-debian-$(2).tgz || wget https://content.mellanox.com/ofed/MLNX_EN-$(2)/MLNX_EN_SRC-debian-$(2).tgz -P @SRCDIR@/ext-drivers +$(1)@src := tar xf @SRCDIR@/ext-drivers/MLNX_EN_SRC-debian-$(2).tgz&& tar xf MLNX_EN_SRC-$(2)/SOURCES/mlnx-en_$($(1)@pv)*.tar.gz && ln -s mlnx-en-$($(1)@pv)* $(1) +$(1)@patch := patches/mellanox--$(1)--$($(1)@pv) +$(1)@prepare := @BUILDDIR@/mlx5-config.sh +$(1)@build := make -C $(1) NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ EXTRA_CFLAGS="$$($(1)@cflags) $(EXTRA_CFLAGS)" +$(1)@install := make -C $(1) install_modules INSTALL_MOD_PATH=@MODPATH@ NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ +$(1)@clean := if [ -d $(1) ]; then make -C $(1) clean; fi NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ +$(1)@distclean := rm -rf mlnx-en-$($(1)@pv).* mlnx-en-$(2) mlx5-config.sh MLNX_EN_SRC-$(2) +$(1)@force := 1 +endef + +$(eval $(call default,mlx5,5.8-3.0.7.0)) +mlx5@pv = $(firstword $(subst -, ,$(mlx5@v))) +mlx5@conf = CONFIG_MLX5_CORE_EN +mlx5@cflags = -Wframe-larger-than=2000 +mlx5@prepare := $(if $(filter $(mlx5@v),5.8),@SRCDIR@/mlx5-prepare.sh @KSRC@,) + +$(foreach d,$(filter mlx5,$(E_DRIVERS)),$(eval $(call mellanox_driver,$d,$($(d)@v)))) + +define virtio_net +virtio_net.c@fetch := test -e @SRCDIR@/ext-drivers/virtio_net.c || wget https://raw.githubusercontent.com/torvalds/linux/v4.9/drivers/net/virtio_net.c -P @SRCDIR@/ext-drivers/ +virtio_net.c@src := mkdir -p virtio_net.c && cp @SRCDIR@/ext-drivers/virtio_net.c virtio_net.c/ +virtio_net.c@patch := patches/custom--virtio_net.c--4.9 +virtio_net.c@prepare := +virtio_net.c@build := [ -z "$(EXTRA_CFLAGS)" ] || make -C virtio_net.c EXTRA_CFLAGS="$(EXTRA_CFLAGS)" @KOPTS@ NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ KSRC=@KSRC@ +virtio_net.c@install := make -C virtio_net.c install INSTALL_MOD_PATH=@MODPATH@ EXTRA_CFLAGS="$(EXTRA_CFLAGS)" NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ KSRC=@KSRC@ +virtio_net.c@clean := if [ -d virtio_net.c ]; then make -C virtio_net.c clean EXTRA_CFLAGS="$(EXTRA_CFLAGS)" NETMAP_DRIVER_SUFFIX=@DRVSUFFIX@ KSRC=@KSRC@; fi +virtio_net.c@distclean := +virtio_net.c@force := 1 +endef + +$(foreach d,$(filter virtio_net.c,$(E_DRIVERS)),$(eval $(call virtio_net))) diff --git a/LINUX/dkms/README.md b/LINUX/dkms/README.md index b569482b5..16c9e3dfa 100644 --- a/LINUX/dkms/README.md +++ b/LINUX/dkms/README.md @@ -1,6 +1,16 @@ DKMS GUIDE ========== +**Disclaimer** +The dkms build infrastructure is not the official way to build netmap and its +patched drivers. +This alternative build system is not actively maintained, so you may need some +tweaks to make it work on your platform. + +Please prefer the standard ./configure && make && make install process to +build netmap. +************** + Some prerequisites: # apt-get install dkms linux-source linux-headers-$(uname -r) devscripts diff --git a/LINUX/dkms/dkms.conf b/LINUX/dkms/dkms.conf index 4d0017bd5..5f503bf3a 100644 --- a/LINUX/dkms/dkms.conf +++ b/LINUX/dkms/dkms.conf @@ -5,47 +5,69 @@ REMAKE_INITRD=yes AUTOINSTALL=yes # netmap driver -MAKE[0]=\'make\' +MAKE[0]="'make' kernelver=$kernelver" BUILT_MODULE_NAME[0]=netmap -BUILT_MODULE_LOCATION[0]=LINUX/ DEST_MODULE_LOCATION[0]=/kernel/net/netmap/ # forcedeth driver BUILT_MODULE_NAME[1]=forcedeth -BUILT_MODULE_LOCATION[1]=LINUX/ DEST_MODULE_LOCATION[1]=/kernel/drivers/net/ethernet/nvidia/ # veth driver BUILT_MODULE_NAME[2]=veth -BUILT_MODULE_LOCATION[2]=LINUX/ DEST_MODULE_LOCATION[2]=/kernel/drivers/net/ # virtio_net driver BUILT_MODULE_NAME[3]=virtio_net -BUILT_MODULE_LOCATION[3]=LINUX/ DEST_MODULE_LOCATION[3]=/kernel/drivers/net/ # e1000 driver BUILT_MODULE_NAME[4]=e1000 -BUILT_MODULE_LOCATION[4]=LINUX/e1000/ +BUILT_MODULE_LOCATION[4]=e1000/ DEST_MODULE_LOCATION[4]=/kernel/drivers/net/ethernet/intel/e1000/ # e1000e driver BUILT_MODULE_NAME[5]=e1000e -BUILT_MODULE_LOCATION[5]=LINUX/e1000e/ +BUILT_MODULE_LOCATION[5]=e1000e/ DEST_MODULE_LOCATION[5]=/kernel/drivers/net/ethernet/intel/e1000e/ # igb driver BUILT_MODULE_NAME[6]=igb -BUILT_MODULE_LOCATION[6]=LINUX/igb/ +BUILT_MODULE_LOCATION[6]=igb/ DEST_MODULE_LOCATION[6]=/kernel/drivers/net/ethernet/intel/igb/ # ixgbe driver BUILT_MODULE_NAME[7]=ixgbe -BUILT_MODULE_LOCATION[7]=LINUX/ixgbe/ +BUILT_MODULE_LOCATION[7]=ixgbe/ DEST_MODULE_LOCATION[7]=/kernel/drivers/net/ethernet/intel/ixgbe/ # i40e driver BUILT_MODULE_NAME[8]=i40e -BUILT_MODULE_LOCATION[8]=LINUX/i40e/ +BUILT_MODULE_LOCATION[8]=i40e/ DEST_MODULE_LOCATION[8]=/kernel/drivers/net/ethernet/intel/i40e/ + +# vmxnet3 driver +BUILT_MODULE_NAME[9]=vmxnet3 +BUILT_MODULE_LOCATION[9]=vmxnet3/ +DEST_MODULE_LOCATION[9]=/kernel/drivers/net/vmxnet3/ + +# mlx5 driver +BUILT_MODULE_NAME[10]=mlx5_core +BUILT_MODULE_LOCATION[10]=mlx5/drivers/net/ethernet/mellanox/mlx5/core +DEST_MODULE_LOCATION[10]=/kernel/drivers/net/ethernet/mellanox/mlx5/ + +BUILT_MODULE_NAME[11]=mlxfw +BUILT_MODULE_LOCATION[11]=mlx5/drivers/net/ethernet/mellanox/mlxfw/ +DEST_MODULE_LOCATION[11]=/kernel/drivers/net/ethernet/mellanox/mlx5/ + +BUILT_MODULE_NAME[12]=mlx_compat +BUILT_MODULE_LOCATION[12]=mlx5/compat/ +DEST_MODULE_LOCATION[12]=/kernel/drivers/net/ethernet/mellanox/mlx5/ + +BUILT_MODULE_NAME[13]=auxiliary +BUILT_MODULE_LOCATION[13]=mlx5/drivers/base/ +DEST_MODULE_LOCATION[13]=/kernel/drivers/net/ethernet/mellanox/mlx5/ + +BUILT_MODULE_NAME[14]=mlxdevm +BUILT_MODULE_LOCATION[14]=mlx5/net/mlxdevm/ +DEST_MODULE_LOCATION[14]=/kernel/drivers/net/ethernet/mellanox/mlx5/ diff --git a/LINUX/final-patches/custom--virtio_net.c--4.9 b/LINUX/final-patches/custom--virtio_net.c--4.9 new file mode 100644 index 000000000..66a437a29 --- /dev/null +++ b/LINUX/final-patches/custom--virtio_net.c--4.9 @@ -0,0 +1,1017 @@ +diff --git a/virtio_net.c/Makefile b/virtio_net.c/Makefile +new file mode 100644 +index 0000000..2c88957 +--- /dev/null ++++ b/virtio_net.c/Makefile +@@ -0,0 +1,25 @@ ++ifneq ($(KERNELRELEASE),) ++ ++# virtio_net_src.c is just a symbolic link to virtio_net.c ++# This workaround is needed because when defining modulename-y ++# it is not possible to have a source called "modulename.c". ++# Note that this is a problem only when NETMAP_DRIVER_SUFFIX ++# is empty. ++EXTRA_CFLAGS += "${EXTRA_CFLAGS}" ++obj-m := virtio_net$(NETMAP_DRIVER_SUFFIX).o ++virtio_net$(NETMAP_DRIVER_SUFFIX)-y := virtio_net_src.o ++ ++else ++ ++KSRC ?= /lib/modules/$(shell uname -r)/build ++ ++all: virtio_net.c ++ $(MAKE) -C "${KSRC}" M=$(shell pwd) modules ++ ++install: ++ $(MAKE) -C "${KSRC}" M=$(shell pwd) modules_install ++ ++clean: ++ $(MAKE) -C "${KSRC}" M=$(shell pwd) clean ++ ++endif +diff --git a/virtio_net.c/virtio_net.c b/virtio_net.c/virtio_net.c +index cbf1c61..9b92747 100644 +--- a/virtio_net.c/virtio_net.c ++++ b/virtio_net.c/virtio_net.c +@@ -26,8 +26,12 @@ + #include + #include + #include +-#include +-#include ++ ++#include /* needed for netmap_linux_config.h */ ++ ++#ifndef NAPI_POLL_WEIGHT ++#define NAPI_POLL_WEIGHT 64 ++#endif + + static int napi_weight = NAPI_POLL_WEIGHT; + module_param(napi_weight, int, 0444); +@@ -40,6 +44,37 @@ module_param(gso, bool, 0444); + #define GOOD_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN) + #define GOOD_COPY_LEN 128 + ++#ifdef NETMAP_LINUX_HAVE_AVERAGE_H ++#include ++#else /* !NETMAP_LINUX_HAVE_AVERAGE_H */ ++/* Exponentially weighted moving average (EWMA) */ ++#define DECLARE_EWMA(name, _factor, _weight) \ ++ struct ewma_##name { \ ++ unsigned long internal; \ ++ }; \ ++ static inline void ewma_##name##_init(struct ewma_##name *e) \ ++ { \ ++ e->internal = 0; \ ++ } \ ++ static inline unsigned long \ ++ ewma_##name##_read(struct ewma_##name *e) \ ++ { \ ++ return e->internal >> ilog2(_factor); \ ++ } \ ++ static inline void ewma_##name##_add(struct ewma_##name *e, \ ++ unsigned long val) \ ++ { \ ++ unsigned long internal = ACCESS_ONCE(e->internal); \ ++ unsigned long weight = ilog2(_weight); \ ++ unsigned long factor = ilog2(_factor); \ ++ \ ++ ACCESS_ONCE(e->internal) = internal ? \ ++ (((internal << weight) - internal) + \ ++ (val << factor)) >> weight : \ ++ (val << factor); \ ++ } ++#endif /* !NETMAP_LINUX_HAVE_AVERAGE_H */ ++ + /* RX packet size EWMA. The average packet size is used to determine the packet + * buffer size when refilling RX rings. As the entire RX ring may be refilled + * at once, the weight is chosen so that the EWMA will be insensitive to short- +@@ -70,6 +105,8 @@ struct send_queue { + /* TX: fragments + linear part + virtio header */ + struct scatterlist sg[MAX_SKB_FRAGS + 2]; + ++ struct virtio_net_hdr_mrg_rxbuf shared_txvhdr ____cacheline_aligned_in_smp; ++ + /* Name of the send queue: output.$index */ + char name[40]; + }; +@@ -93,6 +130,8 @@ struct receive_queue { + /* RX: fragments + linear part + virtio header */ + struct scatterlist sg[MAX_SKB_FRAGS + 2]; + ++ struct virtio_net_hdr_mrg_rxbuf shared_rxvhdr ____cacheline_aligned_in_smp; ++ + /* Name of this receive queue: input.$index */ + char name[40]; + }; +@@ -135,13 +174,6 @@ struct virtnet_info { + /* Work struct for config space updates */ + struct work_struct config_work; + +- /* Does the affinity hint is set for virtqueues? */ +- bool affinity_hint_set; +- +- /* CPU hotplug instances for online & dead */ +- struct hlist_node node; +- struct hlist_node node_dead; +- + /* Control VQ buffers: protected by the rtnl lock */ + struct virtio_net_ctrl_hdr ctrl_hdr; + virtio_net_ctrl_ack ctrl_status; +@@ -220,35 +252,44 @@ static struct page *get_a_page(struct receive_queue *rq, gfp_t gfp_mask) + return p; + } + +-static void skb_xmit_done(struct virtqueue *vq) +-{ +- struct virtnet_info *vi = vq->vdev->priv; +- +- /* Suppress further interrupts. */ +- virtqueue_disable_cb(vq); +- +- /* We were probably waiting for more output buffers. */ +- netif_wake_subqueue(vi->dev, vq2txq(vq)); +-} +- +-static unsigned int mergeable_ctx_to_buf_truesize(unsigned long mrg_ctx) ++unsigned int mergeable_ctx_to_buf_truesize(unsigned long mrg_ctx) + { + unsigned int truesize = mrg_ctx & (MERGEABLE_BUFFER_ALIGN - 1); + return (truesize + 1) * MERGEABLE_BUFFER_ALIGN; + } + +-static void *mergeable_ctx_to_buf_address(unsigned long mrg_ctx) ++void *mergeable_ctx_to_buf_address(unsigned long mrg_ctx) + { + return (void *)(mrg_ctx & -MERGEABLE_BUFFER_ALIGN); + + } + +-static unsigned long mergeable_buf_to_ctx(void *buf, unsigned int truesize) ++unsigned long mergeable_buf_to_ctx(void *buf, unsigned int truesize) + { + unsigned int size = truesize / MERGEABLE_BUFFER_ALIGN; + return (unsigned long)buf | (size - 1); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ ++static void skb_xmit_done(struct virtqueue *vq) ++{ ++ struct virtnet_info *vi = vq->vdev->priv; ++ ++ /* Suppress further interrupts. */ ++ virtqueue_disable_cb(vq); ++ ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(vi->dev, vq2txq(vq))) ++ return; ++#endif /* DEV_NETMAP */ ++ ++ /* We were probably waiting for more output buffers. */ ++ netif_wake_subqueue(vi->dev, vq2txq(vq)); ++} ++ + /* Called from bottom half context */ + static struct sk_buff *page_to_skb(struct virtnet_info *vi, + struct receive_queue *rq, +@@ -263,7 +304,13 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi, + p = page_address(page) + offset; + + /* copy small packet so we can reuse these pages for small data */ ++#ifdef NETMAP_LINUX_HAVE_NAPI_ALLOC_SKB + skb = napi_alloc_skb(&rq->napi, GOOD_COPY_LEN); ++#elif defined(NETMAP_LINUX_HAVE_ALLOC_SKB_IP_ALIGN) ++ skb = netdev_alloc_skb_ip_align(vi->dev, GOOD_COPY_LEN); ++#else ++ skb = netdev_alloc_csb(vi->dev, GOOD_COPY_LEN); ++#endif + if (unlikely(!skb)) + return NULL; + +@@ -360,6 +407,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, + unsigned long ctx, + unsigned int len) + { ++#ifdef WITH_MERGEABLE_RX_BUFS + void *buf = mergeable_ctx_to_buf_address(ctx); + struct virtio_net_hdr_mrg_rxbuf *hdr = buf; + u16 num_buf = virtio16_to_cpu(vi->vdev, hdr->num_buffers); +@@ -439,6 +487,7 @@ err_skb: + err_buf: + dev->stats.rx_dropped++; + dev_kfree_skb(head_skb); ++#endif /* WITH_MERGEABLE_RX_BUFS */ + return NULL; + } + +@@ -521,7 +570,11 @@ static int add_recvbuf_small(struct virtnet_info *vi, struct receive_queue *rq, + hdr = skb_vnet_hdr(skb); + sg_init_table(rq->sg, 2); + sg_set_buf(rq->sg, hdr, vi->hdr_len); +- skb_to_sgvec(skb, rq->sg + 1, 0, skb->len); ++ err = skb_to_sgvec(skb, rq->sg + 1, 0, skb->len); ++ if (err < 0) { ++ dev_kfree_skb(skb); ++ return err; ++ } + + err = virtqueue_add_inbuf(rq->vq, rq->sg, 2, skb, gfp); + if (err < 0) +@@ -579,7 +632,7 @@ static int add_recvbuf_big(struct virtnet_info *vi, struct receive_queue *rq, + return err; + } + +-static unsigned int get_mergeable_buf_len(struct ewma_pkt_len *avg_pkt_len) ++unsigned int get_mergeable_buf_len(struct ewma_pkt_len *avg_pkt_len) + { + const size_t hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf); + unsigned int len; +@@ -591,6 +644,7 @@ static unsigned int get_mergeable_buf_len(struct ewma_pkt_len *avg_pkt_len) + + static int add_recvbuf_mergeable(struct receive_queue *rq, gfp_t gfp) + { ++#ifdef WITH_MERGEABLE_RX_BUFS + struct page_frag *alloc_frag = &rq->alloc_frag; + char *buf; + unsigned long ctx; +@@ -622,6 +676,9 @@ static int add_recvbuf_mergeable(struct receive_queue *rq, gfp_t gfp) + put_page(virt_to_head_page(buf)); + + return err; ++#else /* !WITH_MERGEABLE_RX_BUFS */ ++ return -1; ++#endif /* !WITH_MERGEABLE_RX_BUFS */ + } + + /* +@@ -637,7 +694,6 @@ static bool try_fill_recv(struct virtnet_info *vi, struct receive_queue *rq, + int err; + bool oom; + +- gfp |= __GFP_COLD; + do { + if (vi->mergeable_rx_bufs) + err = add_recvbuf_mergeable(rq, gfp); +@@ -729,16 +785,32 @@ static int virtnet_poll(struct napi_struct *napi, int budget) + struct receive_queue *rq = + container_of(napi, struct receive_queue, napi); + unsigned int r, received; +- ++ struct virtqueue *vq = rq->vq; ++#ifdef DEV_NETMAP ++ int work_done = 0; ++ struct virtnet_info *vi = vq->vdev->priv; ++ int nm_irq = netmap_rx_irq(vi->dev, vq2rxq(vq), &work_done); ++ ++ if (nm_irq == NM_IRQ_COMPLETED) { ++ napi_complete(napi); ++ return 1; ++ } ++ if (nm_irq == NM_IRQ_RESCHED) ++ return budget; ++#endif /* DEV_NETMAP */ + received = virtnet_receive(rq, budget); + + /* Out of packets? */ + if (received < budget) { +- r = virtqueue_enable_cb_prepare(rq->vq); ++ r = virtqueue_enable_cb_prepare(vq); ++#ifdef NETMAP_LINUX_HAVE_NAPI_COMPLETE_DONE + napi_complete_done(napi, received); +- if (unlikely(virtqueue_poll(rq->vq, r)) && ++#else /* !NETMAP_LINUX_HAVE_NAPI_COMPLETE_DONE */ ++ napi_complete(napi); ++#endif /* !NETMAP_LINUX_HAVE_NAPI_COMPLETE_DONE */ ++ if (unlikely(virtqueue_poll(vq, r)) && + napi_schedule_prep(napi)) { +- virtqueue_disable_cb(rq->vq); ++ virtqueue_disable_cb(vq); + __napi_schedule(napi); + } + } +@@ -746,53 +818,19 @@ static int virtnet_poll(struct napi_struct *napi, int budget) + return received; + } + +-#ifdef CONFIG_NET_RX_BUSY_POLL +-/* must be called with local_bh_disable()d */ +-static int virtnet_busy_poll(struct napi_struct *napi) +-{ +- struct receive_queue *rq = +- container_of(napi, struct receive_queue, napi); +- struct virtnet_info *vi = rq->vq->vdev->priv; +- int r, received = 0, budget = 4; +- +- if (!(vi->status & VIRTIO_NET_S_LINK_UP)) +- return LL_FLUSH_FAILED; +- +- if (!napi_schedule_prep(napi)) +- return LL_FLUSH_BUSY; +- +- virtqueue_disable_cb(rq->vq); +- +-again: +- received += virtnet_receive(rq, budget); +- +- r = virtqueue_enable_cb_prepare(rq->vq); +- clear_bit(NAPI_STATE_SCHED, &napi->state); +- if (unlikely(virtqueue_poll(rq->vq, r)) && +- napi_schedule_prep(napi)) { +- virtqueue_disable_cb(rq->vq); +- if (received < budget) { +- budget -= received; +- goto again; +- } else { +- __napi_schedule(napi); +- } +- } +- +- return received; +-} +-#endif /* CONFIG_NET_RX_BUSY_POLL */ +- + static int virtnet_open(struct net_device *dev) + { + struct virtnet_info *vi = netdev_priv(dev); + int i; + + for (i = 0; i < vi->max_queue_pairs; i++) { +- if (i < vi->curr_queue_pairs) +- /* Make sure we have some buffers: if oom use wq. */ +- if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) +- schedule_delayed_work(&vi->refill, 0); ++ if (i < vi->curr_queue_pairs) { ++ if (!virtio_net_netmap_init_buffers(vi, i)) { ++ /* Make sure we have some buffers: if oom use wq. */ ++ if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) ++ schedule_delayed_work(&vi->refill, 0); ++ } ++ } + virtnet_napi_enable(&vi->rq[i]); + } + +@@ -840,7 +878,14 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb) + hdr = skb_vnet_hdr(skb); + + if (virtio_net_hdr_from_skb(skb, &hdr->hdr, +- virtio_is_little_endian(vi->vdev))) ++ virtio_is_little_endian(vi->vdev) ++#if defined(NETMAP_LINUX_VIRTIO_NET_HDR_FROM_SKB_5ARGS) || defined(NETMAP_LINUX_VIRTIO_NET_HDR_FROM_SKB_4ARGS) ++ , false ++#endif ++#if defined(NETMAP_LINUX_VIRTIO_NET_HDR_FROM_SKB_5ARGS) ++ , 0 ++#endif ++)) + BUG(); + + if (vi->mergeable_rx_bufs) +@@ -866,7 +911,11 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) + struct send_queue *sq = &vi->sq[qnum]; + int err; + struct netdev_queue *txq = netdev_get_tx_queue(dev, qnum); ++#ifdef NETMAP_LINUX_HAVE_XMIT_MORE + bool kick = !skb->xmit_more; ++#else /* !NETMAP_LINUX_HAVE_XMIT_MORE */ ++ bool kick = true; ++#endif /* !NETMAP_LINUX_HAVE_XMIT_MORE */ + + /* Free up any pending old buffers before queueing new ones. */ + free_old_xmit_skbs(sq); +@@ -890,7 +939,11 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) + + /* Don't wait up for transmitted skbs to be freed. */ + skb_orphan(skb); ++#ifdef NETMAP_LINUX_HAVE_NF_RESET_CT ++ nf_reset_ct(skb); ++#else /* !NETMAP_LINUX_HAVE_NF_RESET_CT */ + nf_reset(skb); ++#endif /* !NETMAP_LINUX_HAVE_NF_RESET_CT */ + + /* If running out of space, stop queue to avoid getting packets that we + * are then unable to transmit. +@@ -951,8 +1004,7 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd, + BUG_ON(out_num + 1 > ARRAY_SIZE(sgs)); + virtqueue_add_sgs(vi->cvq, sgs, out_num, 1, vi, GFP_ATOMIC); + +- if (unlikely(!virtqueue_kick(vi->cvq))) +- return vi->ctrl_status == VIRTIO_NET_OK; ++ virtqueue_kick(vi->cvq); + + /* Spin for a response, the kick causes an ioport write, trapping + * into the hypervisor, so the request should be handled immediately. +@@ -1009,8 +1061,13 @@ out: + return ret; + } + +-static struct rtnl_link_stats64 *virtnet_stats(struct net_device *dev, +- struct rtnl_link_stats64 *tot) ++#ifdef NETMAP_LINUX_HAVE_GET_STATS64 ++#ifdef NETMAP_LINUX_HAVE_NONVOID_GET_STATS64 ++static struct rtnl_link_stats64 * ++#else /* !NETMAP_LINUX_HAVE_NONVOID_GET_STATS64 */ ++static void ++#endif /* !NETMAP_LINUX_HAVE_NONVOID_GET_STATS64 */ ++virtnet_stats(struct net_device *dev, struct rtnl_link_stats64 *tot) + { + struct virtnet_info *vi = netdev_priv(dev); + int cpu; +@@ -1043,9 +1100,11 @@ static struct rtnl_link_stats64 *virtnet_stats(struct net_device *dev, + tot->rx_dropped = dev->stats.rx_dropped; + tot->rx_length_errors = dev->stats.rx_length_errors; + tot->rx_frame_errors = dev->stats.rx_frame_errors; +- ++#ifdef NETMAP_LINUX_HAVE_NONVOID_GET_STATS64 + return tot; ++#endif /* NETMAP_LINUX_HAVE_NONVOID_GET_STATS64 */ + } ++#endif /* NETMAP_LINUX_HAVE_GET_STATS64 */ + + #ifdef CONFIG_NET_POLL_CONTROLLER + static void virtnet_netpoll(struct net_device *dev) +@@ -1207,95 +1266,6 @@ static int virtnet_vlan_rx_kill_vid(struct net_device *dev, + return 0; + } + +-static void virtnet_clean_affinity(struct virtnet_info *vi, long hcpu) +-{ +- int i; +- +- if (vi->affinity_hint_set) { +- for (i = 0; i < vi->max_queue_pairs; i++) { +- virtqueue_set_affinity(vi->rq[i].vq, -1); +- virtqueue_set_affinity(vi->sq[i].vq, -1); +- } +- +- vi->affinity_hint_set = false; +- } +-} +- +-static void virtnet_set_affinity(struct virtnet_info *vi) +-{ +- int i; +- int cpu; +- +- /* In multiqueue mode, when the number of cpu is equal to the number of +- * queue pairs, we let the queue pairs to be private to one cpu by +- * setting the affinity hint to eliminate the contention. +- */ +- if (vi->curr_queue_pairs == 1 || +- vi->max_queue_pairs != num_online_cpus()) { +- virtnet_clean_affinity(vi, -1); +- return; +- } +- +- i = 0; +- for_each_online_cpu(cpu) { +- virtqueue_set_affinity(vi->rq[i].vq, cpu); +- virtqueue_set_affinity(vi->sq[i].vq, cpu); +- netif_set_xps_queue(vi->dev, cpumask_of(cpu), i); +- i++; +- } +- +- vi->affinity_hint_set = true; +-} +- +-static int virtnet_cpu_online(unsigned int cpu, struct hlist_node *node) +-{ +- struct virtnet_info *vi = hlist_entry_safe(node, struct virtnet_info, +- node); +- virtnet_set_affinity(vi); +- return 0; +-} +- +-static int virtnet_cpu_dead(unsigned int cpu, struct hlist_node *node) +-{ +- struct virtnet_info *vi = hlist_entry_safe(node, struct virtnet_info, +- node_dead); +- virtnet_set_affinity(vi); +- return 0; +-} +- +-static int virtnet_cpu_down_prep(unsigned int cpu, struct hlist_node *node) +-{ +- struct virtnet_info *vi = hlist_entry_safe(node, struct virtnet_info, +- node); +- +- virtnet_clean_affinity(vi, cpu); +- return 0; +-} +- +-static enum cpuhp_state virtionet_online; +- +-static int virtnet_cpu_notif_add(struct virtnet_info *vi) +-{ +- int ret; +- +- ret = cpuhp_state_add_instance_nocalls(virtionet_online, &vi->node); +- if (ret) +- return ret; +- ret = cpuhp_state_add_instance_nocalls(CPUHP_VIRT_NET_DEAD, +- &vi->node_dead); +- if (!ret) +- return ret; +- cpuhp_state_remove_instance_nocalls(virtionet_online, &vi->node); +- return ret; +-} +- +-static void virtnet_cpu_notif_remove(struct virtnet_info *vi) +-{ +- cpuhp_state_remove_instance_nocalls(virtionet_online, &vi->node); +- cpuhp_state_remove_instance_nocalls(CPUHP_VIRT_NET_DEAD, +- &vi->node_dead); +-} +- + static void virtnet_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *ring) + { +@@ -1342,8 +1312,6 @@ static int virtnet_set_channels(struct net_device *dev, + if (!err) { + netif_set_real_num_tx_queues(dev, queue_pairs); + netif_set_real_num_rx_queues(dev, queue_pairs); +- +- virtnet_set_affinity(vi); + } + put_online_cpus(); + +@@ -1363,51 +1331,63 @@ static void virtnet_get_channels(struct net_device *dev, + channels->other_count = 0; + } + ++#ifdef NETMAP_LINUX_HAVE_GET_LINK_KSETTINGS + /* Check if the user is trying to change anything besides speed/duplex */ +-static bool virtnet_validate_ethtool_cmd(const struct ethtool_cmd *cmd) ++static bool ++virtnet_validate_ethtool_cmd(const struct ethtool_link_ksettings *cmd) + { +- struct ethtool_cmd diff1 = *cmd; +- struct ethtool_cmd diff2 = {}; ++ struct ethtool_link_ksettings diff1 = *cmd; ++ struct ethtool_link_ksettings diff2 = {}; + + /* cmd is always set so we need to clear it, validate the port type + * and also without autonegotiation we can ignore advertising + */ +- ethtool_cmd_speed_set(&diff1, 0); +- diff2.port = PORT_OTHER; +- diff1.advertising = 0; +- diff1.duplex = 0; +- diff1.cmd = 0; ++ diff1.base.speed = 0; ++ diff2.base.port = PORT_OTHER; ++ ethtool_link_ksettings_zero_link_mode(&diff1, advertising); ++ diff1.base.duplex = 0; ++ diff1.base.cmd = 0; ++ diff1.base.link_mode_masks_nwords = 0; + +- return !memcmp(&diff1, &diff2, sizeof(diff1)); ++ return !memcmp(&diff1.base, &diff2.base, sizeof(diff1.base)) && ++ bitmap_empty(diff1.link_modes.supported, ++ __ETHTOOL_LINK_MODE_MASK_NBITS) && ++ bitmap_empty(diff1.link_modes.advertising, ++ __ETHTOOL_LINK_MODE_MASK_NBITS) && ++ bitmap_empty(diff1.link_modes.lp_advertising, ++ __ETHTOOL_LINK_MODE_MASK_NBITS); + } + +-static int virtnet_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) ++static int virtnet_set_link_ksettings(struct net_device *dev, ++ const struct ethtool_link_ksettings *cmd) + { + struct virtnet_info *vi = netdev_priv(dev); + u32 speed; + +- speed = ethtool_cmd_speed(cmd); ++ speed = cmd->base.speed; + /* don't allow custom speed and duplex */ + if (!ethtool_validate_speed(speed) || +- !ethtool_validate_duplex(cmd->duplex) || ++ !ethtool_validate_duplex(cmd->base.duplex) || + !virtnet_validate_ethtool_cmd(cmd)) + return -EINVAL; + vi->speed = speed; +- vi->duplex = cmd->duplex; ++ vi->duplex = cmd->base.duplex; + + return 0; + } + +-static int virtnet_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) ++static int virtnet_get_link_ksettings(struct net_device *dev, ++ struct ethtool_link_ksettings *cmd) + { + struct virtnet_info *vi = netdev_priv(dev); + +- ethtool_cmd_speed_set(cmd, vi->speed); +- cmd->duplex = vi->duplex; +- cmd->port = PORT_OTHER; ++ cmd->base.speed = vi->speed; ++ cmd->base.duplex = vi->duplex; ++ cmd->base.port = PORT_OTHER; + + return 0; + } ++#endif /* NETMAP_LINUX_HAVE_GET_LINK_KSETTINGS */ + + static void virtnet_init_settings(struct net_device *dev) + { +@@ -1424,8 +1404,10 @@ static const struct ethtool_ops virtnet_ethtool_ops = { + .set_channels = virtnet_set_channels, + .get_channels = virtnet_get_channels, + .get_ts_info = ethtool_op_get_ts_info, +- .get_settings = virtnet_get_settings, +- .set_settings = virtnet_set_settings, ++#ifdef NETMAP_LINUX_HAVE_GET_LINK_KSETTINGS ++ .get_link_ksettings = virtnet_get_link_ksettings, ++ .set_link_ksettings = virtnet_set_link_ksettings, ++#endif /* NETMAP_LINUX_HAVE_GET_LINK_KSETTINGS */ + }; + + #define MIN_MTU 68 +@@ -1446,16 +1428,15 @@ static const struct net_device_ops virtnet_netdev = { + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = virtnet_set_mac_address, + .ndo_set_rx_mode = virtnet_set_rx_mode, +- .ndo_change_mtu = virtnet_change_mtu, ++ .NETMAP_LINUX_CHANGE_MTU = virtnet_change_mtu, ++#ifdef NETMAP_LINUX_HAVE_GET_STATS64 + .ndo_get_stats64 = virtnet_stats, ++#endif + .ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid, + #ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = virtnet_netpoll, + #endif +-#ifdef CONFIG_NET_RX_BUSY_POLL +- .ndo_busy_poll = virtnet_busy_poll, +-#endif + }; + + static void virtnet_config_changed_work(struct work_struct *work) +@@ -1502,7 +1483,9 @@ static void virtnet_free_queues(struct virtnet_info *vi) + int i; + + for (i = 0; i < vi->max_queue_pairs; i++) { ++#ifdef NETMAP_LINUX_HAVE_NAPI_HASH_DEL + napi_hash_del(&vi->rq[i].napi); ++#endif /* NETMAP_LINUX_HAVE_NAPI_HASH_DEL */ + netif_napi_del(&vi->rq[i].napi); + } + +@@ -1565,8 +1548,6 @@ static void virtnet_del_vqs(struct virtnet_info *vi) + { + struct virtio_device *vdev = vi->vdev; + +- virtnet_clean_affinity(vi, -1); +- + vdev->config->del_vqs(vdev); + + virtnet_free_queues(vi); +@@ -1615,7 +1596,15 @@ static int virtnet_find_vqs(struct virtnet_info *vi) + } + + ret = vi->vdev->config->find_vqs(vi->vdev, total_vqs, vqs, callbacks, +- names); ++ names ++#if defined(NETMAP_LINUX_HAVE_FIND_VQS_CTX_ARG) ++ , NULL ++#endif ++#if defined(NETMAP_LINUX_HAVE_FIND_VQS_CTX_ARG) || defined(NETMAP_LINUX_HAVE_FIND_VQS_IRQAFF_ARG) ++ , NULL ++#endif ++ ++ ); + if (ret) + goto err_find; + +@@ -1689,10 +1678,6 @@ static int init_vqs(struct virtnet_info *vi) + if (ret) + goto err_free; + +- get_online_cpus(); +- virtnet_set_affinity(vi); +- put_online_cpus(); +- + return 0; + + err_free: +@@ -1701,33 +1686,6 @@ err: + return ret; + } + +-#ifdef CONFIG_SYSFS +-static ssize_t mergeable_rx_buffer_size_show(struct netdev_rx_queue *queue, +- struct rx_queue_attribute *attribute, char *buf) +-{ +- struct virtnet_info *vi = netdev_priv(queue->dev); +- unsigned int queue_index = get_netdev_rx_queue_index(queue); +- struct ewma_pkt_len *avg; +- +- BUG_ON(queue_index >= vi->max_queue_pairs); +- avg = &vi->rq[queue_index].mrg_avg_pkt_len; +- return sprintf(buf, "%u\n", get_mergeable_buf_len(avg)); +-} +- +-static struct rx_queue_attribute mergeable_rx_buffer_size_attribute = +- __ATTR_RO(mergeable_rx_buffer_size); +- +-static struct attribute *virtio_net_mrg_rx_attrs[] = { +- &mergeable_rx_buffer_size_attribute.attr, +- NULL +-}; +- +-static const struct attribute_group virtio_net_mrg_rx_group = { +- .name = "virtio_net", +- .attrs = virtio_net_mrg_rx_attrs +-}; +-#endif +- + static bool virtnet_fail_on_feature(struct virtio_device *vdev, + unsigned int fbit, + const char *fname, const char *dname) +@@ -1768,7 +1726,10 @@ static int virtnet_probe(struct virtio_device *vdev) + struct net_device *dev; + struct virtnet_info *vi; + u16 max_queue_pairs; ++#ifdef VIRTIO_NET_F_MTU + int mtu; ++#endif /* VIRTIO_NET_F_MTU */ ++ bool mrg_rxbuf = false; + + if (!vdev->config->get) { + dev_err(&vdev->dev, "%s failure: config access disabled\n", +@@ -1804,6 +1765,7 @@ static int virtnet_probe(struct virtio_device *vdev) + SET_NETDEV_DEV(dev, &vdev->dev); + + /* Do we support "hardware" checksums? */ ++#ifndef DEV_NETMAP + if (virtio_has_feature(vdev, VIRTIO_NET_F_CSUM)) { + /* This opens up the world of extra features. */ + dev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_SG; +@@ -1811,7 +1773,7 @@ static int virtnet_probe(struct virtio_device *vdev) + dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG; + + if (virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) { +- dev->hw_features |= NETIF_F_TSO | NETIF_F_UFO ++ dev->hw_features |= NETIF_F_TSO + | NETIF_F_TSO_ECN | NETIF_F_TSO6; + } + /* Individual feature bits: what can host handle? */ +@@ -1821,17 +1783,16 @@ static int virtnet_probe(struct virtio_device *vdev) + dev->hw_features |= NETIF_F_TSO6; + if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN)) + dev->hw_features |= NETIF_F_TSO_ECN; +- if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UFO)) +- dev->hw_features |= NETIF_F_UFO; + + dev->features |= NETIF_F_GSO_ROBUST; + + if (gso) +- dev->features |= dev->hw_features & (NETIF_F_ALL_TSO|NETIF_F_UFO); ++ dev->features |= dev->hw_features & (NETIF_F_ALL_TSO); + /* (!csum && gso) case will be fixed by register_netdev() */ + } + if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_CSUM)) + dev->features |= NETIF_F_RXCSUM; ++#endif /* !DEV_NETMAP */ + + dev->vlan_features = dev->features; + +@@ -1863,16 +1824,21 @@ static int virtnet_probe(struct virtio_device *vdev) + INIT_WORK(&vi->config_work, virtnet_config_changed_work); + + /* If we can receive ANY GSO packets, we must allocate large ones. */ ++#ifndef DEV_NETMAP + if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) || + virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6) || + virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN) || + virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_UFO)) + vi->big_packets = true; ++#endif /* !DEV_NETMAP */ + +- if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) ++#ifdef WITH_MERGEABLE_RX_BUFS ++ mrg_rxbuf = virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF); ++#endif ++ if (mrg_rxbuf) + vi->mergeable_rx_bufs = true; + +- if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF) || ++ if (mrg_rxbuf || + virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) + vi->hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf); + else +@@ -1885,6 +1851,7 @@ static int virtnet_probe(struct virtio_device *vdev) + if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) + vi->has_cvq = true; + ++#ifdef VIRTIO_NET_F_MTU + if (virtio_has_feature(vdev, VIRTIO_NET_F_MTU)) { + mtu = virtio_cread16(vdev, + offsetof(struct virtio_net_config, +@@ -1892,6 +1859,7 @@ static int virtnet_probe(struct virtio_device *vdev) + if (virtnet_change_mtu(dev, mtu)) + __virtio_clear_bit(vdev, VIRTIO_NET_F_MTU); + } ++#endif /* VIRTIO_NET_F_MTU */ + + if (vi->any_header_sg) + dev->needed_headroom = vi->hdr_len; +@@ -1905,10 +1873,6 @@ static int virtnet_probe(struct virtio_device *vdev) + if (err) + goto free_stats; + +-#ifdef CONFIG_SYSFS +- if (vi->mergeable_rx_bufs) +- dev->sysfs_rx_queue_group = &virtio_net_mrg_rx_group; +-#endif + netif_set_real_num_tx_queues(dev, vi->curr_queue_pairs); + netif_set_real_num_rx_queues(dev, vi->curr_queue_pairs); + +@@ -1920,13 +1884,11 @@ static int virtnet_probe(struct virtio_device *vdev) + goto free_vqs; + } + +- virtio_device_ready(vdev); ++#ifdef DEV_NETMAP ++ virtio_net_netmap_attach(vi); ++#endif /* DEV_NETMAP */ + +- err = virtnet_cpu_notif_add(vi); +- if (err) { +- pr_debug("virtio_net: registering cpu notifier failed\n"); +- goto free_unregister_netdev; +- } ++ virtio_device_ready(vdev); + + /* Assume link up if device can't report link status, + otherwise get link status from config. */ +@@ -1943,10 +1905,6 @@ static int virtnet_probe(struct virtio_device *vdev) + + return 0; + +-free_unregister_netdev: +- vi->vdev->config->reset(vdev); +- +- unregister_netdev(dev); + free_vqs: + cancel_delayed_work_sync(&vi->refill); + free_receive_page_frags(vi); +@@ -1976,11 +1934,13 @@ static void virtnet_remove(struct virtio_device *vdev) + { + struct virtnet_info *vi = vdev->priv; + +- virtnet_cpu_notif_remove(vi); +- + /* Make sure no work handler is accessing the device. */ + flush_work(&vi->config_work); + ++#ifdef DEV_NETMAP ++ netmap_detach(vi->dev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(vi->dev); + + remove_vq_common(vi); +@@ -1995,8 +1955,6 @@ static int virtnet_freeze(struct virtio_device *vdev) + struct virtnet_info *vi = vdev->priv; + int i; + +- virtnet_cpu_notif_remove(vi); +- + /* Make sure no work handler is accessing the device */ + flush_work(&vi->config_work); + +@@ -2039,10 +1997,6 @@ static int virtnet_restore(struct virtio_device *vdev) + virtnet_set_queues(vi, vi->curr_queue_pairs); + rtnl_unlock(); + +- err = virtnet_cpu_notif_add(vi); +- if (err) +- return err; +- + return 0; + } + #endif +@@ -2052,33 +2006,60 @@ static struct virtio_device_id id_table[] = { + { 0 }, + }; + +-#define VIRTNET_FEATURES \ ++#ifdef DEV_NETMAP ++/* Netmap cannot handle checksum offloads, and rx csum offload cannot be ++ * disabled with virtio-net. For this reason we do not negotiate any ++ * checksum offload, nor other features derived from those. With this ++ * trick, host rings work properly with TCP and UDP traffic. */ ++#define CSUM_FEATURES ++#else /* !DEV_NETMAP */ ++#define CSUM_FEATURES \ + VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GUEST_CSUM, \ +- VIRTIO_NET_F_MAC, \ + VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6, \ + VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, \ +- VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_UFO, \ +- VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ, \ ++ VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_UFO, ++#endif /* !DEV_NETMAP */ ++ ++#define VIRTNET_FEATURES \ ++ CSUM_FEATURES \ ++ VIRTIO_NET_F_MAC, \ ++ VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ, \ + VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN, \ + VIRTIO_NET_F_GUEST_ANNOUNCE, VIRTIO_NET_F_MQ, \ +- VIRTIO_NET_F_CTRL_MAC_ADDR, \ +- VIRTIO_NET_F_MTU ++ VIRTIO_NET_F_CTRL_MAC_ADDR + +-static unsigned int features[] = { ++unsigned int features[] = { + VIRTNET_FEATURES, ++#ifdef VIRTIO_NET_F_MTU ++ VIRTIO_NET_F_MTU, ++#endif /* VIRTIO_NET_F_MTU */ ++#ifdef WITH_MERGEABLE_RX_BUFS ++ VIRTIO_NET_F_MRG_RXBUF, ++#endif /* WITH_MERGEABLE_RX_BUFS */ + }; + + static unsigned int features_legacy[] = { + VIRTNET_FEATURES, ++#ifdef VIRTIO_NET_F_MTU ++ VIRTIO_NET_F_MTU, ++#endif /* VIRTIO_NET_F_MTU */ ++#ifdef WITH_MERGEABLE_RX_BUFS ++ VIRTIO_NET_F_MRG_RXBUF, ++#endif /* WITH_MERGEABLE_RX_BUFS */ + VIRTIO_NET_F_GSO, + VIRTIO_F_ANY_LAYOUT, + }; + + static struct virtio_driver virtio_net_driver = { ++#ifdef NETMAP_LINUX_HAVE_VIRTIO_DRIVER_FEATURE_TABLE_LEGACY + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .feature_table_legacy = features_legacy, + .feature_table_size_legacy = ARRAY_SIZE(features_legacy), ++#else /* !NETMAP_LINUX_HAVE_VIRTIO_DRIVER_FEATURE_TABLE_LEGACY */ ++ .feature_table = features_legacy, ++ .feature_table_size = ARRAY_SIZE(features_legacy), ++#endif /* !NETMAP_LINUX_HAVE_VIRTIO_DRIVER_FEATURE_TABLE_LEGACY */ + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, +@@ -2091,41 +2072,7 @@ static struct virtio_driver virtio_net_driver = { + #endif + }; + +-static __init int virtio_net_driver_init(void) +-{ +- int ret; +- +- ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "AP_VIRT_NET_ONLINE", +- virtnet_cpu_online, +- virtnet_cpu_down_prep); +- if (ret < 0) +- goto out; +- virtionet_online = ret; +- ret = cpuhp_setup_state_multi(CPUHP_VIRT_NET_DEAD, "VIRT_NET_DEAD", +- NULL, virtnet_cpu_dead); +- if (ret) +- goto err_dead; +- +- ret = register_virtio_driver(&virtio_net_driver); +- if (ret) +- goto err_virtio; +- return 0; +-err_virtio: +- cpuhp_remove_multi_state(CPUHP_VIRT_NET_DEAD); +-err_dead: +- cpuhp_remove_multi_state(virtionet_online); +-out: +- return ret; +-} +-module_init(virtio_net_driver_init); +- +-static __exit void virtio_net_driver_exit(void) +-{ +- cpuhp_remove_multi_state(CPUHP_VIRT_NET_DEAD); +- cpuhp_remove_multi_state(virtionet_online); +- unregister_virtio_driver(&virtio_net_driver); +-} +-module_exit(virtio_net_driver_exit); ++module_virtio_driver(virtio_net_driver); + + MODULE_DEVICE_TABLE(virtio, id_table); + MODULE_DESCRIPTION("Virtio network driver"); +diff --git a/virtio_net.c/virtio_net_src.c b/virtio_net.c/virtio_net_src.c +new file mode 120000 +index 0000000..e2a5c2a +--- /dev/null ++++ b/virtio_net.c/virtio_net_src.c +@@ -0,0 +1 @@ ++virtio_net.c +\ No newline at end of file diff --git a/LINUX/final-patches/intel--e1000e--3.3.5.10 b/LINUX/final-patches/intel--e1000e--3.3.5.10 new file mode 100644 index 000000000..ea1d58a7b --- /dev/null +++ b/LINUX/final-patches/intel--e1000e--3.3.5.10 @@ -0,0 +1,110 @@ +diff --git a/e1000e/Makefile b/e1000e/Makefile +index c4558ce..b951433 100644 +--- a/e1000e/Makefile ++++ b/e1000e/Makefile +@@ -36,7 +36,7 @@ ifeq (,$(BUILD_KERNEL)) + BUILD_KERNEL=$(shell uname -r) + endif + +-DRIVER_NAME = e1000e ++DRIVER_NAME = e1000e$(NETMAP_DRIVER_SUFFIX) + + ########################################################################### + # Environment tests +@@ -139,7 +139,7 @@ ifeq ($(ARCH),ppc64) + endif + + # extra flags for module builds +-EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') ++EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z-]' '[A-Z_]') + EXTRA_CFLAGS += -DDRIVER_NAME=$(DRIVER_NAME) + EXTRA_CFLAGS += -DDRIVER_NAME_CAPS=$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') + # standard flags for module builds +@@ -345,6 +345,9 @@ DEPVER := $(shell /sbin/depmod -V 2>/dev/null | \ + $(MANFILE).gz: ../$(MANFILE) + gzip -c $< > $@ + ++../$(MANFILE): ++ touch $@ ++ + install: default $(MANFILE).gz + # remove all old versions of the driver + find $(INSTALL_MOD_PATH)/lib/modules/$(KVER) -name $(TARGET) -exec rm -f {} \; || true +diff --git a/e1000e/netdev.c b/e1000e/netdev.c +index 6018f28..816a77c 100644 +--- a/e1000e/netdev.c ++++ b/e1000e/netdev.c +@@ -499,6 +499,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) + return ring->count + ring->next_to_clean - ring->next_to_use - 1; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + #ifdef HAVE_HW_TIME_STAMP + /** + * e1000e_systim_to_hwtstamp - convert system time value to hw time stamp +@@ -1022,6 +1026,17 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring) + bool cleaned = false; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + ++#ifdef DEV_NETMAP ++#ifdef CONFIG_E1000E_NAPI ++#define NETMAP_DUMMY work_done ++#else ++ int dummy; ++#define NETMAP_DUMMY &dummy ++#endif ++ if (netmap_rx_irq(netdev, 0, NETMAP_DUMMY)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); +@@ -1333,6 +1348,11 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) + unsigned int total_tx_bytes = 0, total_tx_packets = 0; + unsigned int bytes_compl = 0, pkts_compl = 0; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(netdev, 0)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + i = tx_ring->next_to_clean; + eop = tx_ring->buffer_info[i].next_to_watch; + eop_desc = E1000_TX_DESC(*tx_ring, eop); +@@ -4225,6 +4245,10 @@ static void e1000_configure(struct e1000_adapter *adapter) + #endif + e1000_setup_rctl(adapter); + e1000_configure_rx(adapter); ++#ifdef DEV_NETMAP ++ if (e1000e_netmap_init_buffers(adapter)) ++ return; ++#endif /* DEV_NETMAP */ + adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); + } + +@@ -8408,6 +8432,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_register; + ++#ifdef DEV_NETMAP ++ e1000_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + +@@ -8509,6 +8537,10 @@ static void e1000_remove(struct pci_dev *pdev) + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + iounmap(adapter->hw.hw_addr); + if ((adapter->hw.flash_address) && + (adapter->hw.mac.type < e1000_pch_spt)) diff --git a/LINUX/final-patches/intel--e1000e--3.3.6 b/LINUX/final-patches/intel--e1000e--3.3.6 new file mode 100644 index 000000000..beced2309 --- /dev/null +++ b/LINUX/final-patches/intel--e1000e--3.3.6 @@ -0,0 +1,110 @@ +diff --git a/e1000e/Makefile b/e1000e/Makefile +index c4558ce..b951433 100644 +--- a/e1000e/Makefile ++++ b/e1000e/Makefile +@@ -36,7 +36,7 @@ ifeq (,$(BUILD_KERNEL)) + BUILD_KERNEL=$(shell uname -r) + endif + +-DRIVER_NAME = e1000e ++DRIVER_NAME = e1000e$(NETMAP_DRIVER_SUFFIX) + + ########################################################################### + # Environment tests +@@ -139,7 +139,7 @@ ifeq ($(ARCH),ppc64) + endif + + # extra flags for module builds +-EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') ++EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z-]' '[A-Z_]') + EXTRA_CFLAGS += -DDRIVER_NAME=$(DRIVER_NAME) + EXTRA_CFLAGS += -DDRIVER_NAME_CAPS=$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') + # standard flags for module builds +@@ -345,6 +345,9 @@ DEPVER := $(shell /sbin/depmod -V 2>/dev/null | \ + $(MANFILE).gz: ../$(MANFILE) + gzip -c $< > $@ + ++../$(MANFILE): ++ touch $@ ++ + install: default $(MANFILE).gz + # remove all old versions of the driver + find $(INSTALL_MOD_PATH)/lib/modules/$(KVER) -name $(TARGET) -exec rm -f {} \; || true +diff --git a/e1000e/netdev.c b/e1000e/netdev.c +index 815b777..bb06ea4 100644 +--- a/e1000e/netdev.c ++++ b/e1000e/netdev.c +@@ -499,6 +499,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) + return ring->count + ring->next_to_clean - ring->next_to_use - 1; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + #ifdef HAVE_HW_TIME_STAMP + /** + * e1000e_systim_to_hwtstamp - convert system time value to hw time stamp +@@ -1022,6 +1026,17 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring) + bool cleaned = false; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + ++#ifdef DEV_NETMAP ++#ifdef CONFIG_E1000E_NAPI ++#define NETMAP_DUMMY work_done ++#else ++ int dummy; ++#define NETMAP_DUMMY &dummy ++#endif ++ if (netmap_rx_irq(netdev, 0, NETMAP_DUMMY)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); +@@ -1339,6 +1354,11 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) + unsigned int total_tx_bytes = 0, total_tx_packets = 0; + unsigned int bytes_compl = 0, pkts_compl = 0; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(netdev, 0)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + i = tx_ring->next_to_clean; + eop = tx_ring->buffer_info[i].next_to_watch; + eop_desc = E1000_TX_DESC(*tx_ring, eop); +@@ -4237,6 +4257,10 @@ static void e1000_configure(struct e1000_adapter *adapter) + #endif + e1000_setup_rctl(adapter); + e1000_configure_rx(adapter); ++#ifdef DEV_NETMAP ++ if (e1000e_netmap_init_buffers(adapter)) ++ return; ++#endif /* DEV_NETMAP */ + adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); + } + +@@ -8433,6 +8457,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_register; + ++#ifdef DEV_NETMAP ++ e1000_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + +@@ -8534,6 +8562,10 @@ static void e1000_remove(struct pci_dev *pdev) + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + iounmap(adapter->hw.hw_addr); + if ((adapter->hw.flash_address) && + (adapter->hw.mac.type < e1000_pch_spt)) diff --git a/LINUX/final-patches/intel--e1000e--3.4.0.2 b/LINUX/final-patches/intel--e1000e--3.4.0.2 new file mode 100644 index 000000000..3650d58a8 --- /dev/null +++ b/LINUX/final-patches/intel--e1000e--3.4.0.2 @@ -0,0 +1,110 @@ +diff --git a/e1000e/Makefile b/e1000e/Makefile +index c4558ce..b951433 100644 +--- a/e1000e/Makefile ++++ b/e1000e/Makefile +@@ -36,7 +36,7 @@ ifeq (,$(BUILD_KERNEL)) + BUILD_KERNEL=$(shell uname -r) + endif + +-DRIVER_NAME = e1000e ++DRIVER_NAME = e1000e$(NETMAP_DRIVER_SUFFIX) + + ########################################################################### + # Environment tests +@@ -139,7 +139,7 @@ ifeq ($(ARCH),ppc64) + endif + + # extra flags for module builds +-EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') ++EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z-]' '[A-Z_]') + EXTRA_CFLAGS += -DDRIVER_NAME=$(DRIVER_NAME) + EXTRA_CFLAGS += -DDRIVER_NAME_CAPS=$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') + # standard flags for module builds +@@ -345,6 +345,9 @@ DEPVER := $(shell /sbin/depmod -V 2>/dev/null | \ + $(MANFILE).gz: ../$(MANFILE) + gzip -c $< > $@ + ++../$(MANFILE): ++ touch $@ ++ + install: default $(MANFILE).gz + # remove all old versions of the driver + find $(INSTALL_MOD_PATH)/lib/modules/$(KVER) -name $(TARGET) -exec rm -f {} \; || true +diff --git a/e1000e/netdev.c b/e1000e/netdev.c +index bfc6624..56a7d56 100644 +--- a/e1000e/netdev.c ++++ b/e1000e/netdev.c +@@ -500,6 +500,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) + return ring->count + ring->next_to_clean - ring->next_to_use - 1; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + #ifdef HAVE_HW_TIME_STAMP + /** + * e1000e_systim_to_hwtstamp - convert system time value to hw time stamp +@@ -1023,6 +1027,17 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring) + bool cleaned = false; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + ++#ifdef DEV_NETMAP ++#ifdef CONFIG_E1000E_NAPI ++#define NETMAP_DUMMY work_done ++#else ++ int dummy; ++#define NETMAP_DUMMY &dummy ++#endif ++ if (netmap_rx_irq(netdev, 0, NETMAP_DUMMY)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); +@@ -1340,6 +1355,11 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) + unsigned int total_tx_bytes = 0, total_tx_packets = 0; + unsigned int bytes_compl = 0, pkts_compl = 0; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(netdev, 0)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + i = tx_ring->next_to_clean; + eop = tx_ring->buffer_info[i].next_to_watch; + eop_desc = E1000_TX_DESC(*tx_ring, eop); +@@ -4253,6 +4273,10 @@ static void e1000_configure(struct e1000_adapter *adapter) + #endif + e1000_setup_rctl(adapter); + e1000_configure_rx(adapter); ++#ifdef DEV_NETMAP ++ if (e1000e_netmap_init_buffers(adapter)) ++ return; ++#endif /* DEV_NETMAP */ + adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); + } + +@@ -8449,6 +8473,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_register; + ++#ifdef DEV_NETMAP ++ e1000_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + +@@ -8550,6 +8578,10 @@ static void e1000_remove(struct pci_dev *pdev) + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + iounmap(adapter->hw.hw_addr); + if ((adapter->hw.flash_address) && + (adapter->hw.mac.type < e1000_pch_spt)) diff --git a/LINUX/final-patches/intel--e1000e--3.4.1.1 b/LINUX/final-patches/intel--e1000e--3.4.1.1 new file mode 100644 index 000000000..e99a7e090 --- /dev/null +++ b/LINUX/final-patches/intel--e1000e--3.4.1.1 @@ -0,0 +1,110 @@ +diff --git a/e1000e/Makefile b/e1000e/Makefile +index 9bfa024..2c106f0 100644 +--- a/e1000e/Makefile ++++ b/e1000e/Makefile +@@ -36,7 +36,7 @@ ifeq (,$(BUILD_KERNEL)) + BUILD_KERNEL=$(shell uname -r) + endif + +-DRIVER_NAME = e1000e ++DRIVER_NAME = e1000e$(NETMAP_DRIVER_SUFFIX) + + ########################################################################### + # Environment tests +@@ -139,7 +139,7 @@ ifeq ($(ARCH),ppc64) + endif + + # extra flags for module builds +-EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') ++EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z-]' '[A-Z_]') + EXTRA_CFLAGS += -DDRIVER_NAME=$(DRIVER_NAME) + EXTRA_CFLAGS += -DDRIVER_NAME_CAPS=$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') + # standard flags for module builds +@@ -345,6 +345,9 @@ DEPVER := $(shell /sbin/depmod -V 2>/dev/null | \ + $(MANFILE).gz: ../$(MANFILE) + gzip -c $< > $@ + ++../$(MANFILE): ++ touch $@ ++ + install: default $(MANFILE).gz + # remove all old versions of the driver + find $(INSTALL_MOD_PATH)/lib/modules/$(KVER) -name $(TARGET) -exec rm -f {} \; || true +diff --git a/e1000e/netdev.c b/e1000e/netdev.c +index 09c8487..fd3c723 100644 +--- a/e1000e/netdev.c ++++ b/e1000e/netdev.c +@@ -500,6 +500,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) + return ring->count + ring->next_to_clean - ring->next_to_use - 1; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + #ifdef HAVE_HW_TIME_STAMP + /** + * e1000e_systim_to_hwtstamp - convert system time value to hw time stamp +@@ -1023,6 +1027,17 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring) + bool cleaned = false; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + ++#ifdef DEV_NETMAP ++#ifdef CONFIG_E1000E_NAPI ++#define NETMAP_DUMMY work_done ++#else ++ int dummy; ++#define NETMAP_DUMMY &dummy ++#endif ++ if (netmap_rx_irq(netdev, 0, NETMAP_DUMMY)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); +@@ -1340,6 +1355,11 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) + unsigned int total_tx_bytes = 0, total_tx_packets = 0; + unsigned int bytes_compl = 0, pkts_compl = 0; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(netdev, 0)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + i = tx_ring->next_to_clean; + eop = tx_ring->buffer_info[i].next_to_watch; + eop_desc = E1000_TX_DESC(*tx_ring, eop); +@@ -4256,6 +4276,10 @@ static void e1000_configure(struct e1000_adapter *adapter) + #endif + e1000_setup_rctl(adapter); + e1000_configure_rx(adapter); ++#ifdef DEV_NETMAP ++ if (e1000e_netmap_init_buffers(adapter)) ++ return; ++#endif /* DEV_NETMAP */ + adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); + } + +@@ -8456,6 +8480,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_register; + ++#ifdef DEV_NETMAP ++ e1000_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + +@@ -8557,6 +8585,10 @@ static void e1000_remove(struct pci_dev *pdev) + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + iounmap(adapter->hw.hw_addr); + if ((adapter->hw.flash_address) && + (adapter->hw.mac.type < e1000_pch_spt)) diff --git a/LINUX/final-patches/intel--e1000e--3.4.2.1 b/LINUX/final-patches/intel--e1000e--3.4.2.1 new file mode 100644 index 000000000..36098a3e3 --- /dev/null +++ b/LINUX/final-patches/intel--e1000e--3.4.2.1 @@ -0,0 +1,110 @@ +diff --git a/e1000e/Makefile b/e1000e/Makefile +index 6b73b2c..dfc2900 100644 +--- a/e1000e/Makefile ++++ b/e1000e/Makefile +@@ -15,7 +15,7 @@ ifeq (,$(BUILD_KERNEL)) + BUILD_KERNEL=$(shell uname -r) + endif + +-DRIVER_NAME = e1000e ++DRIVER_NAME = e1000e$(NETMAP_DRIVER_SUFFIX) + + ########################################################################### + # Environment tests +@@ -118,7 +118,7 @@ ifeq ($(ARCH),ppc64) + endif + + # extra flags for module builds +-EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') ++EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z-]' '[A-Z_]') + EXTRA_CFLAGS += -DDRIVER_NAME=$(DRIVER_NAME) + EXTRA_CFLAGS += -DDRIVER_NAME_CAPS=$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') + # standard flags for module builds +@@ -324,6 +324,9 @@ DEPVER := $(shell /sbin/depmod -V 2>/dev/null | \ + $(MANFILE).gz: ../$(MANFILE) + gzip -c $< > $@ + ++../$(MANFILE): ++ touch $@ ++ + install: default $(MANFILE).gz + # remove all old versions of the driver + find $(INSTALL_MOD_PATH)/lib/modules/$(KVER) -name $(TARGET) -exec rm -f {} \; || true +diff --git a/e1000e/netdev.c b/e1000e/netdev.c +index fc51f96..d84222f 100644 +--- a/e1000e/netdev.c ++++ b/e1000e/netdev.c +@@ -482,6 +482,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) + return ring->count + ring->next_to_clean - ring->next_to_use - 1; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + #ifdef HAVE_HW_TIME_STAMP + /** + * e1000e_systim_to_hwtstamp - convert system time value to hw time stamp +@@ -1005,6 +1009,17 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring) + bool cleaned = false; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + ++#ifdef DEV_NETMAP ++#ifdef CONFIG_E1000E_NAPI ++#define NETMAP_DUMMY work_done ++#else ++ int dummy; ++#define NETMAP_DUMMY &dummy ++#endif ++ if (netmap_rx_irq(netdev, 0, NETMAP_DUMMY)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); +@@ -1322,6 +1337,11 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) + unsigned int total_tx_bytes = 0, total_tx_packets = 0; + unsigned int bytes_compl = 0, pkts_compl = 0; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(netdev, 0)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + i = tx_ring->next_to_clean; + eop = tx_ring->buffer_info[i].next_to_watch; + eop_desc = E1000_TX_DESC(*tx_ring, eop); +@@ -4238,6 +4258,10 @@ static void e1000_configure(struct e1000_adapter *adapter) + #endif + e1000_setup_rctl(adapter); + e1000_configure_rx(adapter); ++#ifdef DEV_NETMAP ++ if (e1000e_netmap_init_buffers(adapter)) ++ return; ++#endif /* DEV_NETMAP */ + adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); + } + +@@ -8438,6 +8462,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_register; + ++#ifdef DEV_NETMAP ++ e1000_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + +@@ -8539,6 +8567,10 @@ static void e1000_remove(struct pci_dev *pdev) + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + iounmap(adapter->hw.hw_addr); + if ((adapter->hw.flash_address) && + (adapter->hw.mac.type < e1000_pch_spt)) diff --git a/LINUX/final-patches/intel--e1000e--3.4.2.3 b/LINUX/final-patches/intel--e1000e--3.4.2.3 new file mode 100644 index 000000000..1bc4e7487 --- /dev/null +++ b/LINUX/final-patches/intel--e1000e--3.4.2.3 @@ -0,0 +1,110 @@ +diff --git a/e1000e/Makefile b/e1000e/Makefile +index d285219..4e011bd 100644 +--- a/e1000e/Makefile ++++ b/e1000e/Makefile +@@ -15,7 +15,7 @@ ifeq (,$(BUILD_KERNEL)) + BUILD_KERNEL=$(shell uname -r) + endif + +-DRIVER_NAME = e1000e ++DRIVER_NAME = e1000e$(NETMAP_DRIVER_SUFFIX) + + ########################################################################### + # Environment tests +@@ -118,7 +118,7 @@ ifeq ($(ARCH),ppc64) + endif + + # extra flags for module builds +-EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') ++EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z-]' '[A-Z_]') + EXTRA_CFLAGS += -DDRIVER_NAME=$(DRIVER_NAME) + EXTRA_CFLAGS += -DDRIVER_NAME_CAPS=$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') + # standard flags for module builds +@@ -324,6 +324,9 @@ DEPVER := $(shell /sbin/depmod -V 2>/dev/null | \ + $(MANFILE).gz: ../$(MANFILE) + gzip -c $< > $@ + ++../$(MANFILE): ++ touch $@ ++ + install: default $(MANFILE).gz + # remove all old versions of the driver + find $(INSTALL_MOD_PATH)/lib/modules/$(KVER) -name $(TARGET) -exec rm -f {} \; || true +diff --git a/e1000e/netdev.c b/e1000e/netdev.c +index 740b1f6..c3bf9da 100644 +--- a/e1000e/netdev.c ++++ b/e1000e/netdev.c +@@ -483,6 +483,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) + return ring->count + ring->next_to_clean - ring->next_to_use - 1; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + #ifdef HAVE_HW_TIME_STAMP + /** + * e1000e_systim_to_hwtstamp - convert system time value to hw time stamp +@@ -1013,6 +1017,17 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring) + bool cleaned = false; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + ++#ifdef DEV_NETMAP ++#ifdef CONFIG_E1000E_NAPI ++#define NETMAP_DUMMY work_done ++#else ++ int dummy; ++#define NETMAP_DUMMY &dummy ++#endif ++ if (netmap_rx_irq(netdev, 0, NETMAP_DUMMY)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); +@@ -1330,6 +1345,11 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) + unsigned int total_tx_bytes = 0, total_tx_packets = 0; + unsigned int bytes_compl = 0, pkts_compl = 0; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(netdev, 0)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + i = tx_ring->next_to_clean; + eop = tx_ring->buffer_info[i].next_to_watch; + eop_desc = E1000_TX_DESC(*tx_ring, eop); +@@ -4262,6 +4282,10 @@ static void e1000_configure(struct e1000_adapter *adapter) + #endif + e1000_setup_rctl(adapter); + e1000_configure_rx(adapter); ++#ifdef DEV_NETMAP ++ if (e1000e_netmap_init_buffers(adapter)) ++ return; ++#endif /* DEV_NETMAP */ + adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); + } + +@@ -8467,6 +8491,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_register; + ++#ifdef DEV_NETMAP ++ e1000_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + +@@ -8568,6 +8596,10 @@ static void e1000_remove(struct pci_dev *pdev) + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + iounmap(adapter->hw.hw_addr); + if ((adapter->hw.flash_address) && + (adapter->hw.mac.type < e1000_pch_spt)) diff --git a/LINUX/final-patches/intel--e1000e--3.4.2.4 b/LINUX/final-patches/intel--e1000e--3.4.2.4 new file mode 100644 index 000000000..de3343d94 --- /dev/null +++ b/LINUX/final-patches/intel--e1000e--3.4.2.4 @@ -0,0 +1,110 @@ +diff --git a/e1000e/Makefile b/e1000e/Makefile +index d285219..4e011bd 100644 +--- a/e1000e/Makefile ++++ b/e1000e/Makefile +@@ -15,7 +15,7 @@ ifeq (,$(BUILD_KERNEL)) + BUILD_KERNEL=$(shell uname -r) + endif + +-DRIVER_NAME = e1000e ++DRIVER_NAME = e1000e$(NETMAP_DRIVER_SUFFIX) + + ########################################################################### + # Environment tests +@@ -118,7 +118,7 @@ ifeq ($(ARCH),ppc64) + endif + + # extra flags for module builds +-EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') ++EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z-]' '[A-Z_]') + EXTRA_CFLAGS += -DDRIVER_NAME=$(DRIVER_NAME) + EXTRA_CFLAGS += -DDRIVER_NAME_CAPS=$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') + # standard flags for module builds +@@ -324,6 +324,9 @@ DEPVER := $(shell /sbin/depmod -V 2>/dev/null | \ + $(MANFILE).gz: ../$(MANFILE) + gzip -c $< > $@ + ++../$(MANFILE): ++ touch $@ ++ + install: default $(MANFILE).gz + # remove all old versions of the driver + find $(INSTALL_MOD_PATH)/lib/modules/$(KVER) -name $(TARGET) -exec rm -f {} \; || true +diff --git a/e1000e/netdev.c b/e1000e/netdev.c +index 7f1abff..7a86734 100644 +--- a/e1000e/netdev.c ++++ b/e1000e/netdev.c +@@ -483,6 +483,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) + return ring->count + ring->next_to_clean - ring->next_to_use - 1; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + #ifdef HAVE_HW_TIME_STAMP + /** + * e1000e_systim_to_hwtstamp - convert system time value to hw time stamp +@@ -1013,6 +1017,17 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring) + bool cleaned = false; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + ++#ifdef DEV_NETMAP ++#ifdef CONFIG_E1000E_NAPI ++#define NETMAP_DUMMY work_done ++#else ++ int dummy; ++#define NETMAP_DUMMY &dummy ++#endif ++ if (netmap_rx_irq(netdev, 0, NETMAP_DUMMY)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); +@@ -1330,6 +1345,11 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) + unsigned int total_tx_bytes = 0, total_tx_packets = 0; + unsigned int bytes_compl = 0, pkts_compl = 0; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(netdev, 0)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + i = tx_ring->next_to_clean; + eop = tx_ring->buffer_info[i].next_to_watch; + eop_desc = E1000_TX_DESC(*tx_ring, eop); +@@ -4262,6 +4282,10 @@ static void e1000_configure(struct e1000_adapter *adapter) + #endif + e1000_setup_rctl(adapter); + e1000_configure_rx(adapter); ++#ifdef DEV_NETMAP ++ if (e1000e_netmap_init_buffers(adapter)) ++ return; ++#endif /* DEV_NETMAP */ + adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); + } + +@@ -8467,6 +8491,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_register; + ++#ifdef DEV_NETMAP ++ e1000_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + +@@ -8568,6 +8596,10 @@ static void e1000_remove(struct pci_dev *pdev) + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + iounmap(adapter->hw.hw_addr); + if ((adapter->hw.flash_address) && + (adapter->hw.mac.type < e1000_pch_spt)) diff --git a/LINUX/final-patches/intel--e1000e--3.5.1 b/LINUX/final-patches/intel--e1000e--3.5.1 new file mode 100644 index 000000000..967c5b550 --- /dev/null +++ b/LINUX/final-patches/intel--e1000e--3.5.1 @@ -0,0 +1,110 @@ +diff --git a/e1000e/Makefile b/e1000e/Makefile +index 7e6d8c8..639da1a 100644 +--- a/e1000e/Makefile ++++ b/e1000e/Makefile +@@ -15,7 +15,7 @@ ifeq (,$(BUILD_KERNEL)) + BUILD_KERNEL=$(shell uname -r) + endif + +-DRIVER_NAME = e1000e ++DRIVER_NAME = e1000e$(NETMAP_DRIVER_SUFFIX) + + ########################################################################### + # Environment tests +@@ -118,7 +118,7 @@ ifeq ($(ARCH),ppc64) + endif + + # extra flags for module builds +-EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') ++EXTRA_CFLAGS += -DDRIVER_$(shell echo $(DRIVER_NAME) | tr '[a-z-]' '[A-Z_]') + EXTRA_CFLAGS += -DDRIVER_NAME=$(DRIVER_NAME) + EXTRA_CFLAGS += -DDRIVER_NAME_CAPS=$(shell echo $(DRIVER_NAME) | tr '[a-z]' '[A-Z]') + # standard flags for module builds +@@ -324,6 +324,9 @@ DEPVER := $(shell /sbin/depmod -V 2>/dev/null | \ + $(MANFILE).gz: ../$(MANFILE) + gzip -c $< > $@ + ++../$(MANFILE): ++ touch $@ ++ + install: default $(MANFILE).gz + # remove all old versions of the driver + find $(INSTALL_MOD_PATH)/lib/modules/$(KVER) -name $(TARGET) -exec rm -f {} \; || true +diff --git a/e1000e/netdev.c b/e1000e/netdev.c +index 7402857..ec84246 100644 +--- a/e1000e/netdev.c ++++ b/e1000e/netdev.c +@@ -483,6 +483,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) + return ring->count + ring->next_to_clean - ring->next_to_use - 1; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + #ifdef HAVE_HW_TIME_STAMP + /** + * e1000e_systim_to_hwtstamp - convert system time value to hw time stamp +@@ -1013,6 +1017,17 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring) + bool cleaned = false; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + ++#ifdef DEV_NETMAP ++#ifdef CONFIG_E1000E_NAPI ++#define NETMAP_DUMMY work_done ++#else ++ int dummy; ++#define NETMAP_DUMMY &dummy ++#endif ++ if (netmap_rx_irq(netdev, 0, NETMAP_DUMMY)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); +@@ -1330,6 +1345,11 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) + unsigned int total_tx_bytes = 0, total_tx_packets = 0; + unsigned int bytes_compl = 0, pkts_compl = 0; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(netdev, 0)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + i = tx_ring->next_to_clean; + eop = tx_ring->buffer_info[i].next_to_watch; + eop_desc = E1000_TX_DESC(*tx_ring, eop); +@@ -4262,6 +4282,10 @@ static void e1000_configure(struct e1000_adapter *adapter) + #endif + e1000_setup_rctl(adapter); + e1000_configure_rx(adapter); ++#ifdef DEV_NETMAP ++ if (e1000e_netmap_init_buffers(adapter)) ++ return; ++#endif /* DEV_NETMAP */ + adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); + } + +@@ -8473,6 +8497,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_register; + ++#ifdef DEV_NETMAP ++ e1000_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + +@@ -8574,6 +8602,10 @@ static void e1000_remove(struct pci_dev *pdev) + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + iounmap(adapter->hw.hw_addr); + if ((adapter->hw.flash_address) && + (adapter->hw.mac.type < e1000_pch_spt)) diff --git a/LINUX/final-patches/intel--e1000e--3.6.0 b/LINUX/final-patches/intel--e1000e--3.6.0 new file mode 100644 index 000000000..c9fd21300 --- /dev/null +++ b/LINUX/final-patches/intel--e1000e--3.6.0 @@ -0,0 +1,137 @@ +diff --git a/e1000e/Makefile b/e1000e/Makefile +index f300712..206f69f 100644 +--- a/e1000e/Makefile ++++ b/e1000e/Makefile +@@ -9,9 +9,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the @SUMMARY@ + # + +-obj-$(CONFIG_E1000E) += e1000e.o ++obj-$(CONFIG_E1000E) += e1000e$(NETMAP_DRIVER_SUFFIX).o + +-define e1000e-y ++define e1000e$(NETMAP_DRIVER_SUFFIX)-y + netdev.o + ethtool.o + ich8lan.o +@@ -23,20 +23,20 @@ define e1000e-y + 82571.o + param.o + endef +-e1000e-y := $(strip ${e1000e-y}) ++e1000e$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${e1000e$(NETMAP_DRIVER_SUFFIX)-y}) + + #ifdef BUILD_PTP_SUPPORT +-e1000e-$(CONFIG_PTP_1588_CLOCK:m=y) += ptp.o ++e1000e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ptp.o + #endif + + #ifndef REMOVE_COMPAT + +-e1000e-y += kcompat.o ++e1000e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := e1000e ++DRIVER := e1000e$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -94,7 +94,7 @@ ccc: clean + + # Build manfiles + manfile: +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ #@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + + # Clean the module subdirectories + clean: +@@ -104,7 +104,7 @@ clean: + # Install the modules and manpage + install: default manfile + @echo "Copying manpages..." +- @install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ #@install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + @echo "Installing modules..." + @+$(call devkernelbuild,modules_install) + @echo "Running depmod..." +diff --git a/e1000e/netdev.c b/e1000e/netdev.c +index 081ca43..12f992c 100644 +--- a/e1000e/netdev.c ++++ b/e1000e/netdev.c +@@ -483,6 +483,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) + return ring->count + ring->next_to_clean - ring->next_to_use - 1; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + #ifdef HAVE_HW_TIME_STAMP + /** + * e1000e_systim_to_hwtstamp - convert system time value to hw time stamp +@@ -1013,6 +1017,17 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring) + bool cleaned = false; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + ++#ifdef DEV_NETMAP ++#ifdef CONFIG_E1000E_NAPI ++#define NETMAP_DUMMY work_done ++#else ++ int dummy; ++#define NETMAP_DUMMY &dummy ++#endif ++ if (netmap_rx_irq(netdev, 0, NETMAP_DUMMY)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); +@@ -1330,6 +1345,11 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) + unsigned int total_tx_bytes = 0, total_tx_packets = 0; + unsigned int bytes_compl = 0, pkts_compl = 0; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(netdev, 0)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + i = tx_ring->next_to_clean; + eop = tx_ring->buffer_info[i].next_to_watch; + eop_desc = E1000_TX_DESC(*tx_ring, eop); +@@ -4262,6 +4282,10 @@ static void e1000_configure(struct e1000_adapter *adapter) + #endif + e1000_setup_rctl(adapter); + e1000_configure_rx(adapter); ++#ifdef DEV_NETMAP ++ if (e1000e_netmap_init_buffers(adapter)) ++ return; ++#endif /* DEV_NETMAP */ + adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); + } + +@@ -8674,6 +8698,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_register; + ++#ifdef DEV_NETMAP ++ e1000_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + +@@ -8775,6 +8803,10 @@ static void e1000_remove(struct pci_dev *pdev) + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + iounmap(adapter->hw.hw_addr); + if ((adapter->hw.flash_address) && + (adapter->hw.mac.type < e1000_pch_spt)) diff --git a/LINUX/final-patches/intel--e1000e--3.8.4 b/LINUX/final-patches/intel--e1000e--3.8.4 new file mode 100644 index 000000000..a64f23bae --- /dev/null +++ b/LINUX/final-patches/intel--e1000e--3.8.4 @@ -0,0 +1,137 @@ +diff --git a/e1000e/Makefile b/e1000e/Makefile +index 9af58b1..5a62b40 100644 +--- a/e1000e/Makefile ++++ b/e1000e/Makefile +@@ -9,9 +9,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the @SUMMARY@ + # + +-obj-$(CONFIG_E1000E) += e1000e.o ++obj-$(CONFIG_E1000E) += e1000e$(NETMAP_DRIVER_SUFFIX).o + +-define e1000e-y ++define e1000e$(NETMAP_DRIVER_SUFFIX)-y + netdev.o + ethtool.o + ich8lan.o +@@ -23,20 +23,20 @@ define e1000e-y + 82571.o + param.o + endef +-e1000e-y := $(strip ${e1000e-y}) ++e1000e$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${e1000e$(NETMAP_DRIVER_SUFFIX)-y}) + + #ifdef BUILD_PTP_SUPPORT +-e1000e-$(CONFIG_PTP_1588_CLOCK:m=y) += ptp.o ++e1000e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ptp.o + #endif + + #ifndef REMOVE_COMPAT + +-e1000e-y += kcompat.o ++e1000e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := e1000e ++DRIVER := e1000e$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -94,7 +94,7 @@ ccc: clean + + # Build manfiles + manfile: +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ #@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + + # Clean the module subdirectories + clean: +@@ -104,7 +104,7 @@ clean: + # Install the modules and manpage + install: default manfile + @echo "Copying manpages..." +- @install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ #@install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + @echo "Installing modules..." + @+$(call devkernelbuild,modules_install) + @echo "Running depmod..." +diff --git a/e1000e/netdev.c b/e1000e/netdev.c +index bfd9dc8..b7e91da 100644 +--- a/e1000e/netdev.c ++++ b/e1000e/netdev.c +@@ -483,6 +483,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) + return ring->count + ring->next_to_clean - ring->next_to_use - 1; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + #ifdef HAVE_HW_TIME_STAMP + /** + * e1000e_systim_to_hwtstamp - convert system time value to hw time stamp +@@ -1013,6 +1017,17 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring) + bool cleaned = false; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + ++#ifdef DEV_NETMAP ++#ifdef CONFIG_E1000E_NAPI ++#define NETMAP_DUMMY work_done ++#else ++ int dummy; ++#define NETMAP_DUMMY &dummy ++#endif ++ if (netmap_rx_irq(netdev, 0, NETMAP_DUMMY)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); +@@ -1330,6 +1345,11 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) + unsigned int total_tx_bytes = 0, total_tx_packets = 0; + unsigned int bytes_compl = 0, pkts_compl = 0; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(netdev, 0)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + i = tx_ring->next_to_clean; + eop = tx_ring->buffer_info[i].next_to_watch; + eop_desc = E1000_TX_DESC(*tx_ring, eop); +@@ -4263,6 +4283,10 @@ static void e1000_configure(struct e1000_adapter *adapter) + #endif + e1000_setup_rctl(adapter); + e1000_configure_rx(adapter); ++#ifdef DEV_NETMAP ++ if (e1000e_netmap_init_buffers(adapter)) ++ return; ++#endif /* DEV_NETMAP */ + adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); + } + +@@ -8735,6 +8759,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_register; + ++#ifdef DEV_NETMAP ++ e1000_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + +@@ -8831,6 +8859,10 @@ static void e1000_remove(struct pci_dev *pdev) + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + iounmap(adapter->hw.hw_addr); + if ((adapter->hw.flash_address) && + (adapter->hw.mac.type < e1000_pch_spt)) diff --git a/LINUX/final-patches/intel--e1000e--3.8.7 b/LINUX/final-patches/intel--e1000e--3.8.7 new file mode 100644 index 000000000..536a41ca7 --- /dev/null +++ b/LINUX/final-patches/intel--e1000e--3.8.7 @@ -0,0 +1,137 @@ +diff --git a/e1000e/Makefile b/e1000e/Makefile +index 9af58b1..5a62b40 100644 +--- a/e1000e/Makefile ++++ b/e1000e/Makefile +@@ -9,9 +9,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the @SUMMARY@ + # + +-obj-$(CONFIG_E1000E) += e1000e.o ++obj-$(CONFIG_E1000E) += e1000e$(NETMAP_DRIVER_SUFFIX).o + +-define e1000e-y ++define e1000e$(NETMAP_DRIVER_SUFFIX)-y + netdev.o + ethtool.o + ich8lan.o +@@ -23,20 +23,20 @@ define e1000e-y + 82571.o + param.o + endef +-e1000e-y := $(strip ${e1000e-y}) ++e1000e$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${e1000e$(NETMAP_DRIVER_SUFFIX)-y}) + + #ifdef BUILD_PTP_SUPPORT +-e1000e-$(CONFIG_PTP_1588_CLOCK:m=y) += ptp.o ++e1000e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ptp.o + #endif + + #ifndef REMOVE_COMPAT + +-e1000e-y += kcompat.o ++e1000e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := e1000e ++DRIVER := e1000e$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -94,7 +94,7 @@ ccc: clean + + # Build manfiles + manfile: +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ #@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + + # Clean the module subdirectories + clean: +@@ -104,7 +104,7 @@ clean: + # Install the modules and manpage + install: default manfile + @echo "Copying manpages..." +- @install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ #@install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + @echo "Installing modules..." + @+$(call devkernelbuild,modules_install) + @echo "Running depmod..." +diff --git a/e1000e/netdev.c b/e1000e/netdev.c +index a8eb9b7..de06d62 100644 +--- a/e1000e/netdev.c ++++ b/e1000e/netdev.c +@@ -483,6 +483,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) + return ring->count + ring->next_to_clean - ring->next_to_use - 1; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + #ifdef HAVE_HW_TIME_STAMP + /** + * e1000e_systim_to_hwtstamp - convert system time value to hw time stamp +@@ -1013,6 +1017,17 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring) + bool cleaned = false; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + ++#ifdef DEV_NETMAP ++#ifdef CONFIG_E1000E_NAPI ++#define NETMAP_DUMMY work_done ++#else ++ int dummy; ++#define NETMAP_DUMMY &dummy ++#endif ++ if (netmap_rx_irq(netdev, 0, NETMAP_DUMMY)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); +@@ -1330,6 +1345,11 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) + unsigned int total_tx_bytes = 0, total_tx_packets = 0; + unsigned int bytes_compl = 0, pkts_compl = 0; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(netdev, 0)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + i = tx_ring->next_to_clean; + eop = tx_ring->buffer_info[i].next_to_watch; + eop_desc = E1000_TX_DESC(*tx_ring, eop); +@@ -4263,6 +4283,10 @@ static void e1000_configure(struct e1000_adapter *adapter) + #endif + e1000_setup_rctl(adapter); + e1000_configure_rx(adapter); ++#ifdef DEV_NETMAP ++ if (e1000e_netmap_init_buffers(adapter)) ++ return; ++#endif /* DEV_NETMAP */ + adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); + } + +@@ -8755,6 +8779,10 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_register; + ++#ifdef DEV_NETMAP ++ e1000_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + +@@ -8851,6 +8879,10 @@ static void e1000_remove(struct pci_dev *pdev) + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + iounmap(adapter->hw.hw_addr); + if ((adapter->hw.flash_address) && + (adapter->hw.mac.type < e1000_pch_spt)) diff --git a/LINUX/final-patches/intel--e1000e--netmap-3.6.0 b/LINUX/final-patches/intel--e1000e--netmap-3.6.0 new file mode 100644 index 000000000..e69de29bb diff --git a/LINUX/final-patches/intel--i40e--1.5.25 b/LINUX/final-patches/intel--i40e--1.5.25 index 8c0ee87c3..d63333abf 100644 --- a/LINUX/final-patches/intel--i40e--1.5.25 +++ b/LINUX/final-patches/intel--i40e--1.5.25 @@ -1,5 +1,5 @@ diff --git a/i40e/Makefile b/i40e/Makefile -index 8c1483c..104b8c9 100644 +index 8c1483c..509f19e 100644 --- a/i40e/Makefile +++ b/i40e/Makefile @@ -27,9 +27,9 @@ ifneq ($(KERNELRELEASE),) @@ -48,7 +48,7 @@ index 8c1483c..104b8c9 100644 clean: @+$(call kernelbuild,clean) diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c -index 577cc4a..d9ee330 100644 +index e729c22..b830e4c 100644 --- a/i40e/i40e_main.c +++ b/i40e/i40e_main.c @@ -132,6 +132,11 @@ MODULE_VERSION(DRV_VERSION); @@ -74,7 +74,18 @@ index 577cc4a..d9ee330 100644 return 0; } -@@ -3141,6 +3150,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) +@@ -3119,6 +3128,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3141,6 +3154,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); writel(0, ring->tail); @@ -86,7 +97,7 @@ index 577cc4a..d9ee330 100644 i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); return 0; -@@ -10100,6 +10114,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) +@@ -10101,6 +10119,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) return -ENODEV; } @@ -98,7 +109,7 @@ index 577cc4a..d9ee330 100644 uplink_seid = vsi->uplink_seid; if (vsi->type != I40E_VSI_SRIOV) { if (vsi->netdev_registered) { -@@ -10461,6 +10480,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, +@@ -10462,6 +10485,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, (vsi->type == I40E_VSI_VMDQ2)) { ret = i40e_vsi_config_rss(vsi); } @@ -112,7 +123,7 @@ index 577cc4a..d9ee330 100644 err_rings: diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c -index e0f1f6c..3b131f2 100644 +index e0f1f6c..beb9d6f 100644 --- a/i40e/i40e_txrx.c +++ b/i40e/i40e_txrx.c @@ -25,6 +25,10 @@ @@ -131,22 +142,24 @@ index e0f1f6c..3b131f2 100644 unsigned int total_bytes = 0; +#ifdef DEV_NETMAP -+ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) + return true; +#endif /* DEV_NETMAP */ + tx_buf = &tx_ring->tx_bi[i]; tx_desc = I40E_TX_DESC(tx_ring, i); i -= tx_ring->count; -@@ -1892,6 +1901,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) +@@ -1892,6 +1901,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); bool failure = false; +#ifdef DEV_NETMAP -+ int dummy, nm_irq; -+ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); -+ if (nm_irq != NM_IRQ_PASS) { -+ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } + } +#endif /* DEV_NETMAP */ + diff --git a/LINUX/final-patches/intel--i40e--1.6.42 b/LINUX/final-patches/intel--i40e--1.6.42 index 095c6cc6b..20aa9131f 100644 --- a/LINUX/final-patches/intel--i40e--1.6.42 +++ b/LINUX/final-patches/intel--i40e--1.6.42 @@ -48,7 +48,7 @@ index a190706..314e79e 100644 clean: @+$(call kernelbuild,clean) diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c -index 4dd9457..f4783e5 100644 +index 4dd9457..a2949b4 100644 --- a/i40e/i40e_main.c +++ b/i40e/i40e_main.c @@ -134,6 +134,11 @@ MODULE_VERSION(DRV_VERSION); @@ -74,7 +74,18 @@ index 4dd9457..f4783e5 100644 return 0; } -@@ -3286,6 +3295,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) +@@ -3264,6 +3273,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3286,6 +3299,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); writel(0, ring->tail); @@ -86,7 +97,7 @@ index 4dd9457..f4783e5 100644 i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); return 0; -@@ -10417,6 +10431,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) +@@ -10417,6 +10435,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) return -ENODEV; } @@ -98,7 +109,7 @@ index 4dd9457..f4783e5 100644 uplink_seid = vsi->uplink_seid; if (vsi->type != I40E_VSI_SRIOV) { if (vsi->netdev_registered) { -@@ -10788,6 +10807,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, +@@ -10788,6 +10811,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, (vsi->type == I40E_VSI_VMDQ2)) { ret = i40e_vsi_config_rss(vsi); } @@ -112,7 +123,7 @@ index 4dd9457..f4783e5 100644 err_rings: diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c -index cd37999..1faa8e6 100644 +index cd37999..6a7f635 100644 --- a/i40e/i40e_txrx.c +++ b/i40e/i40e_txrx.c @@ -25,6 +25,10 @@ @@ -131,22 +142,24 @@ index cd37999..1faa8e6 100644 unsigned int budget = vsi->work_limit; +#ifdef DEV_NETMAP -+ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) + return true; +#endif /* DEV_NETMAP */ + tx_buf = &tx_ring->tx_bi[i]; tx_desc = I40E_TX_DESC(tx_ring, i); i -= tx_ring->count; -@@ -1874,6 +1883,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) +@@ -1874,6 +1883,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); bool failure = false; +#ifdef DEV_NETMAP -+ int dummy, nm_irq; -+ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); -+ if (nm_irq != NM_IRQ_PASS) { -+ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } + } +#endif /* DEV_NETMAP */ + diff --git a/LINUX/final-patches/intel--i40e--2.0.19 b/LINUX/final-patches/intel--i40e--2.0.19 index dbd8d03b4..a5a3121ab 100644 --- a/LINUX/final-patches/intel--i40e--2.0.19 +++ b/LINUX/final-patches/intel--i40e--2.0.19 @@ -48,7 +48,7 @@ index 1af83c9..e896ffe 100644 clean: @+$(call kernelbuild,clean) diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c -index 7a2d4d7..659cf4a 100644 +index 7a2d4d7..0b79e20 100644 --- a/i40e/i40e_main.c +++ b/i40e/i40e_main.c @@ -133,6 +133,11 @@ MODULE_VERSION(DRV_VERSION); @@ -74,7 +74,18 @@ index 7a2d4d7..659cf4a 100644 return 0; } -@@ -3353,6 +3362,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) +@@ -3331,6 +3340,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3353,6 +3366,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); writel(0, ring->tail); @@ -86,7 +97,7 @@ index 7a2d4d7..659cf4a 100644 i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); return 0; -@@ -10570,6 +10584,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) +@@ -10570,6 +10588,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) return -ENODEV; } @@ -98,7 +109,7 @@ index 7a2d4d7..659cf4a 100644 uplink_seid = vsi->uplink_seid; if (vsi->type != I40E_VSI_SRIOV) { if (vsi->netdev_registered) { -@@ -10941,6 +10960,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, +@@ -10941,6 +10964,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, (vsi->type == I40E_VSI_VMDQ2)) { ret = i40e_vsi_config_rss(vsi); } @@ -112,7 +123,7 @@ index 7a2d4d7..659cf4a 100644 err_rings: diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c -index 6c8aa0c..bd90202 100644 +index 6c8aa0c..f6eb037 100644 --- a/i40e/i40e_txrx.c +++ b/i40e/i40e_txrx.c @@ -25,6 +25,10 @@ @@ -131,22 +142,24 @@ index 6c8aa0c..bd90202 100644 unsigned int budget = vsi->work_limit; +#ifdef DEV_NETMAP -+ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) + return true; +#endif /* DEV_NETMAP */ + tx_buf = &tx_ring->tx_bi[i]; tx_desc = I40E_TX_DESC(tx_ring, i); i -= tx_ring->count; -@@ -1894,6 +1903,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) +@@ -1894,6 +1903,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); bool failure = false; +#ifdef DEV_NETMAP -+ int dummy, nm_irq; -+ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); -+ if (nm_irq != NM_IRQ_PASS) { -+ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } + } +#endif /* DEV_NETMAP */ + diff --git a/LINUX/final-patches/intel--i40e--2.0.26 b/LINUX/final-patches/intel--i40e--2.0.26 new file mode 100644 index 000000000..02e64ad9f --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.0.26 @@ -0,0 +1,169 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 1af83c9..e896ffe 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -27,9 +27,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 40-10 Gigabit Ethernet Connection Network Driver + # + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -43,14 +43,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-$(CONFIG_FCOE:m=y) += i40e_fcoe.o +-i40e-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_FCOE:m=y) += i40e_fcoe.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -90,9 +90,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 15e43a1..d02fc33 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -133,6 +133,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3281,6 +3286,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3331,6 +3340,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3353,6 +3366,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -10645,6 +10663,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -11016,6 +11039,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 6c8aa0c..f6eb037 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -25,6 +25,10 @@ + #include "i40e.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -695,6 +699,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1894,6 +1903,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); + bool failure = false; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + while (likely(total_rx_packets < (unsigned int)budget)) { + union i40e_rx_desc *rx_desc; + u16 vlan_tag; diff --git a/LINUX/final-patches/intel--i40e--2.0.30 b/LINUX/final-patches/intel--i40e--2.0.30 new file mode 100644 index 000000000..c3ac26053 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.0.30 @@ -0,0 +1,169 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 1af83c9..e896ffe 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -27,9 +27,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 40-10 Gigabit Ethernet Connection Network Driver + # + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -43,14 +43,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-$(CONFIG_FCOE:m=y) += i40e_fcoe.o +-i40e-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_FCOE:m=y) += i40e_fcoe.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -90,9 +90,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 142be1c..9a00bf1 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -133,6 +133,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3281,6 +3286,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3331,6 +3340,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3353,6 +3366,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -10655,6 +10673,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -11026,6 +11049,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 15b2ecf..2d06c40 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -25,6 +25,10 @@ + #include "i40e.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -695,6 +699,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1877,6 +1886,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); + bool failure = false; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + while (likely(total_rx_packets < (unsigned int)budget)) { + union i40e_rx_desc *rx_desc; + u16 vlan_tag; diff --git a/LINUX/final-patches/intel--i40e--2.1.26 b/LINUX/final-patches/intel--i40e--2.1.26 new file mode 100644 index 000000000..106dbc354 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.1.26 @@ -0,0 +1,167 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index f653b71..c356d02 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -30,9 +30,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -46,13 +46,13 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -92,9 +92,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 4c70594..8a7cace 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -141,6 +141,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3215,6 +3220,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3263,6 +3272,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3291,6 +3304,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -10844,6 +10862,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -11212,6 +11235,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index cbd49a9..e9d2a48 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -26,6 +26,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -729,6 +733,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2148,6 +2157,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); + bool failure = false; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + while (likely(total_rx_packets < (unsigned int)budget)) { + struct i40e_rx_buffer *rx_buffer; + union i40e_rx_desc *rx_desc; diff --git a/LINUX/final-patches/intel--i40e--2.10.19.30 b/LINUX/final-patches/intel--i40e--2.10.19.30 new file mode 100644 index 000000000..e592af958 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.10.19.30 @@ -0,0 +1,168 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 5b3ca74..79db7aa 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 4452767..93b47d2 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -136,6 +136,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3480,6 +3485,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3533,6 +3542,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3561,6 +3574,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -13732,6 +13750,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -14104,6 +14127,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index d90951f..13516d4 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -784,6 +788,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2555,6 +2564,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--i40e--2.10.19.82 b/LINUX/final-patches/intel--i40e--2.10.19.82 new file mode 100644 index 000000000..94dfbeb4e --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.10.19.82 @@ -0,0 +1,168 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 5b3ca74..79db7aa 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 3588ce8..b5def2e 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -136,6 +136,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3482,6 +3487,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3535,6 +3544,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3563,6 +3576,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -13773,6 +13791,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -14145,6 +14168,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index b6b1a78..31aefc5 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -784,6 +788,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2555,6 +2564,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--i40e--2.11.21 b/LINUX/final-patches/intel--i40e--2.11.21 new file mode 100644 index 000000000..89d776d11 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.11.21 @@ -0,0 +1,168 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 81f5ab9..85bfb8c 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index bd4b467..3e461eb 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -146,6 +146,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3539,6 +3544,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3592,6 +3601,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3620,6 +3633,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -13959,6 +13977,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -14331,6 +14354,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 96bc531..12b3953 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -784,6 +788,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2555,6 +2564,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--i40e--2.11.25 b/LINUX/final-patches/intel--i40e--2.11.25 new file mode 100644 index 000000000..fce69d7df --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.11.25 @@ -0,0 +1,168 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 81f5ab9..85bfb8c 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index b67de06..ce19f27 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -146,6 +146,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3539,6 +3544,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3592,6 +3601,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3620,6 +3633,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -13959,6 +13977,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -14331,6 +14354,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 96bc531..12b3953 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -784,6 +788,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2555,6 +2564,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--i40e--2.11.29 b/LINUX/final-patches/intel--i40e--2.11.29 new file mode 100644 index 000000000..65deaef37 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.11.29 @@ -0,0 +1,169 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 81f5ab9..85bfb8c 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 7ad12f4..2768a89 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -147,6 +147,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3555,6 +3560,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3608,6 +3617,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3636,6 +3649,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -14000,6 +14018,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -14372,6 +14396,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 96bc531..12b3953 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -784,6 +788,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2555,6 +2564,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--i40e--2.12.6 b/LINUX/final-patches/intel--i40e--2.12.6 new file mode 100644 index 000000000..f3507c22f --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.12.6 @@ -0,0 +1,169 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 81f5ab9..85bfb8c 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 6750c3c..f6e3fd1 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -148,6 +148,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3619,6 +3624,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3672,6 +3681,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3700,6 +3713,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -14167,6 +14185,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -14539,6 +14563,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index e3eb496..509d0e1 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -974,6 +978,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2745,6 +2754,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--i40e--2.13.10 b/LINUX/final-patches/intel--i40e--2.13.10 new file mode 100644 index 000000000..ab2876f1d --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.13.10 @@ -0,0 +1,169 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 81f5ab9..85bfb8c 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 62aeafe..5572ab1 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -148,6 +148,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3693,6 +3698,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3746,6 +3755,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3774,6 +3787,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -14676,6 +14694,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -15048,6 +15072,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index f5d5bdf..b127543 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -935,6 +939,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2709,6 +2718,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--i40e--2.14.13 b/LINUX/final-patches/intel--i40e--2.14.13 new file mode 100644 index 000000000..1031d4669 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.14.13 @@ -0,0 +1,170 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 81f5ab9..85bfb8c 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 9aea7ca..09297e6 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -150,6 +150,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3716,6 +3721,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3769,6 +3778,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3797,6 +3810,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -14865,6 +14883,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -15237,6 +15261,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index ea07464..6ea220b 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -935,6 +939,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2741,7 +2750,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.15.9 b/LINUX/final-patches/intel--i40e--2.15.9 new file mode 100644 index 000000000..c76e131c4 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.15.9 @@ -0,0 +1,167 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index e9a83b9..a5d7708 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 37e7bd9..27ff181 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -150,6 +150,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3733,6 +3738,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3786,6 +3795,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3814,6 +3827,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -14870,6 +14888,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -15242,6 +15266,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index ce23f6b..4a69af0 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -935,6 +939,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2741,6 +2750,14 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_FRAME_SZ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); diff --git a/LINUX/final-patches/intel--i40e--2.16.11 b/LINUX/final-patches/intel--i40e--2.16.11 new file mode 100644 index 000000000..183956c09 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.16.11 @@ -0,0 +1,170 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index e9a83b9..a5d7708 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 366c67e..389a449 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -150,6 +150,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3953,6 +3958,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4006,6 +4015,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4034,6 +4047,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -15162,6 +15180,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -15534,6 +15558,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 096f44f..3d3b7b3 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -935,6 +939,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2743,7 +2752,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.17.15 b/LINUX/final-patches/intel--i40e--2.17.15 new file mode 100644 index 000000000..a8b2658a4 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.17.15 @@ -0,0 +1,173 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 2f77091..d1bcc36 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,9 +28,9 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + ifeq (${NEED_AUX_BUS},2) + obj-m += auxiliary.o +@@ -40,7 +40,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -94,9 +94,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index c42883f..39fb831 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -152,6 +152,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3992,6 +3997,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4045,6 +4054,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4073,6 +4086,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -15231,6 +15249,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -15603,6 +15627,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index a039064..2c34cf8 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -10,6 +10,10 @@ + #include "i40e_prototype.h" + #include "i40e_txrx_common.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -936,6 +940,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2771,7 +2780,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.17.4 b/LINUX/final-patches/intel--i40e--2.17.4 new file mode 100644 index 000000000..4e3cd60cf --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.17.4 @@ -0,0 +1,170 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index e9a83b9..a5d7708 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 874644b..6440a6d 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -152,6 +152,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3989,6 +3994,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4042,6 +4051,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4070,6 +4083,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -15228,6 +15246,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -15600,6 +15624,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index a039064..2c34cf8 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -10,6 +10,10 @@ + #include "i40e_prototype.h" + #include "i40e_txrx_common.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -936,6 +940,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2771,7 +2780,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.18.9 b/LINUX/final-patches/intel--i40e--2.18.9 new file mode 100644 index 000000000..1b88f16c2 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.18.9 @@ -0,0 +1,173 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 62220eb..6e5b009 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_xsk.o \ + i40e_adminq.o \ +@@ -29,9 +29,9 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + ifeq (${NEED_AUX_BUS},2) + obj-m += auxiliary.o +@@ -41,7 +41,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -95,9 +95,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index f19b701..d723387 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -161,6 +161,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -4142,6 +4147,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4271,6 +4280,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4299,6 +4312,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + #ifdef HAVE_MEM_TYPE_XSK_BUFF_POOL + #ifdef HAVE_NETDEV_BPF_XSK_POOL +@@ -15595,6 +15613,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -15988,6 +16012,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index cbfc1a1..2d0646e 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -13,6 +13,10 @@ + #include "i40e_xsk.h" + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -977,6 +981,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2908,7 +2917,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.19.3 b/LINUX/final-patches/intel--i40e--2.19.3 new file mode 100644 index 000000000..6f79f49a3 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.19.3 @@ -0,0 +1,173 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 572217f..5059ff4 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_xsk.o \ + i40e_adminq.o \ +@@ -29,9 +29,9 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + ifeq (${NEED_AUX_BUS},2) + obj-m += auxiliary.o +@@ -41,7 +41,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -95,9 +95,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index b9dc44c..45d84f0 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -163,6 +163,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -4146,6 +4151,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4275,6 +4284,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4303,6 +4316,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + #ifdef HAVE_MEM_TYPE_XSK_BUFF_POOL + #ifdef HAVE_NETDEV_BPF_XSK_POOL +@@ -15612,6 +15630,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -16005,6 +16029,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 453e7ac..cb78e15 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -13,6 +13,10 @@ + #include "i40e_xsk.h" + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -983,6 +987,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2931,7 +2940,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.20.12 b/LINUX/final-patches/intel--i40e--2.20.12 new file mode 100644 index 000000000..87d13b430 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.20.12 @@ -0,0 +1,173 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 7c87855..51480db 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_xsk.o \ + i40e_adminq.o \ +@@ -29,9 +29,9 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + ifeq (${NEED_AUX_BUS},2) + obj-m += auxiliary.o +@@ -41,7 +41,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -95,9 +95,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 88050c6..000b0c8 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -170,6 +170,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -4153,6 +4158,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4282,6 +4291,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4310,6 +4323,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + #ifdef HAVE_MEM_TYPE_XSK_BUFF_POOL + #ifdef HAVE_NETDEV_BPF_XSK_POOL +@@ -15624,6 +15642,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -16017,6 +16041,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 47ef1a9..ab2bd0e 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -13,6 +13,10 @@ + #include "i40e_xsk.h" + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -983,6 +987,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2933,7 +2942,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.21.12 b/LINUX/final-patches/intel--i40e--2.21.12 new file mode 100644 index 000000000..778cd4c36 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.21.12 @@ -0,0 +1,173 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 35c640f..e780da7 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_xsk.o \ + i40e_adminq.o \ +@@ -29,9 +29,9 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + ifeq (${NEED_AUX_BUS},2) + intel_auxiliary-objs := auxiliary.o +@@ -42,7 +42,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -96,9 +96,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 64f3d84..f90c7bf 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -161,6 +161,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -4147,6 +4152,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4275,6 +4284,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4303,6 +4316,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + #ifdef HAVE_MEM_TYPE_XSK_BUFF_POOL + #ifdef HAVE_NETDEV_BPF_XSK_POOL +@@ -15663,6 +15681,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -16056,6 +16080,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 72471f0..2300e06 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -13,6 +13,10 @@ + #include "i40e_xsk.h" + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -981,6 +985,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2947,7 +2956,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.22.18 b/LINUX/final-patches/intel--i40e--2.22.18 new file mode 100644 index 000000000..fc1eec6b2 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.22.18 @@ -0,0 +1,173 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index ac4e0ca..aecc31e 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_xsk.o \ + i40e_adminq.o \ +@@ -29,9 +29,9 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + ifeq (${NEED_AUX_BUS},2) + intel_auxiliary-objs := auxiliary.o +@@ -42,7 +42,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -96,9 +96,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index cd83ec2..44a23a2 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -161,6 +161,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -4154,6 +4159,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4282,6 +4291,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4310,6 +4323,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + #ifdef HAVE_MEM_TYPE_XSK_BUFF_POOL + #ifdef HAVE_NETDEV_BPF_XSK_POOL +@@ -15711,6 +15729,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -16104,6 +16128,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 31f9819..7e6dabe 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -13,6 +13,10 @@ + #include "i40e_xsk.h" + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -981,6 +985,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2948,7 +2957,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.22.20 b/LINUX/final-patches/intel--i40e--2.22.20 new file mode 100644 index 000000000..7904b2e3f --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.22.20 @@ -0,0 +1,173 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index ac4e0ca..aecc31e 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_xsk.o \ + i40e_adminq.o \ +@@ -29,9 +29,9 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + ifeq (${NEED_AUX_BUS},2) + intel_auxiliary-objs := auxiliary.o +@@ -42,7 +42,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -96,9 +96,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 996a4c0..707fa4e 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -161,6 +161,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -4154,6 +4159,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4282,6 +4291,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4310,6 +4323,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + #ifdef HAVE_MEM_TYPE_XSK_BUFF_POOL + #ifdef HAVE_NETDEV_BPF_XSK_POOL +@@ -15720,6 +15738,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -16113,6 +16137,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 31f9819..7e6dabe 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -13,6 +13,10 @@ + #include "i40e_xsk.h" + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -981,6 +985,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2948,7 +2957,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.22.8 b/LINUX/final-patches/intel--i40e--2.22.8 new file mode 100644 index 000000000..295dc1584 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.22.8 @@ -0,0 +1,173 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 35c640f..e780da7 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_xsk.o \ + i40e_adminq.o \ +@@ -29,9 +29,9 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + ifeq (${NEED_AUX_BUS},2) + intel_auxiliary-objs := auxiliary.o +@@ -42,7 +42,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -96,9 +96,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index ed4271e..1d2f26b 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -161,6 +161,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -4154,6 +4159,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4282,6 +4291,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4310,6 +4323,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + #ifdef HAVE_MEM_TYPE_XSK_BUFF_POOL + #ifdef HAVE_NETDEV_BPF_XSK_POOL +@@ -15711,6 +15729,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -16104,6 +16128,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index d991b07..4bebbd8 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -13,6 +13,10 @@ + #include "i40e_xsk.h" + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -981,6 +985,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2948,7 +2957,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.23.17 b/LINUX/final-patches/intel--i40e--2.23.17 new file mode 100644 index 000000000..ca1671ed7 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.23.17 @@ -0,0 +1,173 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index aafff04..44e33f6 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_xsk.o \ + i40e_adminq.o \ +@@ -29,9 +29,9 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + ifeq (${NEED_AUX_BUS},2) + intel_auxiliary-objs := auxiliary.o +@@ -42,7 +42,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -96,9 +96,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index da5a6e8..3111d22 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -161,6 +161,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -4158,6 +4163,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4286,6 +4295,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4314,6 +4327,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + #ifdef HAVE_MEM_TYPE_XSK_BUFF_POOL + #ifdef HAVE_NETDEV_BPF_XSK_POOL +@@ -15838,6 +15856,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -16231,6 +16255,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index b3234d3..299402e 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -13,6 +13,10 @@ + #include "i40e_xsk.h" + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -986,6 +990,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2958,7 +2967,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.24.6 b/LINUX/final-patches/intel--i40e--2.24.6 new file mode 100644 index 000000000..dd33b7297 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.24.6 @@ -0,0 +1,173 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index aafff04..44e33f6 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_xsk.o \ + i40e_adminq.o \ +@@ -29,9 +29,9 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + ifeq (${NEED_AUX_BUS},2) + intel_auxiliary-objs := auxiliary.o +@@ -42,7 +42,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -96,9 +96,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 91965dc..350f142 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -162,6 +162,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -4160,6 +4165,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4288,6 +4297,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4316,6 +4329,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + #ifdef HAVE_MEM_TYPE_XSK_BUFF_POOL + #ifdef HAVE_NETDEV_BPF_XSK_POOL +@@ -15996,6 +16014,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -16389,6 +16413,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index da83e97..a011384 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -10,6 +10,10 @@ + #include "i40e_xsk.h" + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -983,6 +987,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2954,7 +2963,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.25.11 b/LINUX/final-patches/intel--i40e--2.25.11 new file mode 100644 index 000000000..bd95b7f07 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.25.11 @@ -0,0 +1,173 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 5f1e70d..ff4910f 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_xsk.o \ + i40e_adminq.o \ +@@ -29,9 +29,9 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + ifeq (${NEED_AUX_BUS},2) + intel_auxiliary-objs := auxiliary.o +@@ -42,7 +42,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -96,9 +96,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index ede3885..cc10297 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -162,6 +162,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -4171,6 +4176,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4299,6 +4308,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4327,6 +4340,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + #ifdef HAVE_MEM_TYPE_XSK_BUFF_POOL + #ifdef HAVE_NETDEV_BPF_XSK_POOL +@@ -16049,6 +16067,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -16442,6 +16466,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index e21ecf1..aed14cd 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -10,6 +10,10 @@ + #include "i40e_xsk.h" + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -983,6 +987,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2954,7 +2963,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.27.8 b/LINUX/final-patches/intel--i40e--2.27.8 new file mode 100644 index 000000000..4c12c530b --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.27.8 @@ -0,0 +1,173 @@ +diff --git a/i40e/Makefile b/src/Makefile +index 5f1e70d..ff4910f 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_xsk.o \ + i40e_adminq.o \ +@@ -29,9 +29,9 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + ifeq (${NEED_AUX_BUS},2) + intel_auxiliary-objs := auxiliary.o +@@ -42,7 +42,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -96,9 +96,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/src/i40e_main.c +index f15e9b9..86a17c0 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -162,6 +162,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_allocate_dma_mem - OS specific memory alloc for shared code + * @hw: pointer to the HW structure +@@ -4264,6 +4269,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4392,6 +4401,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4420,6 +4433,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + #ifdef HAVE_MEM_TYPE_XSK_BUFF_POOL + #ifdef HAVE_NETDEV_BPF_XSK_POOL +@@ -16136,6 +16154,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -16532,6 +16556,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/src/i40e_txrx.c +index 1b8e9bb..8d057a7 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -10,6 +10,10 @@ + #include "i40e_xsk.h" + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -986,6 +990,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2954,7 +2963,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.28.9 b/LINUX/final-patches/intel--i40e--2.28.9 new file mode 100644 index 000000000..4d8e4d04e --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.28.9 @@ -0,0 +1,170 @@ +diff --git a/i40e/Makefile b/src/Makefile +index fc5830b..1cee489 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,8 +10,8 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += i40e.o +-i40e-y := i40e_main.o \ ++obj-m += i40e$(NETMAP_DRIVER_SUFFIX).o ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_xsk.o \ + i40e_adminq.o \ +@@ -27,9 +27,9 @@ i40e-y := i40e_main.o \ + i40e_ddp.o \ + i40e_client.o \ + i40e_virtchnl_pf.o +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + ifeq (${NEED_AUX_BUS},2) + intel_auxiliary-objs := auxiliary.o + obj-m += intel_auxiliary.o +@@ -38,7 +38,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + # 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 +@@ -88,8 +88,10 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++../${DRIVER}.${MANSECTION}: ++ touch $@ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/src/i40e_main.c +index ed85099..8d7bc02 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -163,6 +163,11 @@ bool i40e_is_l4mode_enabled(void) + return l4mode > L4_MODE_DISABLED; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_allocate_dma_mem - OS specific memory alloc for shared code + * @hw: pointer to the HW structure +@@ -4280,6 +4285,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -4407,6 +4416,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -4435,6 +4448,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + #ifdef HAVE_MEM_TYPE_XSK_BUFF_POOL + #ifdef HAVE_NETDEV_BPF_XSK_POOL +@@ -16081,6 +16099,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + } + + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -16477,6 +16501,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/src/i40e_txrx.c +index 9434135..57425a5 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -10,6 +10,10 @@ + #include "i40e_xsk.h" + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -986,6 +990,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2952,7 +2961,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + u16 tpid; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ + #ifdef HAVE_XDP_BUFF_FRAME_SZ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/intel--i40e--2.3.6 b/LINUX/final-patches/intel--i40e--2.3.6 new file mode 100644 index 000000000..8052a5251 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.3.6 @@ -0,0 +1,167 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index ff50970..8d7f7fb 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -30,9 +30,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -46,13 +46,13 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -94,9 +94,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 7fea797..6eaed5b 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -141,6 +141,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3239,6 +3244,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3292,6 +3301,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3320,6 +3333,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -10843,6 +10861,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -11211,6 +11234,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 6ec8bf6..bfb1740 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -26,6 +26,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -788,6 +792,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2254,6 +2263,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); + bool failure = false; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + while (likely(total_rx_packets < (unsigned int)budget)) { + struct i40e_rx_buffer *rx_buffer; + union i40e_rx_desc *rx_desc; diff --git a/LINUX/final-patches/intel--i40e--2.4.10 b/LINUX/final-patches/intel--i40e--2.4.10 new file mode 100644 index 000000000..cf2c7000b --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.4.10 @@ -0,0 +1,167 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index d33d3a8..e5f49a0 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -30,9 +30,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -46,13 +46,13 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -94,9 +94,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index e60e81e..9af8153 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -141,6 +141,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3267,6 +3272,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3320,6 +3329,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3348,6 +3361,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -10944,6 +10962,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -11312,6 +11335,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index eea26ba..cf33b88 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -26,6 +26,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -788,6 +792,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2386,6 +2395,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); + bool failure = false; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + while (likely(total_rx_packets < (unsigned int)budget)) { + struct i40e_rx_buffer *rx_buffer; + union i40e_rx_desc *rx_desc; diff --git a/LINUX/final-patches/intel--i40e--2.4.3 b/LINUX/final-patches/intel--i40e--2.4.3 new file mode 100644 index 000000000..77fbb4f13 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.4.3 @@ -0,0 +1,167 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index ff50970..8d7f7fb 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -30,9 +30,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -46,13 +46,13 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -94,9 +94,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 52a661a..487ccc0 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -141,6 +141,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3280,6 +3285,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3333,6 +3342,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3361,6 +3374,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -10901,6 +10919,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -11269,6 +11292,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 0c949dd..f204f32 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -26,6 +26,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -788,6 +792,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2386,6 +2395,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); + bool failure = false; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + while (likely(total_rx_packets < (unsigned int)budget)) { + struct i40e_rx_buffer *rx_buffer; + union i40e_rx_desc *rx_desc; diff --git a/LINUX/final-patches/intel--i40e--2.4.6 b/LINUX/final-patches/intel--i40e--2.4.6 new file mode 100644 index 000000000..fb8533d23 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.4.6 @@ -0,0 +1,167 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index d33d3a8..e5f49a0 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -30,9 +30,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -46,13 +46,13 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -94,9 +94,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index ae669c3..0fa8375 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -141,6 +141,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3280,6 +3285,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3333,6 +3342,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3361,6 +3374,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -10914,6 +10932,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -11282,6 +11305,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index eea26ba..cf33b88 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -26,6 +26,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -788,6 +792,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2386,6 +2395,17 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); + bool failure = false; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + while (likely(total_rx_packets < (unsigned int)budget)) { + struct i40e_rx_buffer *rx_buffer; + union i40e_rx_desc *rx_desc; diff --git a/LINUX/final-patches/intel--i40e--2.7.11 b/LINUX/final-patches/intel--i40e--2.7.11 new file mode 100644 index 000000000..da76d0641 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.7.11 @@ -0,0 +1,168 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index ba722c7..56fc6ac 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -27,14 +27,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -88,9 +88,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 86d76c0..5629a97 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -132,6 +132,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3442,6 +3447,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3495,6 +3504,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3523,6 +3536,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -13429,6 +13447,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -13801,6 +13824,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 1859d78..39d426e 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -783,6 +787,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2551,6 +2560,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false, xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--i40e--2.7.12 b/LINUX/final-patches/intel--i40e--2.7.12 new file mode 100644 index 000000000..d7b4222e5 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.7.12 @@ -0,0 +1,168 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index ba722c7..56fc6ac 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -27,14 +27,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -88,9 +88,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 6f8e9b4..a62aadd 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -132,6 +132,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3442,6 +3447,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3495,6 +3504,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3523,6 +3536,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -13432,6 +13450,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -13804,6 +13827,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 1859d78..39d426e 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -783,6 +787,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2551,6 +2560,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false, xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--i40e--2.7.26 b/LINUX/final-patches/intel--i40e--2.7.26 new file mode 100644 index 000000000..0e3d3ab9f --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.7.26 @@ -0,0 +1,168 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 4d046c5..5f2e15a 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 6d2e21d..9845b9d 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -132,6 +132,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3442,6 +3447,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3495,6 +3504,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3523,6 +3536,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -13432,6 +13450,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -13804,6 +13827,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 1859d78..39d426e 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -783,6 +787,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2551,6 +2560,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false, xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--i40e--2.7.29 b/LINUX/final-patches/intel--i40e--2.7.29 new file mode 100644 index 000000000..ea949f491 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.7.29 @@ -0,0 +1,168 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 4d046c5..5f2e15a 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 1914934..c1dcee9 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -132,6 +132,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3442,6 +3447,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3495,6 +3504,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3523,6 +3536,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -13432,6 +13450,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -13804,6 +13827,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 1859d78..39d426e 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -783,6 +787,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2551,6 +2560,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false, xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--i40e--2.8.43 b/LINUX/final-patches/intel--i40e--2.8.43 new file mode 100644 index 000000000..560df6f9d --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.8.43 @@ -0,0 +1,168 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 5b3ca74..79db7aa 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 54940ba..f9e79b7 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -136,6 +136,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3481,6 +3486,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3534,6 +3543,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3562,6 +3575,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -13729,6 +13747,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -14101,6 +14124,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index c3ebcd7..ff4bcda 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -784,6 +788,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2554,6 +2563,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--i40e--2.9.21 b/LINUX/final-patches/intel--i40e--2.9.21 new file mode 100644 index 000000000..3b7d1e9a3 --- /dev/null +++ b/LINUX/final-patches/intel--i40e--2.9.21 @@ -0,0 +1,168 @@ +diff --git a/i40e/Makefile b/i40e/Makefile +index 5b3ca74..79db7aa 100644 +--- a/i40e/Makefile ++++ b/i40e/Makefile +@@ -10,9 +10,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_I40E) += i40e.o ++obj-$(CONFIG_I40E) += i40e$(NETMAP_DRIVER_SUFFIX).o + +-i40e-y := i40e_main.o \ ++i40e$(NETMAP_DRIVER_SUFFIX)-y := i40e_main.o \ + i40e_ethtool.o \ + i40e_adminq.o \ + i40e_common.o \ +@@ -28,14 +28,14 @@ i40e-y := i40e_main.o \ + i40e_client.o \ + i40e_virtchnl_pf.o + +-i40e-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o +-i40e-y += kcompat.o +-i40e-y += kcompat_vfd.o ++i40e$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += i40e_dcb.o i40e_dcb_nl.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o ++i40e$(NETMAP_DRIVER_SUFFIX)-y += kcompat_vfd.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := i40e ++DRIVER := i40e$(NETMAP_DRIVER_SUFFIX) + + # 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 +@@ -89,9 +89,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report) + + # Build manfiles +-manfile: ++manfile: ../${DRIVER}.${MANSECTION} + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../${DRIVER}.${MANSECTION}: ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 47f8b5f..58f65de 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -136,6 +136,11 @@ MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif ++ + /** + * i40e_get_lump - find a lump of free generic resource + * @pf: board private structure +@@ -3481,6 +3486,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3534,6 +3543,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3562,6 +3575,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -13750,6 +13768,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -14122,6 +14145,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index b1987c8..1daecc8 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -9,6 +9,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -784,6 +788,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2555,6 +2564,16 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return (nm_irq == NM_IRQ_COMPLETED) ? 1 : budget; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; + #endif diff --git a/LINUX/final-patches/intel--ice--1.10.1.2 b/LINUX/final-patches/intel--ice--1.10.1.2 new file mode 100644 index 000000000..e3b84270e --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.10.1.2 @@ -0,0 +1,221 @@ +diff --git a/ice/Makefile b/ice/Makefile +index bdae0d5..5a99acb 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -26,9 +26,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -69,12 +69,12 @@ ice-y := ice_main.o \ + ice_fwlog.o \ + ice_ieps.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o + +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -87,20 +87,20 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_vf_lib.o + + ifneq (${ENABLE_SIOV_SUPPORT},) +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + + +@@ -113,7 +113,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + COMMON_MK ?= $(wildcard common.mk) + ifeq (${COMMON_MK},) +@@ -147,7 +147,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -192,7 +192,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index f3cd8dc..a6c9061 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -461,6 +466,10 @@ static int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -617,6 +626,11 @@ int ice_vsi_cfg_rxq(struct ice_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -889,6 +903,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *tx_ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + tx_ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(tx_ring); ++#endif /* DEV_NETMAP */ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 9df9e85..493ebc2 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -104,6 +104,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6404,6 +6409,10 @@ probe_done: + #ifdef HAVE_DEVLINK_NOTIFY_REGISTER + ice_devlink_register(pf); + #endif /* HAVE_DEVLINK_NOTIFY_REGISTER */ ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + /* Unwind non-managed device resources, etc. if something failed */ +@@ -6528,6 +6537,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + hw = &pf->hw; + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 6aa6b7a..7470c56 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -32,6 +32,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -230,6 +234,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1626,6 +1634,16 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + #endif + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifndef CONFIG_ICE_USE_SKB + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ diff --git a/LINUX/final-patches/intel--ice--1.10.1.2.2 b/LINUX/final-patches/intel--ice--1.10.1.2.2 new file mode 100644 index 000000000..5c8faaa98 --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.10.1.2.2 @@ -0,0 +1,221 @@ +diff --git a/ice/Makefile b/ice/Makefile +index bdae0d5..5a99acb 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -26,9 +26,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -69,12 +69,12 @@ ice-y := ice_main.o \ + ice_fwlog.o \ + ice_ieps.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o + +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -87,20 +87,20 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_vf_lib.o + + ifneq (${ENABLE_SIOV_SUPPORT},) +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + + +@@ -113,7 +113,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + COMMON_MK ?= $(wildcard common.mk) + ifeq (${COMMON_MK},) +@@ -147,7 +147,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -192,7 +192,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index f3cd8dc..a6c9061 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -461,6 +466,10 @@ static int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -617,6 +626,11 @@ int ice_vsi_cfg_rxq(struct ice_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -889,6 +903,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *tx_ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + tx_ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(tx_ring); ++#endif /* DEV_NETMAP */ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index bc6e6fa..d2d6e32 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -104,6 +104,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6404,6 +6409,10 @@ probe_done: + #ifdef HAVE_DEVLINK_NOTIFY_REGISTER + ice_devlink_register(pf); + #endif /* HAVE_DEVLINK_NOTIFY_REGISTER */ ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + /* Unwind non-managed device resources, etc. if something failed */ +@@ -6528,6 +6537,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + hw = &pf->hw; + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 6aa6b7a..7470c56 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -32,6 +32,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -230,6 +234,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1626,6 +1634,16 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + #endif + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifndef CONFIG_ICE_USE_SKB + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ diff --git a/LINUX/final-patches/intel--ice--1.11.14 b/LINUX/final-patches/intel--ice--1.11.14 new file mode 100644 index 000000000..059e55d58 --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.11.14 @@ -0,0 +1,223 @@ +diff --git a/ice/Makefile b/ice/Makefile +index 7cf1e17..0b5d5ea 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -26,9 +26,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -68,12 +68,12 @@ ice-y := ice_main.o \ + ice_fwlog.o \ + ice_ieps.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o + +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -86,21 +86,21 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_vf_lib.o + + ifneq (${ENABLE_SIOV_SUPPORT},) +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_GNSS:m=y) += ice_gnss.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_GNSS:m=y) += ice_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + + +@@ -113,7 +113,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + COMMON_MK ?= $(wildcard common.mk) + ifeq (${COMMON_MK},) +@@ -147,7 +147,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -192,7 +192,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index fdbf6f9..c094d20 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -480,6 +485,10 @@ static int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -636,6 +645,11 @@ int ice_vsi_cfg_rxq(struct ice_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -908,6 +922,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *tx_ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + tx_ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(tx_ring); ++#endif /* DEV_NETMAP */ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 7bdd417..061fbe3 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -104,6 +104,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6551,6 +6556,10 @@ probe_done: + #ifdef HAVE_DEVLINK_NOTIFY_REGISTER + ice_devlink_register(pf); + #endif /* HAVE_DEVLINK_NOTIFY_REGISTER */ ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + /* Unwind non-managed device resources, etc. if something failed */ +@@ -6680,6 +6689,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + hw = &pf->hw; + set_bit(ICE_SHUTTING_DOWN, pf->state); + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index e3ee85c..5fcb360 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -32,6 +32,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -229,6 +233,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1624,6 +1632,16 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + #endif + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifndef CONFIG_ICE_USE_SKB + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ diff --git a/LINUX/final-patches/intel--ice--1.11.17.1 b/LINUX/final-patches/intel--ice--1.11.17.1 new file mode 100644 index 000000000..04143ac4d --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.11.17.1 @@ -0,0 +1,233 @@ +diff --git a/ice/Makefile b/ice/Makefile +index 5f29380..ae7008b 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -26,9 +26,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -69,12 +69,12 @@ ice-y := ice_main.o \ + ice_ieps.o \ + ice_gnss.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o + +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -87,30 +87,30 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_vf_lib.o + + ifneq (${ENABLE_SIOV_SUPPORT},) +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + # Use kcompat GNSS if kernel doesn't provide it + ifneq (${CONFIG_GNSS}, y) + ifneq (${CONFIG_GNSS}, m) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + endif + + ifeq (${CONFIG_SUSE_KERNEL}, y) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + + +@@ -123,7 +123,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + COMMON_MK ?= $(wildcard common.mk) + ifeq (${COMMON_MK},) +@@ -157,7 +157,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -202,7 +202,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index fdbf6f9..c094d20 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -480,6 +485,10 @@ static int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -636,6 +645,11 @@ int ice_vsi_cfg_rxq(struct ice_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -908,6 +922,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *tx_ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + tx_ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(tx_ring); ++#endif /* DEV_NETMAP */ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index dd4ee2b..45bef2b 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -104,6 +104,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6559,6 +6564,10 @@ probe_done: + #ifdef HAVE_DEVLINK_NOTIFY_REGISTER + ice_devlink_register(pf); + #endif /* HAVE_DEVLINK_NOTIFY_REGISTER */ ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + /* Unwind non-managed device resources, etc. if something failed */ +@@ -6688,6 +6697,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + hw = &pf->hw; + set_bit(ICE_SHUTTING_DOWN, pf->state); + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index e3ee85c..5fcb360 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -32,6 +32,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -229,6 +233,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1624,6 +1632,16 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + #endif + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifndef CONFIG_ICE_USE_SKB + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ diff --git a/LINUX/final-patches/intel--ice--1.12.18 b/LINUX/final-patches/intel--ice--1.12.18 new file mode 100644 index 000000000..c9da8e07f --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.12.18 @@ -0,0 +1,253 @@ +diff --git a/ice/Makefile b/ice/Makefile +index 62548b3..018ca55 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -52,9 +52,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -96,13 +96,13 @@ ice-y := ice_main.o \ + ice_ieps.o \ + ice_gnss.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_peer_support.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_peer_support.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o + +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -115,37 +115,37 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_vf_lib.o + + ifneq (${ENABLE_SIOV_SUPPORT},) +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + # Use kcompat GNSS if kernel doesn't provide it + ifneq (${CONFIG_GNSS}, y) + ifneq (${CONFIG_GNSS}, m) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + endif + + ifeq (${CONFIG_SUSE_KERNEL}, y) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + + ifneq (${ENABLE_LM_SUPPORT},) + obj-$(CONFIG_VFIO_PCI_CORE:y=m) += ice-vfio-pci.o + +-ice-vfio-pci-y := ice_vfio_pci.o +-ice-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o ++ice$(NETMAP_DRIVER_SUFFIX)-vfio-pci-y := ice_vfio_pci.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o + endif + + +@@ -157,7 +157,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + # ice does not support building on kernels older than 3.10.0 + $(call minimum_kver_check,3,10,0) +@@ -176,7 +176,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -221,7 +221,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index 84adc70..3a910f1 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -481,6 +486,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -638,6 +647,11 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -910,6 +924,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 112e871..202face 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -105,6 +105,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6841,6 +6846,7 @@ static int ice_init_devlink(struct ice_pf *pf) + if (need_register) + ice_devlink_register(pf); + #endif /* !HAVE_DEVLINK_PARAMS_PUBLISH */ ++ + return 0; + } + +@@ -7122,6 +7128,11 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + err = ice_init_features(pf); + if (err) + goto err_init_features; ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_features: +@@ -7214,6 +7225,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + set_bit(ICE_SHUTTING_DOWN, pf->state); + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 582e2cf..f5e07bd 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -32,6 +32,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -237,6 +241,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1670,6 +1678,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + #endif + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifndef CONFIG_ICE_USE_SKB + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ diff --git a/LINUX/final-patches/intel--ice--1.12.6 b/LINUX/final-patches/intel--ice--1.12.6 new file mode 100644 index 000000000..125db4d82 --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.12.6 @@ -0,0 +1,270 @@ +diff --git a/ice/Makefile b/ice/Makefile +index 16ec261..8b2034b 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -26,9 +26,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -70,13 +70,13 @@ ice-y := ice_main.o \ + ice_ieps.o \ + ice_gnss.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_peer_support.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_peer_support.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o + +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -89,37 +89,37 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_vf_lib.o + + ifneq (${ENABLE_SIOV_SUPPORT},) +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + # Use kcompat GNSS if kernel doesn't provide it + ifneq (${CONFIG_GNSS}, y) + ifneq (${CONFIG_GNSS}, m) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + endif + + ifeq (${CONFIG_SUSE_KERNEL}, y) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + + ifneq (${ENABLE_LM_SUPPORT},) + obj-$(CONFIG_VFIO_PCI_CORE:y=m) += ice-vfio-pci.o + +-ice-vfio-pci-y := ice_vfio_pci.o +-ice-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o ++ice$(NETMAP_DRIVER_SUFFIX)-vfio-pci-y := ice_vfio_pci.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o + endif + + +@@ -131,7 +131,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + COMMON_MK ?= $(wildcard common.mk) + ifeq (${COMMON_MK},) +@@ -172,7 +172,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -217,7 +217,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index 84adc70..3a910f1 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -481,6 +486,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -638,6 +647,11 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -910,6 +924,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 8f515cd..b15f994 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -105,6 +105,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6828,6 +6833,7 @@ static int ice_init_devlink(struct ice_pf *pf) + if (need_register) + ice_devlink_register(pf); + #endif /* !HAVE_DEVLINK_PARAMS_PUBLISH */ ++ + return 0; + } + +@@ -7109,6 +7115,11 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + err = ice_init_features(pf); + if (err) + goto err_init_features; ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_features: +@@ -7201,6 +7212,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + set_bit(ICE_SHUTTING_DOWN, pf->state); + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 582e2cf..e2c6401 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -32,6 +32,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -237,6 +241,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -419,6 +427,16 @@ void ice_clean_rx_ring(struct ice_rx_ring *rx_ring) + u32 size; + u16 i; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* ring already cleared, nothing to do */ + if (!rx_ring->rx_buf) + return; +@@ -1670,6 +1688,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + #endif + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifndef CONFIG_ICE_USE_SKB + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ diff --git a/LINUX/final-patches/intel--ice--1.12.7 b/LINUX/final-patches/intel--ice--1.12.7 new file mode 100644 index 000000000..d91286592 --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.12.7 @@ -0,0 +1,253 @@ +diff --git a/ice/Makefile b/ice/Makefile +index 16ec261..8b2034b 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -26,9 +26,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -70,13 +70,13 @@ ice-y := ice_main.o \ + ice_ieps.o \ + ice_gnss.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_peer_support.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_peer_support.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o + +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -89,37 +89,37 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_vf_lib.o + + ifneq (${ENABLE_SIOV_SUPPORT},) +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + # Use kcompat GNSS if kernel doesn't provide it + ifneq (${CONFIG_GNSS}, y) + ifneq (${CONFIG_GNSS}, m) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + endif + + ifeq (${CONFIG_SUSE_KERNEL}, y) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + + ifneq (${ENABLE_LM_SUPPORT},) + obj-$(CONFIG_VFIO_PCI_CORE:y=m) += ice-vfio-pci.o + +-ice-vfio-pci-y := ice_vfio_pci.o +-ice-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o ++ice$(NETMAP_DRIVER_SUFFIX)-vfio-pci-y := ice_vfio_pci.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o + endif + + +@@ -131,7 +131,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + COMMON_MK ?= $(wildcard common.mk) + ifeq (${COMMON_MK},) +@@ -172,7 +172,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -217,7 +217,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index 84adc70..3a910f1 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -481,6 +486,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -638,6 +647,11 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -910,6 +924,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index a32bb2a..5b455c5 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -105,6 +105,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6835,6 +6840,7 @@ static int ice_init_devlink(struct ice_pf *pf) + if (need_register) + ice_devlink_register(pf); + #endif /* !HAVE_DEVLINK_PARAMS_PUBLISH */ ++ + return 0; + } + +@@ -7116,6 +7122,11 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + err = ice_init_features(pf); + if (err) + goto err_init_features; ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_features: +@@ -7208,6 +7219,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + set_bit(ICE_SHUTTING_DOWN, pf->state); + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 582e2cf..f5e07bd 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -32,6 +32,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -237,6 +241,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1670,6 +1678,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + #endif + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifndef CONFIG_ICE_USE_SKB + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ diff --git a/LINUX/final-patches/intel--ice--1.13.7 b/LINUX/final-patches/intel--ice--1.13.7 new file mode 100644 index 000000000..cbd588c5d --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.13.7 @@ -0,0 +1,253 @@ +diff --git a/ice/Makefile b/ice/Makefile +index 66ba8a6..436d2e2 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -52,9 +52,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -97,13 +97,13 @@ ice-y := ice_main.o \ + ice_ieps.o \ + ice_gnss.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_aux_support.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_aux_support.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o + +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -116,37 +116,37 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_vf_lib.o + + ifneq (${ENABLE_SIOV_SUPPORT},) +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + # Use kcompat GNSS if kernel doesn't provide it + ifneq (${CONFIG_GNSS}, y) + ifneq (${CONFIG_GNSS}, m) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + endif + + ifeq (${CONFIG_SUSE_KERNEL}, y) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + + ifneq (${ENABLE_LM_SUPPORT},) + obj-$(CONFIG_VFIO_PCI_CORE:y=m) += ice-vfio-pci.o + +-ice-vfio-pci-y := ice_vfio_pci.o +-ice-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o ++ice$(NETMAP_DRIVER_SUFFIX)-vfio-pci-y := ice_vfio_pci.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o + endif + + +@@ -158,7 +158,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + EXTRA_CFLAGS += -std=gnu11 + + # ice does not support building on kernels older than 3.10.0 +@@ -178,7 +178,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -223,7 +223,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index c020e40..c1e82a2 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -482,6 +487,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -639,6 +648,11 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -911,6 +925,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index e9cc474..0eed1e5 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -107,6 +107,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -7008,6 +7013,7 @@ static int ice_init_devlink(struct ice_pf *pf) + if (need_register) + ice_devlink_register(pf); + #endif /* !HAVE_DEVLINK_PARAMS_PUBLISH */ ++ + return 0; + } + +@@ -7294,6 +7300,11 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + err = ice_init_features(pf); + if (err) + goto err_init_features; ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_features: +@@ -7385,6 +7396,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + set_bit(ICE_SHUTTING_DOWN, pf->state); + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 0117124..1caa0a3 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -32,6 +32,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -237,6 +241,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1675,6 +1683,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + #endif + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifndef CONFIG_ICE_USE_SKB + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ diff --git a/LINUX/final-patches/intel--ice--1.14.13 b/LINUX/final-patches/intel--ice--1.14.13 new file mode 100644 index 000000000..db7435aa6 --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.14.13 @@ -0,0 +1,280 @@ +diff --git a/ice/Makefile b/ice/Makefile +index c1be1d4..23ba9d4 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -41,9 +41,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -85,14 +85,14 @@ ice-y := ice_main.o \ + ice_ieps.o \ + ice_gnss.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o devlink/ice_devlink_health.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_aux_support.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o +-ice-$(CONFIG_DEBUG_FS) += ice_fwlog.o +- +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o devlink/ice_devlink_health.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_aux_support.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_fwlog.o ++ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -104,42 +104,42 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_virtchnl_fsub.o \ + ice_vf_lib.o + +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + + ifneq (${CONFIG_DPLL},) +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ice_dpll.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ice_dpll.o + else +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_cpi.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_cpi.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + # Use kcompat GNSS if kernel doesn't provide it + ifneq (${CONFIG_GNSS}, y) + ifneq (${CONFIG_GNSS}, m) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + endif + + ifeq (${CONFIG_SUSE_KERNEL}, y) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + + ifneq ($(shell grep HAVE_LMV1_SUPPORT $(src)/kcompat_generated_defs.h),) + obj-$(CONFIG_VFIO_PCI_CORE:y=m) += ice-vfio-pci.o + +-ice-vfio-pci-y := ice_vfio_pci.o +-ice-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o ++ice$(NETMAP_DRIVER_SUFFIX)-vfio-pci-y := ice_vfio_pci.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o + endif + + +@@ -151,7 +151,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + # ice does not support building on kernels older than 3.10.0 + $(call minimum_kver_check,3,10,0) +@@ -170,7 +170,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -215,7 +215,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index 68aeb25..8e3be6d 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -9,6 +9,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -477,6 +482,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -635,6 +644,11 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -907,6 +921,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 4a07275..3e41b15 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -66,6 +66,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all), hw debug_mask (0x8XXXX + MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + #endif /* !CONFIG_DYNAMIC_DEBUG */ + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6879,6 +6884,7 @@ static int ice_init_devlink(struct ice_pf *pf) + if (need_register) + ice_devlink_register(pf); + #endif /* !HAVE_DEVLINK_PARAMS_PUBLISH */ ++ + return 0; + } + +@@ -7149,6 +7155,11 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + err = ice_init_features(pf); + if (err) + goto err_init_features; ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_features: +@@ -7242,6 +7253,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + set_bit(ICE_SHUTTING_DOWN, pf->state); + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 5fb23d2..a7305fd 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -32,6 +32,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -237,6 +241,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -424,6 +432,16 @@ void ice_clean_rx_ring(struct ice_rx_ring *rx_ring) + u32 size; + u16 i; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* ring already cleared, nothing to do */ + if (!rx_ring->rx_buf) + return; +@@ -1670,6 +1688,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + #endif + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifndef CONFIG_ICE_USE_SKB + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ diff --git a/LINUX/final-patches/intel--ice--1.15.4 b/LINUX/final-patches/intel--ice--1.15.4 new file mode 100644 index 000000000..3dd306b22 --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.15.4 @@ -0,0 +1,282 @@ +diff --git a/ice/Makefile b/src/Makefile +index 43bc5a9..b59b7d7 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -41,9 +41,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -85,14 +85,14 @@ ice-y := ice_main.o \ + ice_ieps.o \ + ice_gnss.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o devlink/ice_devlink_health.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_aux_support.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o +-ice-$(CONFIG_DEBUG_FS) += ice_fwlog.o +- +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o devlink/ice_devlink_health.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_aux_support.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_fwlog.o ++ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -104,43 +104,43 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_virtchnl_fsub.o \ + ice_vf_lib.o + +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + + ifneq (${CONFIG_DPLL},) +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ice_dpll.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ice_dpll.o + else +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_cpi.o +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_tspll.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_cpi.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_tspll.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + # Use kcompat GNSS if kernel doesn't provide it + ifneq (${CONFIG_GNSS}, y) + ifneq (${CONFIG_GNSS}, m) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + endif + + ifeq (${CONFIG_SUSE_KERNEL}, y) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + + ifneq ($(shell grep HAVE_LMV1_SUPPORT $(src)/kcompat_generated_defs.h),) + obj-$(CONFIG_VFIO_PCI_CORE:y=m) += ice-vfio-pci.o + +-ice-vfio-pci-y := ice_vfio_pci.o +-ice-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o ++ice$(NETMAP_DRIVER_SUFFIX)-vfio-pci-y := ice_vfio_pci.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o + endif + + +@@ -152,7 +152,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + # ice does not support building on kernels older than 3.10.0 + $(call minimum_kver_check,3,10,0) +@@ -171,7 +171,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -216,7 +216,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/src/ice_base.c +index 714abce..e318b40 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -9,6 +9,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -536,6 +541,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + rxdid = ICE_RXDID_FLEX_NIC_2; + } + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -694,6 +703,11 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -968,6 +982,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ + + if (tstamp_ring) { + u8 txtime_buf_len = struct_size(txtime_qg_buf, txtimeqs, 1); +diff --git a/ice/ice_main.c b/src/ice_main.c +index 06d8413..3d468f2 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -66,6 +66,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all), hw debug_mask (0x8XXXX + MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + #endif /* !CONFIG_DYNAMIC_DEBUG */ + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6917,6 +6922,7 @@ static int ice_init_devlink(struct ice_pf *pf) + if (need_register) + ice_devlink_register(pf); + #endif /* !HAVE_DEVLINK_PARAMS_PUBLISH */ ++ + return 0; + } + +@@ -7196,6 +7202,11 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + err = ice_init_features(pf); + if (err) + goto err_init_features; ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_features: +@@ -7289,6 +7300,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + set_bit(ICE_SHUTTING_DOWN, pf->state); + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set +diff --git a/ice/ice_txrx.c b/src/ice_txrx.c +index 78945b7..44ae62f 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -32,6 +32,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -258,6 +262,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -476,6 +484,16 @@ void ice_clean_rx_ring(struct ice_rx_ring *rx_ring) + u32 size; + u16 i; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* ring already cleared, nothing to do */ + if (!rx_ring->rx_buf) + return; +@@ -1720,6 +1738,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + #endif + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifndef CONFIG_ICE_USE_SKB + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ diff --git a/LINUX/final-patches/intel--ice--1.16.3 b/LINUX/final-patches/intel--ice--1.16.3 new file mode 100644 index 000000000..9adf21dc6 --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.16.3 @@ -0,0 +1,288 @@ +commit 969129ed6281851577a098501a489e72fa486491 +Author: Giuseppe Lettieri +Date: Tue Feb 11 20:12:52 2025 +0100 + + netmap patch + +diff --git a/ice/Makefile b/src/Makefile +index 8b06e64..684df0f 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -41,9 +41,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -87,14 +87,14 @@ ice-y := ice_main.o \ + ice_ieps_lm.o \ + ice_gnss.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o devlink/ice_devlink_health.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_aux_support.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o +-ice-$(CONFIG_DEBUG_FS) += ice_fwlog.o +- +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o devlink/ice_devlink_health.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_aux_support.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_fwlog.o ++ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -106,43 +106,43 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_virtchnl_fsub.o \ + ice_vf_lib.o + +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + + ifneq (${CONFIG_DPLL},) +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ice_dpll.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ice_dpll.o + else +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_cpi.o +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_tspll.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_cpi.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_tspll.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + # Use kcompat GNSS if kernel doesn't provide it + ifneq (${CONFIG_GNSS}, y) + ifneq (${CONFIG_GNSS}, m) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + endif + + ifeq (${CONFIG_SUSE_KERNEL}, y) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + + ifneq ($(shell grep HAVE_LMV1_SUPPORT $(src)/kcompat_generated_defs.h),) + obj-$(CONFIG_VFIO_PCI_CORE:y=m) += ice-vfio-pci.o + +-ice-vfio-pci-y := ice_vfio_pci.o +-ice-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o ++ice$(NETMAP_DRIVER_SUFFIX)-vfio-pci-y := ice_vfio_pci.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o + endif + + +@@ -154,7 +154,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + # ice does not support building on kernels older than 3.10.0 + $(call minimum_kver_check,3,10,0) +@@ -173,7 +173,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -218,7 +218,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/src/ice_base.c +index f977414..e494a00 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -9,6 +9,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -537,6 +542,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + rxdid = ICE_RXDID_FLEX_NIC_2; + } + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -695,6 +704,11 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -974,6 +988,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ + + if (tstamp_ring) { + u8 txtime_buf_len = struct_size(txtime_qg_buf, txtimeqs, 1); +diff --git a/ice/ice_main.c b/src/ice_main.c +index a8c1721..63f2e69 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -67,6 +67,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all), hw debug_mask (0x8XXXX + MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + #endif /* !CONFIG_DYNAMIC_DEBUG */ + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6995,6 +7000,7 @@ static int ice_init_devlink(struct ice_pf *pf) + if (need_register) + ice_devlink_register(pf); + #endif /* !HAVE_DEVLINK_PARAMS_PUBLISH */ ++ + return 0; + } + +@@ -7274,6 +7280,11 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + err = ice_init_features(pf); + if (err) + goto err_init_features; ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_features: +@@ -7367,6 +7378,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + set_bit(ICE_SHUTTING_DOWN, pf->state); + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set +diff --git a/ice/ice_txrx.c b/src/ice_txrx.c +index d5f7d5c..f84602d 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -32,6 +32,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -258,6 +262,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -476,6 +484,16 @@ void ice_clean_rx_ring(struct ice_rx_ring *rx_ring) + u32 size; + u16 i; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* ring already cleared, nothing to do */ + if (!rx_ring->rx_buf) + return; +@@ -1720,6 +1738,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + #endif + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifndef CONFIG_ICE_USE_SKB + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ diff --git a/LINUX/final-patches/intel--ice--1.7.15 b/LINUX/final-patches/intel--ice--1.7.15 new file mode 100644 index 000000000..e69de29bb diff --git a/LINUX/final-patches/intel--ice--1.7.16 b/LINUX/final-patches/intel--ice--1.7.16 new file mode 100644 index 000000000..1eadbdaa9 --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.7.16 @@ -0,0 +1,218 @@ +diff --git a/ice/Makefile b/ice/Makefile +index a98dd4c..6e6e41f 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -26,9 +26,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -56,26 +56,26 @@ ice-y := ice_main.o \ + ice_fwlog.o \ + ice_ieps.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o +-ice-$(CONFIG_PCI_IOV) += ice_virtchnl_allowlist.o +-ice-$(CONFIG_PCI_IOV) += ice_dcf.o +-ice-$(CONFIG_PCI_IOV) += ice_virtchnl_fdir.o +-ice-$(CONFIG_PCI_IOV) += ice_virtchnl_pf.o ice_vf_mbx.o ice_vf_vsi_vlan_ops.o +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_virtchnl_allowlist.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_dcf.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_virtchnl_fdir.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_virtchnl_pf.o ice_vf_mbx.o ice_vf_vsi_vlan_ops.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + + ifeq (${NEED_AUX_BUS},2) +@@ -85,7 +85,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + COMMON_MK ?= $(wildcard common.mk) + ifeq (${COMMON_MK},) +@@ -112,7 +112,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -144,7 +144,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index 9f2320f..9f9126a 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_virtchnl_pf.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -448,6 +453,10 @@ static int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -601,6 +610,11 @@ int ice_vsi_cfg_rxq(struct ice_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -871,6 +885,11 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 97e754b..4eeb305 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -103,6 +103,12 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ ++ + static struct workqueue_struct *ice_wq; + + static const struct net_device_ops ice_netdev_recovery_ops; +@@ -6169,6 +6175,9 @@ probe_done: + dev_warn(dev, "Aux drivers are not supported on this device\n"); + } + ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + /* Unwind non-managed device resources, etc. if something failed */ +@@ -6279,6 +6288,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set + * it means that the driver went into recovery mode on load. +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 037cc5a..899ee4c 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -30,6 +30,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -227,6 +231,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1481,6 +1489,17 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + struct xdp_buff xdp; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; diff --git a/LINUX/final-patches/intel--ice--1.8.3 b/LINUX/final-patches/intel--ice--1.8.3 new file mode 100644 index 000000000..b15d1e9b5 --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.8.3 @@ -0,0 +1,225 @@ +diff --git a/ice/Makefile b/ice/Makefile +index 789a908..3d66e39 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -26,9 +26,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -57,32 +57,33 @@ ice-y := ice_main.o \ + ice_fwlog.o \ + ice_ieps.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o +-ice-$(CONFIG_PCI_IOV) += ice_virtchnl_allowlist.o +-ice-$(CONFIG_PCI_IOV) += ice_dcf.o +-ice-$(CONFIG_PCI_IOV) += ice_virtchnl_fdir.o +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_virtchnl_allowlist.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_dcf.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_virtchnl_fdir.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_sriov.o \ + ice_vf_mbx.o \ + ice_vf_vsi_vlan_ops.o \ + ice_vf_adq.o \ + ice_virtchnl.o \ + ice_vf_lib.o +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + + ifeq (${NEED_AUX_BUS},2) +@@ -92,7 +93,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + COMMON_MK ?= $(wildcard common.mk) + ifeq (${COMMON_MK},) +@@ -119,7 +120,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -151,7 +152,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index adaaa84..b281b6b 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -459,6 +464,10 @@ static int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -615,6 +624,11 @@ int ice_vsi_cfg_rxq(struct ice_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -885,6 +899,11 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index e00d7f8..9438f93 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -104,6 +104,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -5990,6 +5995,10 @@ probe_done: + #ifdef HAVE_DEVLINK_NOTIFY_REGISTER + ice_devlink_register(pf); + #endif /* HAVE_DEVLINK_NOTIFY_REGISTER */ ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + /* Unwind non-managed device resources, etc. if something failed */ +@@ -6106,6 +6115,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set + * it means that the driver went into recovery mode on load. +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 68e659d..cb048fe 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -30,6 +30,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -227,6 +231,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1472,6 +1480,17 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + struct xdp_buff xdp; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; diff --git a/LINUX/final-patches/intel--ice--1.8.8 b/LINUX/final-patches/intel--ice--1.8.8 new file mode 100644 index 000000000..3f70b2f3a --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.8.8 @@ -0,0 +1,225 @@ +diff --git a/ice/Makefile b/ice/Makefile +index 789a908..3d66e39 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -26,9 +26,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -57,32 +57,33 @@ ice-y := ice_main.o \ + ice_fwlog.o \ + ice_ieps.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o +-ice-$(CONFIG_PCI_IOV) += ice_virtchnl_allowlist.o +-ice-$(CONFIG_PCI_IOV) += ice_dcf.o +-ice-$(CONFIG_PCI_IOV) += ice_virtchnl_fdir.o +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_virtchnl_allowlist.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_dcf.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_virtchnl_fdir.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_sriov.o \ + ice_vf_mbx.o \ + ice_vf_vsi_vlan_ops.o \ + ice_vf_adq.o \ + ice_virtchnl.o \ + ice_vf_lib.o +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + + ifeq (${NEED_AUX_BUS},2) +@@ -92,7 +93,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + COMMON_MK ?= $(wildcard common.mk) + ifeq (${COMMON_MK},) +@@ -119,7 +120,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -151,7 +152,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index adaaa84..b281b6b 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -459,6 +464,10 @@ static int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -615,6 +624,11 @@ int ice_vsi_cfg_rxq(struct ice_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -885,6 +899,11 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index ab5e57c..b957285 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -104,6 +104,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6005,6 +6010,10 @@ probe_done: + #ifdef HAVE_DEVLINK_NOTIFY_REGISTER + ice_devlink_register(pf); + #endif /* HAVE_DEVLINK_NOTIFY_REGISTER */ ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + /* Unwind non-managed device resources, etc. if something failed */ +@@ -6121,6 +6130,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set + * it means that the driver went into recovery mode on load. +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 68e659d..cb048fe 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -30,6 +30,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -227,6 +231,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1472,6 +1480,17 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + struct xdp_buff xdp; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; diff --git a/LINUX/final-patches/intel--ice--1.8.9 b/LINUX/final-patches/intel--ice--1.8.9 new file mode 100644 index 000000000..0531da6d4 --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.8.9 @@ -0,0 +1,225 @@ +diff --git a/ice/Makefile b/ice/Makefile +index 789a908..3d66e39 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -26,9 +26,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -57,32 +57,33 @@ ice-y := ice_main.o \ + ice_fwlog.o \ + ice_ieps.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o +-ice-$(CONFIG_PCI_IOV) += ice_virtchnl_allowlist.o +-ice-$(CONFIG_PCI_IOV) += ice_dcf.o +-ice-$(CONFIG_PCI_IOV) += ice_virtchnl_fdir.o +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_virtchnl_allowlist.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_dcf.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += ice_virtchnl_fdir.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_sriov.o \ + ice_vf_mbx.o \ + ice_vf_vsi_vlan_ops.o \ + ice_vf_adq.o \ + ice_virtchnl.o \ + ice_vf_lib.o +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + + ifeq (${NEED_AUX_BUS},2) +@@ -92,7 +93,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + COMMON_MK ?= $(wildcard common.mk) + ifeq (${COMMON_MK},) +@@ -119,7 +120,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -151,7 +152,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index adaaa84..b281b6b 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -459,6 +464,10 @@ static int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -615,6 +624,11 @@ int ice_vsi_cfg_rxq(struct ice_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -885,6 +899,11 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index e546eef..4894aa7 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -104,6 +104,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6005,6 +6010,10 @@ probe_done: + #ifdef HAVE_DEVLINK_NOTIFY_REGISTER + ice_devlink_register(pf); + #endif /* HAVE_DEVLINK_NOTIFY_REGISTER */ ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + /* Unwind non-managed device resources, etc. if something failed */ +@@ -6121,6 +6130,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set + * it means that the driver went into recovery mode on load. +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 68e659d..cb048fe 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -30,6 +30,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -227,6 +231,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1472,6 +1480,17 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + struct xdp_buff xdp; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; diff --git a/LINUX/final-patches/intel--ice--1.9.11 b/LINUX/final-patches/intel--ice--1.9.11 new file mode 100644 index 000000000..8c0773924 --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.9.11 @@ -0,0 +1,222 @@ +diff --git a/ice/Makefile b/ice/Makefile +index d94e327..b930d52 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -26,9 +26,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -58,12 +58,12 @@ ice-y := ice_main.o \ + ice_fwlog.o \ + ice_ieps.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o + +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -75,20 +75,20 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_vf_lib.o + + ifneq (${ENABLE_SIOV_SUPPORT},) +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + + ifeq (${NEED_AUX_BUS},2) +@@ -98,7 +98,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + COMMON_MK ?= $(wildcard common.mk) + ifeq (${COMMON_MK},) +@@ -131,7 +131,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -176,7 +176,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index b43752e..9fe1540 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -461,6 +466,10 @@ static int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -617,6 +626,11 @@ int ice_vsi_cfg_rxq(struct ice_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -889,6 +903,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *tx_ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + tx_ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(tx_ring); ++#endif /* DEV_NETMAP */ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 6aaddd0..7af2de9 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -104,6 +104,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6336,6 +6341,10 @@ probe_done: + #ifdef HAVE_DEVLINK_NOTIFY_REGISTER + ice_devlink_register(pf); + #endif /* HAVE_DEVLINK_NOTIFY_REGISTER */ ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + /* Unwind non-managed device resources, etc. if something failed */ +@@ -6458,6 +6467,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + hw = &pf->hw; + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index a094aec..020b7ae 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -30,6 +30,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -227,6 +231,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1472,6 +1480,17 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + struct xdp_buff xdp; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; diff --git a/LINUX/final-patches/intel--ice--1.9.7 b/LINUX/final-patches/intel--ice--1.9.7 new file mode 100644 index 000000000..aee31d11c --- /dev/null +++ b/LINUX/final-patches/intel--ice--1.9.7 @@ -0,0 +1,224 @@ +diff --git a/ice/Makefile b/ice/Makefile +index d94e327..b930d52 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -26,9 +26,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_controlq.o \ + ice_common.o \ + ice_nvm.o \ +@@ -58,12 +58,12 @@ ice-y := ice_main.o \ + ice_fwlog.o \ + ice_ieps.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o + +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -75,20 +75,20 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_vf_lib.o + + ifneq (${ENABLE_SIOV_SUPPORT},) +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + + ifeq (${NEED_AUX_BUS},2) +@@ -98,7 +98,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + COMMON_MK ?= $(wildcard common.mk) + ifeq (${COMMON_MK},) +@@ -131,7 +131,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -176,7 +176,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/ice/ice_base.c +index b43752e..f28e37d 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -461,6 +466,10 @@ static int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -617,6 +626,11 @@ int ice_vsi_cfg_rxq(struct ice_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -889,6 +903,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *tx_ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + tx_ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(tx_ring); ++#endif /* DEV_NETMAP */ + + return 0; + } +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 832e31e..71bceb6 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -104,6 +104,11 @@ static unsigned long fwlog_events; /* no enabled events by default */ + module_param(fwlog_events, ulong, 0644); + MODULE_PARM_DESC(fwlog_events, "FW events to log (32-bit mask)\n"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -6336,6 +6341,10 @@ probe_done: + #ifdef HAVE_DEVLINK_NOTIFY_REGISTER + ice_devlink_register(pf); + #endif /* HAVE_DEVLINK_NOTIFY_REGISTER */ ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + /* Unwind non-managed device resources, etc. if something failed */ +@@ -6458,7 +6467,12 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + hw = &pf->hw; ++ + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set + * it means that the driver went into recovery mode on load. +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index a094aec..020b7ae 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -30,6 +30,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -227,6 +231,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -1472,6 +1480,17 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + struct xdp_buff xdp; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ ++ + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ + xdp.rxq = &rx_ring->xdp_rxq; diff --git a/LINUX/final-patches/intel--ice--2.2.9 b/LINUX/final-patches/intel--ice--2.2.9 new file mode 100644 index 000000000..33d261f37 --- /dev/null +++ b/LINUX/final-patches/intel--ice--2.2.9 @@ -0,0 +1,286 @@ +diff --git a/ice/Makefile b/src/Makefile +index 4b73d8c..9456cdd 100644 +--- a/ice/Makefile ++++ b/ice/Makefile +@@ -41,9 +41,9 @@ ifneq ($(KERNELRELEASE),) + ccflags-y += -I$(src) + subdir-ccflags-y += -I$(src) + +-obj-m += ice.o ++obj-m += ice$(NETMAP_DRIVER_SUFFIX).o + +-ice-y := ice_main.o \ ++ice$(NETMAP_DRIVER_SUFFIX)-y := ice_main.o \ + ice_adapter.o \ + ice_controlq.o \ + ice_common.o \ +@@ -88,14 +88,14 @@ ice-y := ice_main.o \ + ice_ieps_lm.o \ + ice_gnss.o \ + ice_ethtool.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o devlink/health.o ice_fw_update.o +-ice-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o +-ice-y += ice_aux_support.o +-ice-y += ice_idc.o +-ice-$(CONFIG_DEBUG_FS) += ice_debugfs.o +-ice-$(CONFIG_DEBUG_FS) += ice_fwlog.o +- +-ice-$(CONFIG_PCI_IOV) += \ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_devlink.o devlink/health.o ice_fw_update.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_NET_DEVLINK:m=y) += ice_eswitch.o ice_repr.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_aux_support.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += ice_idc.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_debugfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DEBUG_FS) += ice_fwlog.o ++ ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_IOV) += \ + ice_dcf.o \ + ice_sriov.o \ + ice_vf_mbx.o \ +@@ -107,36 +107,36 @@ ice-$(CONFIG_PCI_IOV) += \ + ice_virtchnl_fsub.o \ + ice_vf_lib.o + +-ice-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_MDEV:m=y) += ice_vdcm.o ice_siov.o + + ifneq (${CONFIG_DPLL},) +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ice_dpll.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ice_dpll.o + else +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_ptp.o ice_ptp_hw.o + endif +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_cpi.o +-ice-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_tspll.o +-ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o +-ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o +-ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o +-ice-y += kcompat.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_cpi.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ice_tspll.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += ice_arfs.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_XDP_SOCKETS) += ice_xsk.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ice-y += kcompat_pldmfw.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif + # Use kcompat DIMLIB if kernel doesn't provide it + ifndef CONFIG_DIMLIB +-ice-y += kcompat_dim.o kcompat_net_dim.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_dim.o kcompat_net_dim.o + endif + # Use kcompat GNSS if kernel doesn't provide it + ifneq (${CONFIG_GNSS}, y) + ifneq (${CONFIG_GNSS}, m) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + endif + + ifeq (${CONFIG_SUSE_KERNEL}, y) +-ice-y += kcompat_gnss.o ++ice$(NETMAP_DRIVER_SUFFIX)-y += kcompat_gnss.o + endif + + ifeq ($(shell grep HAVE_XARRAY_API $(src)/kcompat_generated_defs.h),) +@@ -146,8 +146,8 @@ endif + ifneq ($(shell grep HAVE_LMV1_SUPPORT $(src)/kcompat_generated_defs.h),) + obj-$(CONFIG_VFIO_PCI_CORE:y=m) += ice-vfio-pci.o + +-ice-vfio-pci-y := ice_vfio_pci.o +-ice-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o ++ice$(NETMAP_DRIVER_SUFFIX)-vfio-pci-y := ice_vfio_pci.o ++ice$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_VFIO_PCI_CORE:m=y) += ice_migration.o + endif + + +@@ -159,7 +159,7 @@ endif + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ice ++DRIVER := ice$(NETMAP_DRIVER_SUFFIX) + + # ice does not support building on kernels older than 3.10.0 + $(call minimum_kver_check,3,10,0) +@@ -178,7 +178,7 @@ endif + + all: + +$(call kernelbuild,modules) +- @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++ ##@gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ifneq ($(wildcard lttng),) + $(MAKE) -C lttng + endif +@@ -223,7 +223,7 @@ ifneq (${DDP_PKG_ORIGIN},) + endif + + mandocs_install: all +- install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz ++ ##install -D -m 644 ${DRIVER}.${MANSECTION}.gz ${INSTALL_MOD_PATH}/${MANDIR}/man${MANSECTION}/${DRIVER}.${MANSECTION}.gz + + # After installing all the files, perform necessary work to ensure the system + # will use the new modules. This includes running depmod to update module +diff --git a/ice/ice_base.c b/src/ice_base.c +index 240113e..3aaf525 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -9,6 +9,11 @@ + #include "ice_dcb_lib.h" + #include "ice_sriov.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI + * @qs_cfg: gathered variables needed for PF->VSI queues assignment +@@ -572,6 +577,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + rxdid = ICE_RXDID_FLEX_NIC_2; + } + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -730,6 +739,11 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + #endif /* HAVE_AF_XDP_ZC_SUPPORT */ ++ ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + + ice_alloc_rx_bufs(ring, num_bufs); + +@@ -1009,6 +1023,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + txq = &qg_buf->txqs[0]; + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ + + if (tstamp_ring) { + u8 txtime_buf_len = struct_size(txtime_qg_buf, txtimeqs, 1); +diff --git a/ice/ice_main.c b/src/ice_main.c +index 3534b0a..ce039f0 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -69,6 +69,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all), hw debug_mask (0x8XXXX + MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + #endif /* !CONFIG_DYNAMIC_DEBUG */ + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -7042,8 +7047,7 @@ static int ice_init_devlink(struct ice_pf *pf) + if (need_register) + ice_devlink_register(pf); + #endif /* !HAVE_DEVLINK_PARAMS_PUBLISH */ +- ice_health_init(pf); +-#endif /* CONFIG_NET_DEVLINK */ ++ + return 0; + } + +@@ -7340,6 +7344,11 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + err = ice_init_features(pf); + if (err) + goto err_init_features; ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_features: +@@ -7435,6 +7444,10 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + set_bit(ICE_SHUTTING_DOWN, pf->state); + /* ICE_PREPPED_RECOVERY_MODE is set when the up and running + * driver transitions to recovery mode. If this is not set +diff --git a/ice/ice_txrx.c b/src/ice_txrx.c +index 2e1afb5..4f5df19 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -32,6 +32,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -258,6 +262,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + #ifdef HAVE_XDP_SUPPORT +@@ -476,6 +484,16 @@ void ice_clean_rx_ring(struct ice_rx_ring *rx_ring) + u32 size; + u16 i; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* ring already cleared, nothing to do */ + if (!rx_ring->rx_buf) + return; +@@ -1720,6 +1738,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + #endif + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + #ifndef CONFIG_ICE_USE_SKB + #ifdef HAVE_XDP_SUPPORT + #ifdef HAVE_XDP_BUFF_RXQ diff --git a/LINUX/final-patches/intel--igb--5.10.2 b/LINUX/final-patches/intel--igb--5.10.2 new file mode 100644 index 000000000..279766615 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.10.2 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 3325ed6..f3195bb 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index fdca9fd..206b190 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -249,6 +249,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3238,6 +3242,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3443,6 +3451,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3857,6 +3869,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7431,6 +7446,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8447,6 +8467,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8766,6 +8791,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.11.4 b/LINUX/final-patches/intel--igb--5.11.4 new file mode 100644 index 000000000..c0c9eee74 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.11.4 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 3325ed6..f3195bb 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index d3d5faa..89cfc64 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -249,6 +249,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3238,6 +3242,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3443,6 +3451,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3857,6 +3869,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7431,6 +7446,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8447,6 +8467,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8766,6 +8791,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.12.3 b/LINUX/final-patches/intel--igb--5.12.3 new file mode 100644 index 000000000..fbd2f2827 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.12.3 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 3325ed6..f3195bb 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 5f22c8b..4eabf3a 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -249,6 +249,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3238,6 +3242,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3443,6 +3451,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3857,6 +3869,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7431,6 +7446,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8447,6 +8467,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8766,6 +8791,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.13.16 b/LINUX/final-patches/intel--igb--5.13.16 new file mode 100644 index 000000000..dfc0e5a50 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.13.16 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index e761697..38767eb 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index d9afbd4..db4f6de 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -249,6 +249,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3238,6 +3242,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3443,6 +3451,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3857,6 +3869,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7431,6 +7446,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8447,6 +8467,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8766,6 +8791,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.13.20 b/LINUX/final-patches/intel--igb--5.13.20 new file mode 100644 index 000000000..ba4f3d1c7 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.13.20 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index e761697..38767eb 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index ca98377..9766c5c 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -249,6 +249,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3238,6 +3242,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3443,6 +3451,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3857,6 +3869,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7431,6 +7446,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8447,6 +8467,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8766,6 +8791,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.13.7 b/LINUX/final-patches/intel--igb--5.13.7 new file mode 100644 index 000000000..732d346a1 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.13.7 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 3325ed6..f3195bb 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 9122b5e..9aa8684 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -249,6 +249,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3238,6 +3242,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3443,6 +3451,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3857,6 +3869,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7431,6 +7446,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8447,6 +8467,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8766,6 +8791,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.14.16 b/LINUX/final-patches/intel--igb--5.14.16 new file mode 100644 index 000000000..f871cd9d0 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.14.16 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index f72e6da..c387ecd 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 8427c35..5b866be 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -249,6 +249,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3243,6 +3247,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3448,6 +3456,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3862,6 +3874,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7437,6 +7452,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8453,6 +8473,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8772,6 +8797,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.15.6 b/LINUX/final-patches/intel--igb--5.15.6 new file mode 100644 index 000000000..3fb6dcca5 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.15.6 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index f72e6da..c387ecd 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 9d3192f..10ed047 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -249,6 +249,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3243,6 +3247,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3448,6 +3456,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3862,6 +3874,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7437,6 +7452,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8453,6 +8473,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8772,6 +8797,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.16.10 b/LINUX/final-patches/intel--igb--5.16.10 new file mode 100644 index 000000000..8054228b7 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.16.10 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 247e336..33aad62 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 09a7478..475059f 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -249,6 +249,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3245,6 +3249,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3450,6 +3458,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3862,6 +3874,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7437,6 +7452,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8453,6 +8473,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8772,6 +8797,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.18.7 b/LINUX/final-patches/intel--igb--5.18.7 new file mode 100644 index 000000000..4eeeb5b96 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.18.7 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/src/Makefile +index 982cd53..d54cb7a 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/src/igb_main.c +index 07d1b87..e8bc3e9 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -249,6 +249,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3245,6 +3249,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3451,6 +3459,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3867,6 +3879,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7442,6 +7457,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8458,6 +8478,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8777,6 +8802,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.19.4 b/LINUX/final-patches/intel--igb--5.19.4 new file mode 100644 index 000000000..f03a5cee8 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.19.4 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/src/Makefile +index f09ad2a..6a2d070 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/src/igb_main.c +index 13469f3..aa59b2f 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -252,6 +252,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3187,6 +3191,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3393,6 +3401,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3809,6 +3821,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7384,6 +7399,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8400,6 +8420,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8719,6 +8744,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.3.5.10 b/LINUX/final-patches/intel--igb--5.3.5.10 new file mode 100644 index 000000000..28975852e --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.3.5.10 @@ -0,0 +1,135 @@ +diff --git a/igb/Makefile b/igb/Makefile +index e3bd4f3..ef900a4 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -28,7 +28,7 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + + define igb-y + igb_main.o +@@ -46,19 +46,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -115,9 +115,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 3ee1ec7..3746d0b 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -258,6 +258,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3052,6 +3056,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3255,6 +3263,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3663,6 +3675,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7211,6 +7226,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8225,6 +8245,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8543,6 +8568,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.3.5.12 b/LINUX/final-patches/intel--igb--5.3.5.12 new file mode 100644 index 000000000..fc627211b --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.3.5.12 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index e3bd4f3..2f6895a 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -46,19 +46,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -115,9 +115,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 6c9b112..0c65b25 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -258,6 +258,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3061,6 +3065,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3264,6 +3272,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3672,6 +3684,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7226,6 +7241,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8240,6 +8260,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8558,6 +8583,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.3.5.15 b/LINUX/final-patches/intel--igb--5.3.5.15 new file mode 100644 index 000000000..09b17a9cd --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.3.5.15 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index e3bd4f3..2f6895a 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -46,19 +46,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -115,9 +115,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index f6faafc..0bb4c07 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -258,6 +258,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3061,6 +3065,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3264,6 +3272,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3672,6 +3684,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7226,6 +7241,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8240,6 +8260,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8558,6 +8583,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.3.5.18 b/LINUX/final-patches/intel--igb--5.3.5.18 new file mode 100644 index 000000000..21365c277 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.3.5.18 @@ -0,0 +1,134 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 02d49bb..1267d14 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -46,19 +46,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -114,6 +114,9 @@ sparse: clean + ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Build manfiles + manfile: + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz +diff --git a/igb/igb_main.c b/igb/igb_main.c +index b98cfa6..928cb8e 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -258,6 +258,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3073,6 +3077,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3276,6 +3284,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3686,6 +3698,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7240,6 +7255,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8256,6 +8276,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8575,6 +8600,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.3.5.20 b/LINUX/final-patches/intel--igb--5.3.5.20 new file mode 100644 index 000000000..9523f1cf9 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.3.5.20 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 02d49bb..1c88549 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -46,19 +46,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -115,9 +115,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index f7f9095..fde3f7c 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -258,6 +258,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3071,6 +3075,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3274,6 +3282,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3684,6 +3696,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7236,6 +7251,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8252,6 +8272,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8571,6 +8596,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.3.5.22 b/LINUX/final-patches/intel--igb--5.3.5.22 new file mode 100644 index 000000000..709140d7d --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.3.5.22 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 02d49bb..1c88549 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -46,19 +46,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -115,9 +115,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 5e06587..de17ae2 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -258,6 +258,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3071,6 +3075,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3274,6 +3282,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3684,6 +3696,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7236,6 +7251,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8252,6 +8272,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8571,6 +8596,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.3.5.36 b/LINUX/final-patches/intel--igb--5.3.5.36 new file mode 100644 index 000000000..06d8999cc --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.3.5.36 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 924ae5b..0a5f720 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -25,19 +25,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -94,9 +94,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 79a3c38..a9a8be5 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -237,6 +237,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3178,6 +3182,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3383,6 +3391,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3795,6 +3807,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7467,6 +7482,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8483,6 +8503,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8802,6 +8827,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.3.5.39 b/LINUX/final-patches/intel--igb--5.3.5.39 new file mode 100644 index 000000000..c8762a71e --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.3.5.39 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 924ae5b..0a5f720 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -25,19 +25,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -94,9 +94,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index afb92bc..33a6a74 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -237,6 +237,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3183,6 +3187,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3388,6 +3396,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3800,6 +3812,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7480,6 +7495,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8496,6 +8516,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8815,6 +8840,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.3.5.4 b/LINUX/final-patches/intel--igb--5.3.5.4 index d66a00dc7..e26d7d09a 100644 --- a/LINUX/final-patches/intel--igb--5.3.5.4 +++ b/LINUX/final-patches/intel--igb--5.3.5.4 @@ -31,20 +31,20 @@ index 8e962f7..2c10939 100644 install -D -m 644 $(TARGET) $(INSTALL_MOD_PATH)$(INSTDIR)/$(TARGET) ifeq (,$(INSTALL_MOD_PATH)) diff --git a/igb/igb_main.c b/igb/igb_main.c -index 2dff0f4..d848e34 100644 +index 2dff0f4..251b755 100644 --- a/igb/igb_main.c +++ b/igb/igb_main.c -@@ -318,6 +318,10 @@ static int debug = NETIF_MSG_DRV | NETIF_MSG_PROBE; - module_param(debug, int, 0); - MODULE_PARM_DESC(debug, "Debug level (0=none, ..., 16=all)"); +@@ -259,6 +259,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); +#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) +#include +#endif + - /** - * igb_init_module - Driver Registration Routine - * + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, @@ -3041,6 +3045,10 @@ static int igb_probe(struct pci_dev *pdev, /* carrier off reporting is important to ethtool even BEFORE open */ netif_carrier_off(netdev); diff --git a/LINUX/final-patches/intel--igb--5.3.5.42 b/LINUX/final-patches/intel--igb--5.3.5.42 new file mode 100644 index 000000000..ce7ad797b --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.3.5.42 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 924ae5b..0a5f720 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -25,19 +25,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -94,9 +94,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index a8ad07b..60568f1 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -237,6 +237,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3180,6 +3184,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3385,6 +3393,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3797,6 +3809,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7477,6 +7492,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8493,6 +8513,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8812,6 +8837,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.3.5.61 b/LINUX/final-patches/intel--igb--5.3.5.61 new file mode 100644 index 000000000..b36da908e --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.3.5.61 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 811a634..2559d39 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -25,19 +25,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -106,9 +106,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 12826ba..9288205 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -241,6 +241,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3203,6 +3207,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3408,6 +3416,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3820,6 +3832,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7505,6 +7520,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8521,6 +8541,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8840,6 +8865,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.3.6 b/LINUX/final-patches/intel--igb--5.3.6 new file mode 100644 index 000000000..b840f93f0 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.3.6 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 811a634..2559d39 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -25,19 +25,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -106,9 +106,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 54ec261..9252c05 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -241,6 +241,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3203,6 +3207,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3408,6 +3416,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3820,6 +3832,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7505,6 +7520,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8521,6 +8541,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8840,6 +8865,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.4.6 b/LINUX/final-patches/intel--igb--5.4.6 new file mode 100644 index 000000000..ec1420cdb --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.4.6 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 811a634..2559d39 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -25,19 +25,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -106,9 +106,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 1517b38..c87d686 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -241,6 +241,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3209,6 +3213,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3414,6 +3422,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3826,6 +3838,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7511,6 +7526,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8527,6 +8547,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8846,6 +8871,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.5.2 b/LINUX/final-patches/intel--igb--5.5.2 new file mode 100644 index 000000000..1ad59fbc0 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.5.2 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 811a634..2559d39 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -25,19 +25,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -106,9 +106,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 0b53658..25d4630 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -241,6 +241,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3209,6 +3213,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3414,6 +3422,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3826,6 +3838,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7511,6 +7526,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8527,6 +8547,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8846,6 +8871,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.7.2 b/LINUX/final-patches/intel--igb--5.7.2 new file mode 100644 index 000000000..be96e5e7f --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.7.2 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 2ff71a6..6bbeb56 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -25,19 +25,19 @@ define igb-y + e1000_82575.o + e1000_i210.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -106,9 +106,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index d9ec4b2..6590a60 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -241,6 +241,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3209,6 +3213,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3414,6 +3422,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3826,6 +3838,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7511,6 +7526,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8527,6 +8547,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8846,6 +8871,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.8.5 b/LINUX/final-patches/intel--igb--5.8.5 new file mode 100644 index 000000000..e58675fd5 --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.8.5 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 682db41..303f09b 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 75d48c1..00fdc04 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -249,6 +249,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3238,6 +3242,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3443,6 +3451,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3857,6 +3869,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7430,6 +7445,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8446,6 +8466,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8765,6 +8790,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--igb--5.9.3 b/LINUX/final-patches/intel--igb--5.9.3 new file mode 100644 index 000000000..90ee9259d --- /dev/null +++ b/LINUX/final-patches/intel--igb--5.9.3 @@ -0,0 +1,138 @@ +diff --git a/igb/Makefile b/igb/Makefile +index 682db41..303f09b 100644 +--- a/igb/Makefile ++++ b/igb/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) Gigabit Ethernet Linux Driver + # + +-obj-$(CONFIG_IGB) += igb.o ++obj-$(CONFIG_IGB) += igb$(NETMAP_DRIVER_SUFFIX).o + +-define igb-y ++define igb$(NETMAP_DRIVER_SUFFIX)-y + igb_main.o + e1000_api.o + igb_ethtool.o +@@ -26,19 +26,19 @@ define igb-y + e1000_i210.o + e1000_base.o + endef +-igb-y := $(strip ${igb-y}) ++igb$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${igb$(NETMAP_DRIVER_SUFFIX)-y}) + +-igb-${CONFIG_DEBUG_FS} += igb_debugfs.o ++igb$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += igb_debugfs.o + +-igb-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o ++igb$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += igb_ptp.o + + +-igb-y += kcompat.o ++igb$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := igb ++DRIVER := igb$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ @touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/igb/igb_main.c b/igb/igb_main.c +index d551598..9512f22 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -249,6 +249,10 @@ static struct pci_error_handlers igb_err_handler = { + static void igb_init_fw(struct igb_adapter *adapter); + static void igb_init_dmac(struct igb_adapter *adapter, u32 pba); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + static struct pci_driver igb_driver = { + .name = igb_driver_name, + .id_table = igb_pci_tbl, +@@ -3238,6 +3242,10 @@ static int igb_probe(struct pci_dev *pdev, + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef IGB_DCA + if (dca_add_requester(&pdev->dev) == E1000_SUCCESS) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3443,6 +3451,10 @@ static void igb_remove(struct pci_dev *pdev) + */ + igb_release_hw_control(adapter); + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igb_clear_interrupt_scheme(adapter); +@@ -3857,6 +3869,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + E1000_WRITE_REG(hw, E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -7430,6 +7445,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -8446,6 +8466,11 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) + unsigned int total_bytes = 0, total_packets = 0; + u16 cleaned_count = igb_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + do { + struct igb_rx_buffer *rx_buffer; + union e1000_adv_rx_desc *rx_desc; +@@ -8765,6 +8790,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + struct igb_rx_buffer *bi; + u16 i = rx_ring->next_to_use; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/intel--ixgbe--4.4.6 b/LINUX/final-patches/intel--ixgbe--4.4.6 new file mode 100644 index 000000000..477113f29 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--4.4.6 @@ -0,0 +1,202 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index c49cba8..546435f 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -49,24 +49,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 202f595..ec4f8d8 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -709,6 +709,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -727,6 +744,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1656,7 +1684,7 @@ static bool ixgbe_add_rx_frag(struct ixgbe_ring *rx_ring, + /* Even if we own the page, we are not allowed to use atomic_set() + * This would break get_page_unless_zero() users. + */ +- atomic_inc(&page->_count); ++ atomic_inc(&page->NETMAP_LINUX_PAGE_COUNT); + + return true; + } +@@ -1767,6 +1795,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + #endif /* CONFIG_FCOE */ + u16 cleaned_count = ixgbe_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + do { + union ixgbe_adv_rx_desc *rx_desc; + struct sk_buff *skb; +@@ -2845,6 +2883,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + + clear_bit(__IXGBE_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3408,6 +3450,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -10104,6 +10150,10 @@ no_info_string: + IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL, + true); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -10149,6 +10199,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + +diff --git a/ixgbe/kcompat.h b/ixgbe/kcompat.h +index 048157a..2349540 100644 +--- a/ixgbe/kcompat.h ++++ b/ixgbe/kcompat.h +@@ -25,6 +25,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else +@@ -4880,7 +4882,7 @@ static inline void csum_replace_by_diff(__sum16 *sum, __wsum diff) + + static inline void page_ref_inc(struct page *page) + { +- atomic_inc(&page->_count); ++ atomic_inc(&page->NETMAP_LINUX_PAGE_COUNT); + } + + #endif /* 4.6.0 */ diff --git a/LINUX/final-patches/intel--ixgbe--4.5.4 b/LINUX/final-patches/intel--ixgbe--4.5.4 index 0c09cc074..135a3d8d1 100644 --- a/LINUX/final-patches/intel--ixgbe--4.5.4 +++ b/LINUX/final-patches/intel--ixgbe--4.5.4 @@ -62,7 +62,7 @@ index c49cba8..546435f 100644 clean: @+$(call devkernelbuild,clean) diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index 06017ea..5b220d4 100644 +index 06017ea..4974b37 100644 --- a/ixgbe/ixgbe_main.c +++ b/ixgbe/ixgbe_main.c @@ -747,6 +747,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -124,17 +124,18 @@ index 06017ea..5b220d4 100644 do { union ixgbe_adv_rx_desc *rx_desc; struct sk_buff *skb; -@@ -2927,6 +2965,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - hw_dbg(hw, "Could not enable Tx Queue %d\n", reg_idx); +@@ -2912,6 +2950,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + + clear_bit(__IXGBE_HANG_CHECK_ARMED, &ring->state); + +#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); +#endif /* DEV_NETMAP */ - } ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); - static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -3476,6 +3517,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, +@@ -3476,6 +3518,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); ixgbe_rx_desc_queue_enable(adapter, ring); @@ -145,7 +146,7 @@ index 06017ea..5b220d4 100644 ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); } -@@ -10436,6 +10481,10 @@ no_info_string: +@@ -10436,6 +10482,10 @@ no_info_string: hw->mac.ops.setup_eee(hw, eee_enable); } @@ -156,13 +157,13 @@ index 06017ea..5b220d4 100644 return 0; err_register: -@@ -10481,6 +10530,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) +@@ -10481,6 +10531,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) return; netdev = adapter->netdev; + +#ifdef DEV_NETMAP -+ netmap_detach(netdev); ++ ixgbe_netmap_detach(adapter); +#endif /* DEV_NETMAP */ + #ifdef HAVE_IXGBE_DEBUG_FS diff --git a/LINUX/final-patches/intel--ixgbe--5.0.4 b/LINUX/final-patches/intel--ixgbe--5.0.4 index dc38053a1..84495c50e 100644 --- a/LINUX/final-patches/intel--ixgbe--5.0.4 +++ b/LINUX/final-patches/intel--ixgbe--5.0.4 @@ -62,7 +62,7 @@ index a3cc895..a038975 100644 clean: @+$(call devkernelbuild,clean) diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index 83c6250..eb9a1a1 100644 +index 83c6250..5da5967 100644 --- a/ixgbe/ixgbe_main.c +++ b/ixgbe/ixgbe_main.c @@ -752,6 +752,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); @@ -124,17 +124,18 @@ index 83c6250..eb9a1a1 100644 do { union ixgbe_adv_rx_desc *rx_desc; struct sk_buff *skb; -@@ -2932,6 +2970,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - hw_dbg(hw, "Could not enable Tx Queue %d\n", reg_idx); +@@ -2917,6 +2955,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + + clear_bit(__IXGBE_HANG_CHECK_ARMED, &ring->state); + +#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); +#endif /* DEV_NETMAP */ - } ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); - static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -3481,6 +3522,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, +@@ -3481,6 +3523,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); ixgbe_rx_desc_queue_enable(adapter, ring); @@ -145,7 +146,7 @@ index 83c6250..eb9a1a1 100644 ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); } -@@ -10444,6 +10489,10 @@ no_info_string: +@@ -10444,6 +10490,10 @@ no_info_string: hw->mac.ops.setup_eee(hw, eee_enable); } @@ -156,13 +157,13 @@ index 83c6250..eb9a1a1 100644 return 0; err_register: -@@ -10489,6 +10538,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) +@@ -10489,6 +10539,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) return; netdev = adapter->netdev; + +#ifdef DEV_NETMAP -+ netmap_detach(netdev); ++ ixgbe_netmap_detach(adapter); +#endif /* DEV_NETMAP */ + #ifdef HAVE_IXGBE_DEBUG_FS diff --git a/LINUX/final-patches/intel--ixgbe--5.1.3 b/LINUX/final-patches/intel--ixgbe--5.1.3 new file mode 100644 index 000000000..f35ff7c76 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.1.3 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index a3cc895..a038975 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -49,24 +49,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index fe4291e..0b8f0e4 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -753,6 +753,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -771,6 +788,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1946,6 +1974,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + #endif /* CONFIG_FCOE */ + u16 cleaned_count = ixgbe_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + do { + union ixgbe_adv_rx_desc *rx_desc; + struct sk_buff *skb; +@@ -3207,6 +3245,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + + clear_bit(__IXGBE_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3798,6 +3840,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -11114,6 +11160,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -11159,6 +11209,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.10.2 b/LINUX/final-patches/intel--ixgbe--5.10.2 new file mode 100644 index 000000000..7302b57ab --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.10.2 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 01e67d7..91f53e1 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index c4eb984..eeb6ff6 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -711,6 +711,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -730,6 +747,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2208,6 +2236,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3688,6 +3726,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4364,6 +4406,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_umem) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -12983,6 +13031,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13038,6 +13090,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.11.3 b/LINUX/final-patches/intel--ixgbe--5.11.3 new file mode 100644 index 000000000..ad7f1900e --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.11.3 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index adccdaa..137c98f 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 3205fc0..ce0b0b5 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -715,6 +715,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -734,6 +751,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2212,6 +2240,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3692,6 +3730,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4384,6 +4426,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -13011,6 +13059,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13067,6 +13119,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.12.5 b/LINUX/final-patches/intel--ixgbe--5.12.5 new file mode 100644 index 000000000..c08963d1e --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.12.5 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index adccdaa..137c98f 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index de15aae..94974ca 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -716,6 +716,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -735,6 +752,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2213,6 +2241,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3731,6 +3769,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4422,6 +4464,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -13130,6 +13178,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13186,6 +13238,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.13.4 b/LINUX/final-patches/intel--ixgbe--5.13.4 new file mode 100644 index 000000000..4ec4c688d --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.13.4 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index adccdaa..137c98f 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 57db217..b806707 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -716,6 +716,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -735,6 +752,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2213,6 +2241,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3731,6 +3769,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4422,6 +4464,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -13172,6 +13220,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13228,6 +13280,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.14.6 b/LINUX/final-patches/intel--ixgbe--5.14.6 new file mode 100644 index 000000000..da08a2996 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.14.6 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index adccdaa..137c98f 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 29e2075..7012791 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -719,6 +719,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -738,6 +755,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2228,6 +2256,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3742,6 +3780,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4433,6 +4475,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -13205,6 +13253,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13261,6 +13313,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.15.2 b/LINUX/final-patches/intel--ixgbe--5.15.2 new file mode 100644 index 000000000..c7bf35978 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.15.2 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 0bd095f..5b38247 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 82ec4c3..95daed1 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -719,6 +719,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -738,6 +755,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2228,6 +2256,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3742,6 +3780,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4433,6 +4475,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -13204,6 +13252,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13260,6 +13312,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.16.5 b/LINUX/final-patches/intel--ixgbe--5.16.5 new file mode 100644 index 000000000..39baa92f2 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.16.5 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 0bd095f..5b38247 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 0450844..66d6a37 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -719,6 +719,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -738,6 +755,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2228,6 +2256,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3742,6 +3780,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4433,6 +4475,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -13204,6 +13252,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13260,6 +13312,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.17.1 b/LINUX/final-patches/intel--ixgbe--5.17.1 new file mode 100644 index 000000000..3a1074caa --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.17.1 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 0bd095f..5b38247 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 977a012..8c44e44 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -719,6 +719,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -738,6 +755,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2229,6 +2257,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3742,6 +3780,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4434,6 +4476,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -13227,6 +13275,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13283,6 +13335,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.18.11 b/LINUX/final-patches/intel--ixgbe--5.18.11 new file mode 100644 index 000000000..206a0cefd --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.18.11 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 884cbf7..4ffc802 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 9fa2109..0c36a59 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -719,6 +719,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -738,6 +755,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2229,6 +2257,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3742,6 +3780,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4434,6 +4476,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -13250,6 +13298,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13306,6 +13358,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.18.13 b/LINUX/final-patches/intel--ixgbe--5.18.13 new file mode 100644 index 000000000..d2928d78e --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.18.13 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 884cbf7..4ffc802 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index df8e130..6c2dc2b 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -719,6 +719,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -738,6 +755,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2229,6 +2257,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3742,6 +3780,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4434,6 +4476,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -13250,6 +13298,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13306,6 +13358,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.18.6 b/LINUX/final-patches/intel--ixgbe--5.18.6 new file mode 100644 index 000000000..f216fc089 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.18.6 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 2a2ffe2..adb10f4 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index effcc4c..c869fbe 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -719,6 +719,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -738,6 +755,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2229,6 +2257,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3742,6 +3780,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4434,6 +4476,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -13242,6 +13290,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13298,6 +13350,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.19.6 b/LINUX/final-patches/intel--ixgbe--5.19.6 new file mode 100644 index 000000000..8257d24ca --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.19.6 @@ -0,0 +1,174 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 1d4c83e..65cf773 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,25 +29,25 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -120,9 +120,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index bf5eddd..97dc25e 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -721,6 +721,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_reset_pf_report - reset pf and print reset report + * @tx_ring: tx ring number +@@ -774,6 +791,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2246,6 +2274,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3759,6 +3797,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4451,6 +4493,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -13288,6 +13336,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13346,6 +13398,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.19.9 b/LINUX/final-patches/intel--ixgbe--5.19.9 new file mode 100644 index 000000000..fcc8fa08f --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.19.9 @@ -0,0 +1,174 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 1d4c83e..65cf773 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,25 +29,25 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -120,9 +120,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index b55418c..a342914 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -721,6 +721,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_reset_pf_report - reset pf and print reset report + * @tx_ring: tx ring number +@@ -774,6 +791,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2246,6 +2274,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3759,6 +3797,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4451,6 +4493,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -13289,6 +13337,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13347,6 +13399,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.2.1 b/LINUX/final-patches/intel--ixgbe--5.2.1 new file mode 100644 index 000000000..8bcdd059e --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.2.1 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index a3cc895..a038975 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -49,24 +49,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 0f65c2e..26a5de6 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -753,6 +753,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -771,6 +788,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2053,6 +2081,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + #endif /* CONFIG_FCOE */ + u16 cleaned_count = ixgbe_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + struct ixgbe_rx_buffer *rx_buffer; +@@ -3336,6 +3374,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + + clear_bit(__IXGBE_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3979,6 +4021,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -11400,6 +11446,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -11445,6 +11495,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.2.3 b/LINUX/final-patches/intel--ixgbe--5.2.3 new file mode 100644 index 000000000..b78cedd0d --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.2.3 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index a3cc895..a038975 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -49,24 +49,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 68bead6..dc29ca1 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -753,6 +753,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -771,6 +788,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2053,6 +2081,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + #endif /* CONFIG_FCOE */ + u16 cleaned_count = ixgbe_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + struct ixgbe_rx_buffer *rx_buffer; +@@ -3336,6 +3374,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + + clear_bit(__IXGBE_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3979,6 +4021,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -11400,6 +11446,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -11445,6 +11495,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.2.4 b/LINUX/final-patches/intel--ixgbe--5.2.4 new file mode 100644 index 000000000..cab246e69 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.2.4 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index a3cc895..a038975 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -49,24 +49,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index c7a1499..eaf8c4a 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -753,6 +753,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -771,6 +788,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2053,6 +2081,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + #endif /* CONFIG_FCOE */ + u16 cleaned_count = ixgbe_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + struct ixgbe_rx_buffer *rx_buffer; +@@ -3336,6 +3374,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + + clear_bit(__IXGBE_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3979,6 +4021,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -11400,6 +11446,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -11445,6 +11495,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.20.10 b/LINUX/final-patches/intel--ixgbe--5.20.10 new file mode 100644 index 000000000..d5bf03bff --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.20.10 @@ -0,0 +1,178 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index b6eb61f..66cc02c 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -32,29 +32,29 @@ define ixgbe-y + ixgbe_devlink.o + ixgbe_fw_update.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW + ixgbe-y += kcompat_pldmfw.o + endif + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 09081f5..94c7429 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -778,6 +778,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_reset_pf_report - reset pf and print reset report + * @tx_ring: tx ring number +@@ -943,6 +960,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2417,6 +2445,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -4643,6 +4681,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -5337,6 +5379,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -14760,6 +14808,10 @@ no_info_string: + #endif /* !HAVE_DEVLINK_PARAMS_PUBLISH */ + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_devlink_register: +@@ -14853,6 +14905,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + } + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.3.3 b/LINUX/final-patches/intel--ixgbe--5.3.3 new file mode 100644 index 000000000..01d214f5a --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.3.3 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index a3cc895..a038975 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -49,24 +49,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 70ba606..a33879e 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -733,6 +733,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -751,6 +768,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2032,6 +2060,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + #endif /* CONFIG_FCOE */ + u16 cleaned_count = ixgbe_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + struct ixgbe_rx_buffer *rx_buffer; +@@ -3319,6 +3357,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3966,6 +4008,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -11481,6 +11527,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -11526,6 +11576,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.3.4 b/LINUX/final-patches/intel--ixgbe--5.3.4 new file mode 100644 index 000000000..b9f3d73f6 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.3.4 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index a3cc895..a038975 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -49,24 +49,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 7b18584..6bee72d 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -734,6 +734,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -752,6 +769,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2033,6 +2061,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + #endif /* CONFIG_FCOE */ + u16 cleaned_count = ixgbe_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + struct ixgbe_rx_buffer *rx_buffer; +@@ -3320,6 +3358,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3967,6 +4009,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -11232,6 +11278,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -11277,6 +11327,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.3.5 b/LINUX/final-patches/intel--ixgbe--5.3.5 new file mode 100644 index 000000000..812b8b8b0 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.3.5 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index a3cc895..a038975 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -49,24 +49,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 6c7e4cd..6e34fb1 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -734,6 +734,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -752,6 +769,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2033,6 +2061,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + #endif /* CONFIG_FCOE */ + u16 cleaned_count = ixgbe_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + struct ixgbe_rx_buffer *rx_buffer; +@@ -3320,6 +3358,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3967,6 +4009,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -11232,6 +11278,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -11277,6 +11327,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.3.6 b/LINUX/final-patches/intel--ixgbe--5.3.6 new file mode 100644 index 000000000..6d666377f --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.3.6 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 545489a..e085666 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -49,24 +49,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 83076bf..7a1fd02 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -734,6 +734,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -752,6 +769,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2033,6 +2061,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + #endif /* CONFIG_FCOE */ + u16 cleaned_count = ixgbe_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + struct ixgbe_rx_buffer *rx_buffer; +@@ -3320,6 +3358,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3967,6 +4009,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -11231,6 +11277,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -11276,6 +11326,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.3.7 b/LINUX/final-patches/intel--ixgbe--5.3.7 new file mode 100644 index 000000000..f656c7197 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.3.7 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 545489a..e085666 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -49,24 +49,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index d1e70f1..392d1ab 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -734,6 +734,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -752,6 +769,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2033,6 +2061,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + #endif /* CONFIG_FCOE */ + u16 cleaned_count = ixgbe_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + struct ixgbe_rx_buffer *rx_buffer; +@@ -3320,6 +3358,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3967,6 +4009,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -11245,6 +11291,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -11290,6 +11340,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.3.8 b/LINUX/final-patches/intel--ixgbe--5.3.8 new file mode 100644 index 000000000..f94a48c84 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.3.8 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 545489a..e085666 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -28,9 +28,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -49,24 +49,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 0191ee2..5093914 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -734,6 +734,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -752,6 +769,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2033,6 +2061,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + #endif /* CONFIG_FCOE */ + u16 cleaned_count = ixgbe_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + struct ixgbe_rx_buffer *rx_buffer; +@@ -3320,6 +3358,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3967,6 +4009,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -11276,6 +11322,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -11321,6 +11371,11 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.5.1 b/LINUX/final-patches/intel--ixgbe--5.5.1 new file mode 100644 index 000000000..baf559795 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.5.1 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 0bf12f5..fa05036 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -28,24 +28,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -106,9 +106,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 6c4e051..2c64477 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -694,6 +694,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -713,6 +730,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2192,6 +2220,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3624,6 +3662,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4245,6 +4287,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -12094,6 +12140,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -12143,6 +12193,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.5.2 b/LINUX/final-patches/intel--ixgbe--5.5.2 new file mode 100644 index 000000000..b296b3331 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.5.2 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 0bf12f5..fa05036 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -28,24 +28,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -106,9 +106,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 903d688..bf03742 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -694,6 +694,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -713,6 +730,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2192,6 +2220,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3624,6 +3662,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4245,6 +4287,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -12102,6 +12148,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -12151,6 +12201,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.5.3 b/LINUX/final-patches/intel--ixgbe--5.5.3 new file mode 100644 index 000000000..d915b0e24 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.5.3 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 0bf12f5..fa05036 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -28,24 +28,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -106,9 +106,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index b39b93e..207dadb 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -694,6 +694,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -713,6 +730,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2192,6 +2220,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3624,6 +3662,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4245,6 +4287,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -12099,6 +12145,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -12148,6 +12198,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.5.5 b/LINUX/final-patches/intel--ixgbe--5.5.5 new file mode 100644 index 000000000..8dba94d53 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.5.5 @@ -0,0 +1,171 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 934a9ee..a375068 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -28,24 +28,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -106,9 +106,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 2f1eb02..d29547d 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -694,6 +694,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -713,6 +730,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2192,6 +2220,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3624,6 +3662,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4245,6 +4287,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + } + +@@ -12099,6 +12145,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -12148,6 +12198,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.6.1 b/LINUX/final-patches/intel--ixgbe--5.6.1 new file mode 100644 index 000000000..c5a46c51a --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.6.1 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index e613859..8ca5eaa 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index c2bcd19..ce130fd 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -706,6 +706,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -725,6 +742,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2189,6 +2217,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3656,6 +3694,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4332,6 +4374,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_umem) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -12896,6 +12944,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -12951,6 +13003,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.6.3 b/LINUX/final-patches/intel--ixgbe--5.6.3 new file mode 100644 index 000000000..10c7afaf2 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.6.3 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index e613859..8ca5eaa 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index af1af52..0be976d 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -706,6 +706,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -725,6 +742,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2189,6 +2217,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3656,6 +3694,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4332,6 +4374,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_umem) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -12894,6 +12942,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -12949,6 +13001,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.6.5 b/LINUX/final-patches/intel--ixgbe--5.6.5 new file mode 100644 index 000000000..69b3443d5 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.6.5 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index e613859..8ca5eaa 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -107,9 +107,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index a6971c3..87a4138 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -706,6 +706,23 @@ struct ixgbe_adapter *adapter = netdev_priv(netdev); + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -725,6 +742,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2189,6 +2217,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3656,6 +3694,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4332,6 +4374,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_umem) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -12900,6 +12948,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -12955,6 +13007,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.7.1 b/LINUX/final-patches/intel--ixgbe--5.7.1 new file mode 100644 index 000000000..4a523e6bf --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.7.1 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 01e67d7..91f53e1 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index f6eb944..82325f1 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -711,6 +711,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -730,6 +747,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2194,6 +2222,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3661,6 +3699,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4337,6 +4379,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_umem) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -12916,6 +12964,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -12971,6 +13023,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.8.1 b/LINUX/final-patches/intel--ixgbe--5.8.1 new file mode 100644 index 000000000..a563b9161 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.8.1 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 01e67d7..91f53e1 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 1e3ac19..5ea92e0 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -711,6 +711,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -730,6 +747,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2194,6 +2222,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3661,6 +3699,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4337,6 +4379,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_umem) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -12940,6 +12988,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -12995,6 +13047,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--5.9.4 b/LINUX/final-patches/intel--ixgbe--5.9.4 new file mode 100644 index 000000000..e5d6c51d5 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--5.9.4 @@ -0,0 +1,173 @@ +diff --git a/ixgbe/Makefile b/ixgbe/Makefile +index 01e67d7..91f53e1 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -29,24 +29,24 @@ define ixgbe-y + ixgbe_x540.o + ixgbe_x550.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -119,9 +119,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 1123fe5..9578b42 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -711,6 +711,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information +@@ -730,6 +747,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2203,6 +2231,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -3670,6 +3708,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4346,6 +4388,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_umem) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -12957,6 +13005,10 @@ no_info_string: + hw->mac.ops.setup_eee(hw, eee_enable); + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -13012,6 +13064,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--6.0.5 b/LINUX/final-patches/intel--ixgbe--6.0.5 new file mode 100644 index 000000000..606f813e8 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--6.0.5 @@ -0,0 +1,178 @@ +diff --git a/ixgbe/Makefile b/src/Makefile +index 84a7f13..268a300 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -32,29 +32,29 @@ define ixgbe-y + ixgbe_devlink.o + ixgbe_fw_update.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW + ixgbe-y += kcompat_pldmfw.o + endif + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/src/ixgbe_main.c +index 5bd35bd..20c6429 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -779,6 +779,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_reset_pf_report - reset pf and print reset report + * @tx_ring: tx ring number +@@ -944,6 +961,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2418,6 +2446,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -4499,6 +4537,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -5193,6 +5235,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -14610,6 +14658,10 @@ no_info_string: + #endif /* !HAVE_DEVLINK_PARAMS_PUBLISH */ + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_devlink_register: +@@ -14694,6 +14746,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + ixgbe_pf_fwlog_deinit(adapter); + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--6.0.6 b/LINUX/final-patches/intel--ixgbe--6.0.6 new file mode 100644 index 000000000..9a28af638 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--6.0.6 @@ -0,0 +1,178 @@ +diff --git a/ixgbe/Makefile b/src/Makefile +index 84a7f13..268a300 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,9 +7,9 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbe-y ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -32,29 +32,29 @@ define ixgbe-y + ixgbe_devlink.o + ixgbe_fw_update.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o + +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o + +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o + +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o + +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW + ixgbe-y += kcompat_pldmfw.o + endif + +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbe ++DRIVER := ixgbe$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -127,9 +127,12 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/src/ixgbe_main.c +index ed3e43b..5db5331 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -779,6 +779,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_reset_pf_report - reset pf and print reset report + * @tx_ring: tx ring number +@@ -944,6 +961,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2418,6 +2446,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -4499,6 +4537,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -5193,6 +5235,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -14610,6 +14658,10 @@ no_info_string: + #endif /* !HAVE_DEVLINK_PARAMS_PUBLISH */ + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_devlink_register: +@@ -14694,6 +14746,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + ixgbe_pf_fwlog_deinit(adapter); + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbe--6.1.6 b/LINUX/final-patches/intel--ixgbe--6.1.6 new file mode 100644 index 000000000..5027ce4f7 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbe--6.1.6 @@ -0,0 +1,168 @@ +diff --git a/ixgbe/Makefile b/src/Makefile +index 057b034..5b9092c 100644 +--- a/ixgbe/Makefile ++++ b/ixgbe/Makefile +@@ -7,8 +7,8 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Linux Network Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbe.o +-define ixgbe-y ++obj-$(CONFIG_IXGBE) += ixgbe$(NETMAP_DRIVER_SUFFIX).o ++define ixgbe$(NETMAP_DRIVER_SUFFIX)-y + ixgbe_main.o + ixgbe_api.o + ixgbe_common.o +@@ -31,19 +31,19 @@ define ixgbe-y + ixgbe_devlink.o + ixgbe_fw_update.o + endef +-ixgbe-y := $(strip ${ixgbe-y}) +- +-ixgbe-${CONFIG_DCB} += ixgbe_dcb_nl.o +-ixgbe-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o +-ixgbe-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o +-ixgbe-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp_e600.o +-ixgbe-${CONFIG_SYSFS} += ixgbe_sysfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbe$(NETMAP_DRIVER_SUFFIX)-y}) ++ ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DCB} += ixgbe_dcb_nl.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_DEBUG_FS} += ixgbe_debugfs.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_FCOE:m=y} += ixgbe_fcoe.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PTP_1588_CLOCK:m=y) += ixgbe_ptp_e600.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-${CONFIG_SYSFS} += ixgbe_sysfs.o + # Use kcompat pldmfw.c if kernel does not provide CONFIG_PLDMFW + ifndef CONFIG_PLDMFW +-ixgbe-y += kcompat_pldmfw.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat_pldmfw.o + endif +-ixgbe-y += kcompat.o ++ixgbe$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + else # ifneq($(KERNELRELEASE),) + # normal makefile + +@@ -116,8 +116,10 @@ ccc: clean + @+$(call devkernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz ++../$(DRIVER).$(MANSECTION): ++ touch $@ + # Clean the module subdirectories + clean: + @+$(call devkernelbuild,clean) +diff --git a/ixgbe/ixgbe_main.c b/src/ixgbe_main.c +index e41b4a8..2fa2a6e 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -805,6 +805,23 @@ static void ixgbe_tx_timeout(struct net_device *netdev) + } + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif ++ + /** + * ixgbe_reset_pf_report - reset pf and print reset report + * @tx_ring: tx ring number +@@ -980,6 +997,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2497,6 +2525,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -4587,6 +4625,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -5280,6 +5322,12 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++ ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_AF_XDP_ZC_SUPPORT + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); +@@ -15170,6 +15218,11 @@ no_info_string: + ixgbe_ptp_init_e600(adapter); + + #endif /* HAVE_PTP_1588_CLOCK && LINKVILLE_HW */ ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_devlink_register: +@@ -15259,6 +15312,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + ixgbe_pf_fwlog_deinit(adapter); + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef HAVE_IXGBE_DEBUG_FS + ixgbe_dbg_adapter_exit(adapter); + diff --git a/LINUX/final-patches/intel--ixgbevf--3.2.2 b/LINUX/final-patches/intel--ixgbevf--3.2.2 new file mode 100644 index 000000000..70dfd2540 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--3.2.2 @@ -0,0 +1,178 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index b50a61d..e8aa31c 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -28,23 +28,23 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbevf.o ++obj-$(CONFIG_IXGBE) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_param.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) + +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -91,9 +91,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index bf6cc35..934c2ae 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -303,6 +303,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -322,6 +339,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1140,6 +1168,17 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ ++ + do { + union ixgbe_adv_rx_desc *rx_desc; + +@@ -1762,6 +1801,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1943,6 +1986,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4767,8 +4814,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + cards_found++; + return 0; + +@@ -4809,6 +4858,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVE, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 683975b..2647519 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -25,6 +25,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else +@@ -4793,7 +4795,7 @@ static inline void csum_replace_by_diff(__sum16 *sum, __wsum diff) + + static inline void page_ref_inc(struct page *page) + { +- atomic_inc(&page->_count); ++ atomic_inc(&page->NETMAP_LINUX_PAGE_COUNT); + } + + #endif /* 4.6.0 */ diff --git a/LINUX/final-patches/intel--ixgbevf--3.3.2 b/LINUX/final-patches/intel--ixgbevf--3.3.2 index f50556456..388e13822 100644 --- a/LINUX/final-patches/intel--ixgbevf--3.3.2 +++ b/LINUX/final-patches/intel--ixgbevf--3.3.2 @@ -1,4 +1,4 @@ -diff --git a/ixgbevf/Makefile b/ixgbevf-3.3.2/src/Makefile +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile index d85c225..da05d8b 100644 --- a/ixgbevf/Makefile +++ b/ixgbevf/Makefile @@ -46,7 +46,7 @@ index d85c225..da05d8b 100644 clean: @+$(call kernelbuild,clean) diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c -index 2435281..a64d083 100644 +index 2435281..f50475d 100644 --- a/ixgbevf/ixgbevf_main.c +++ b/ixgbevf/ixgbevf_main.c @@ -371,6 +371,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) @@ -109,20 +109,27 @@ index 2435281..a64d083 100644 do { union ixgbe_adv_rx_desc *rx_desc; -@@ -1823,8 +1862,11 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - DPRINTK(HW, DEBUG, "Could not enable Tx Queue %d\n", reg_idx); +@@ -1814,6 +1853,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + +#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); +#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1824,7 +1867,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + if (!wait_loop) + DPRINTK(HW, DEBUG, "Could not enable Tx Queue %d\n", reg_idx); } - + /** * ixgbevf_configure_tx - Configure 82599 VF Transmit Unit after Reset * @adapter: board private structure -@@ -1995,6 +2037,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, +@@ -1995,6 +2038,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); ixgbevf_rx_desc_queue_enable(adapter, ring); @@ -133,7 +140,7 @@ index 2435281..a64d083 100644 ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); } -@@ -4883,8 +4929,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, +@@ -4883,8 +4930,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, if (netdev->features & NETIF_F_GRO) DPRINTK(PROBE, INFO, "GRO is enabled\n"); #endif @@ -145,17 +152,17 @@ index 2435281..a64d083 100644 cards_found++; return 0; -@@ -4923,6 +4971,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) - if (!netdev) - return; +@@ -4925,6 +4974,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); +#ifdef DEV_NETMAP -+ netmap_detach(netdev); ++ ixgbe_netmap_detach(adapter); +#endif /* DEV_NETMAP */ + - adapter = netdev_priv(netdev); - set_bit(__IXGBEVF_REMOVE, &adapter->state); + cancel_work_sync(&adapter->service_task); + diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h index 976dc04..30b8868 100644 --- a/ixgbevf/kcompat.h diff --git a/LINUX/final-patches/intel--ixgbevf--4.0.3 b/LINUX/final-patches/intel--ixgbevf--4.0.3 index 2b5e68ddb..bbad1a6bd 100644 --- a/LINUX/final-patches/intel--ixgbevf--4.0.3 +++ b/LINUX/final-patches/intel--ixgbevf--4.0.3 @@ -46,7 +46,7 @@ index d85c225..da05d8b 100644 clean: @+$(call kernelbuild,clean) diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c -index 88f87cc..b285f33 100644 +index 88f87cc..945d7bd 100644 --- a/ixgbevf/ixgbevf_main.c +++ b/ixgbevf/ixgbevf_main.c @@ -371,6 +371,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) @@ -109,20 +109,27 @@ index 88f87cc..b285f33 100644 do { union ixgbe_adv_rx_desc *rx_desc; -@@ -1823,8 +1862,11 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - DPRINTK(HW, DEBUG, "Could not enable Tx Queue %d\n", reg_idx); +@@ -1814,6 +1853,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + +#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); +#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1824,7 +1867,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + if (!wait_loop) + DPRINTK(HW, DEBUG, "Could not enable Tx Queue %d\n", reg_idx); } - + /** * ixgbevf_configure_tx - Configure 82599 VF Transmit Unit after Reset * @adapter: board private structure -@@ -1995,6 +2037,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, +@@ -1995,6 +2038,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); ixgbevf_rx_desc_queue_enable(adapter, ring); @@ -133,7 +140,7 @@ index 88f87cc..b285f33 100644 ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); } -@@ -4888,8 +4934,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, +@@ -4888,8 +4935,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, if (netdev->features & NETIF_F_GRO) DPRINTK(PROBE, INFO, "GRO is enabled\n"); #endif @@ -145,17 +152,17 @@ index 88f87cc..b285f33 100644 cards_found++; return 0; -@@ -4928,6 +4976,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) - if (!netdev) - return; +@@ -4930,6 +4979,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); +#ifdef DEV_NETMAP -+ netmap_detach(netdev); ++ ixgbe_netmap_detach(adapter); +#endif /* DEV_NETMAP */ + - adapter = netdev_priv(netdev); - set_bit(__IXGBEVF_REMOVE, &adapter->state); + cancel_work_sync(&adapter->service_task); + diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h index a39ec3c..b9437ca 100644 --- a/ixgbevf/kcompat.h diff --git a/LINUX/final-patches/intel--ixgbevf--4.1.2 b/LINUX/final-patches/intel--ixgbevf--4.1.2 new file mode 100644 index 000000000..23972e405 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.1.2 @@ -0,0 +1,178 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index ca79ef6..939f185 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -28,22 +28,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbevf.o ++obj-$(CONFIG_IXGBE) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -90,9 +90,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 789187b..8169ad7 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -371,6 +371,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -390,6 +407,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1192,6 +1220,17 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ ++ + do { + union ixgbe_adv_rx_desc *rx_desc; + +@@ -1810,6 +1849,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1820,7 +1863,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + if (!wait_loop) + DPRINTK(HW, DEBUG, "Could not enable Tx Queue %d\n", reg_idx); + } +- ++ + /** + * ixgbevf_configure_tx - Configure 82599 VF Transmit Unit after Reset + * @adapter: board private structure +@@ -1991,6 +2034,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4923,8 +4970,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + cards_found++; + return 0; + +@@ -4965,6 +5014,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVE, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index c08d849..d7a58a5 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -25,6 +25,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.10.2 b/LINUX/final-patches/intel--ixgbevf--4.10.2 new file mode 100644 index 000000000..47d35bb50 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.10.2 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index dc49435..103a5d1 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 518123d..20e8b7d 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -343,6 +343,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -363,6 +380,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1375,6 +1403,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2093,6 +2131,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2327,6 +2369,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5636,8 +5682,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5678,6 +5726,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 080ed09..2c16b66 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -4,6 +4,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.11.1 b/LINUX/final-patches/intel--ixgbevf--4.11.1 new file mode 100644 index 000000000..2e3194900 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.11.1 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index 6391546..780d1c0 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 530df81..aca755d 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -343,6 +343,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -363,6 +380,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1375,6 +1403,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2093,6 +2131,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2327,6 +2369,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5636,8 +5682,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5678,6 +5726,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 7aaf487..7d3fe65 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -4,6 +4,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.12.4 b/LINUX/final-patches/intel--ixgbevf--4.12.4 new file mode 100644 index 000000000..9f81731fc --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.12.4 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index 6391546..780d1c0 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index a474676..265926b 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -345,6 +345,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -365,6 +382,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1377,6 +1405,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2095,6 +2133,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2329,6 +2371,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5623,8 +5669,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5665,6 +5713,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 8990cb5..d439cdb 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -4,6 +4,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.13.3 b/LINUX/final-patches/intel--ixgbevf--4.13.3 new file mode 100644 index 000000000..6c6b63ea3 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.13.3 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index 6391546..780d1c0 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index e03b00e..2c7165f 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -345,6 +345,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -365,6 +382,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1377,6 +1405,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2095,6 +2133,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2329,6 +2371,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5630,8 +5676,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5672,6 +5720,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 22e5420..7b5b3df 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -4,6 +4,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.14.5 b/LINUX/final-patches/intel--ixgbevf--4.14.5 new file mode 100644 index 000000000..d2b891c56 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.14.5 @@ -0,0 +1,169 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index 6391546..780d1c0 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index bdc8871..b57fcb9 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -345,6 +345,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -365,6 +382,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1383,6 +1411,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2101,6 +2139,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2335,6 +2377,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5639,8 +5685,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5681,6 +5729,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 3726b1d..dd43957 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -5,6 +5,9 @@ + #define _KCOMPAT_H_ + + #include "kcompat_gcc.h" ++ ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.15.1 b/LINUX/final-patches/intel--ixgbevf--4.15.1 new file mode 100644 index 000000000..79e875ca1 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.15.1 @@ -0,0 +1,169 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index 1a84106..04f4155 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index f142f2b..ba61d3e 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -345,6 +345,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -365,6 +382,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1383,6 +1411,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2101,6 +2139,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2335,6 +2377,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5639,8 +5685,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5681,6 +5729,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 090771a..250fc59 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -5,6 +5,9 @@ + #define _KCOMPAT_H_ + + #include "kcompat_gcc.h" ++ ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.16.5 b/LINUX/final-patches/intel--ixgbevf--4.16.5 new file mode 100644 index 000000000..017cef4fe --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.16.5 @@ -0,0 +1,169 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index 1a84106..04f4155 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 808b53a..dfbb1d2 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -345,6 +345,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -365,6 +382,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1383,6 +1411,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2101,6 +2139,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2335,6 +2377,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5639,8 +5685,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5681,6 +5729,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index d88e5a1..d71d355 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -5,6 +5,9 @@ + #define _KCOMPAT_H_ + + #include "kcompat_gcc.h" ++ ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.17.11 b/LINUX/final-patches/intel--ixgbevf--4.17.11 new file mode 100644 index 000000000..fa47ad3bd --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.17.11 @@ -0,0 +1,169 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index df6689d..03ce8eb 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 666aae8..1635113 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -345,6 +345,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -365,6 +382,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1383,6 +1411,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2101,6 +2139,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2335,6 +2377,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5643,8 +5689,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5685,6 +5733,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 3211170..13630cc 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -5,6 +5,9 @@ + #define _KCOMPAT_H_ + + #include "kcompat_gcc.h" ++ ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.17.13 b/LINUX/final-patches/intel--ixgbevf--4.17.13 new file mode 100644 index 000000000..c4b8707e5 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.17.13 @@ -0,0 +1,169 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index df6689d..03ce8eb 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 2102b94..b2a0f4e 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -344,6 +344,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -364,6 +381,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1382,6 +1410,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2100,6 +2138,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2334,6 +2376,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5642,8 +5688,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5684,6 +5732,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 7d1bb06..29a4e15 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -5,6 +5,9 @@ + #define _KCOMPAT_H_ + + #include "kcompat_gcc.h" ++ ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.17.5 b/LINUX/final-patches/intel--ixgbevf--4.17.5 new file mode 100644 index 000000000..a33db6001 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.17.5 @@ -0,0 +1,169 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index 27c7cfc..3036354 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 35ef9dc..a7357f6 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -345,6 +345,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -365,6 +382,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1383,6 +1411,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2101,6 +2139,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2335,6 +2377,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5643,8 +5689,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5685,6 +5733,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 8738cb0..27281d6 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -5,6 +5,9 @@ + #define _KCOMPAT_H_ + + #include "kcompat_gcc.h" ++ ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.18.7 b/LINUX/final-patches/intel--ixgbevf--4.18.7 new file mode 100644 index 000000000..6b763f347 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.18.7 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index df6689d..03ce8eb 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 6af15b2..85d8ed3 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -344,6 +344,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -364,6 +381,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1382,6 +1410,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2100,6 +2138,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2334,6 +2376,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5642,8 +5688,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5684,6 +5732,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index afc88b1..104ea20 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -12,6 +12,8 @@ + + #include "kcompat_gcc.h" + ++#include ++ + #include + #include + #include diff --git a/LINUX/final-patches/intel--ixgbevf--4.18.9 b/LINUX/final-patches/intel--ixgbevf--4.18.9 new file mode 100644 index 000000000..e041e6309 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.18.9 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index df6689d..03ce8eb 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index bbe4a4f..0827dc8 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -344,6 +344,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -364,6 +381,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1382,6 +1410,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2100,6 +2138,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2334,6 +2376,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5642,8 +5688,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5684,6 +5732,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index afc88b1..104ea20 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -12,6 +12,8 @@ + + #include "kcompat_gcc.h" + ++#include ++ + #include + #include + #include diff --git a/LINUX/final-patches/intel--ixgbevf--4.19.10 b/LINUX/final-patches/intel--ixgbevf--4.19.10 new file mode 100644 index 000000000..1a39ccbc4 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.19.10 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index 2ec8757..fea9d2b 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 75136a0..cf8023a 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -352,6 +352,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -372,6 +389,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1390,6 +1418,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2108,6 +2146,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2342,6 +2384,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5655,8 +5701,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5697,6 +5745,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 3167668..2bf3b6d 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -12,6 +12,8 @@ + + #include "kcompat_gcc.h" + ++#include ++ + #include + #include + #include diff --git a/LINUX/final-patches/intel--ixgbevf--4.2.1 b/LINUX/final-patches/intel--ixgbevf--4.2.1 new file mode 100644 index 000000000..d56b2ac8c --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.2.1 @@ -0,0 +1,178 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index ca79ef6..939f185 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -28,22 +28,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbevf.o ++obj-$(CONFIG_IXGBE) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -90,9 +90,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 7bb8159..220faa6 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -371,6 +371,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -390,6 +407,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1192,6 +1220,17 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ ++ + do { + union ixgbe_adv_rx_desc *rx_desc; + +@@ -1816,6 +1855,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1826,7 +1869,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + if (!wait_loop) + DPRINTK(HW, DEBUG, "Could not enable Tx Queue %d\n", reg_idx); + } +- ++ + /** + * ixgbevf_configure_tx - Configure 82599 VF Transmit Unit after Reset + * @adapter: board private structure +@@ -1997,6 +2040,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4919,8 +4966,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + cards_found++; + return 0; + +@@ -4961,6 +5010,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVE, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index b53b133..30f592b 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -25,6 +25,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.2.2 b/LINUX/final-patches/intel--ixgbevf--4.2.2 new file mode 100644 index 000000000..c5e2fceef --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.2.2 @@ -0,0 +1,178 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index ca79ef6..939f185 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -28,22 +28,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbevf.o ++obj-$(CONFIG_IXGBE) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -90,9 +90,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index c1f7021..f2ecac4 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -371,6 +371,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -390,6 +407,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1192,6 +1220,17 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ ++ + do { + union ixgbe_adv_rx_desc *rx_desc; + +@@ -1816,6 +1855,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1826,7 +1869,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + if (!wait_loop) + DPRINTK(HW, DEBUG, "Could not enable Tx Queue %d\n", reg_idx); + } +- ++ + /** + * ixgbevf_configure_tx - Configure 82599 VF Transmit Unit after Reset + * @adapter: board private structure +@@ -1997,6 +2040,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4919,8 +4966,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + cards_found++; + return 0; + +@@ -4961,6 +5010,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVE, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 6b93e54..34d4440 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -25,6 +25,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.3.2 b/LINUX/final-patches/intel--ixgbevf--4.3.2 new file mode 100644 index 000000000..1eb36cea6 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.3.2 @@ -0,0 +1,177 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index ca79ef6..939f185 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -28,22 +28,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbevf.o ++obj-$(CONFIG_IXGBE) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -90,9 +90,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 7c5bf52..045005e 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -371,6 +371,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -390,6 +407,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1192,6 +1220,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + +@@ -1825,6 +1863,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1835,7 +1877,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + if (!wait_loop) + DPRINTK(HW, DEBUG, "Could not enable Tx Queue %d\n", reg_idx); + } +- ++ + /** + * ixgbevf_configure_tx - Configure 82599 VF Transmit Unit after Reset + * @adapter: board private structure +@@ -2012,6 +2054,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4969,8 +5015,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5010,6 +5058,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index a36ee42..7f09616 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -25,6 +25,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.3.3 b/LINUX/final-patches/intel--ixgbevf--4.3.3 new file mode 100644 index 000000000..4933a3ab2 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.3.3 @@ -0,0 +1,177 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index ca79ef6..939f185 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -28,22 +28,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbevf.o ++obj-$(CONFIG_IXGBE) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -90,9 +90,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index e50d8ae..2eb1e3e 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -371,6 +371,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -390,6 +407,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1192,6 +1220,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + +@@ -1825,6 +1863,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1835,7 +1877,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + if (!wait_loop) + DPRINTK(HW, DEBUG, "Could not enable Tx Queue %d\n", reg_idx); + } +- ++ + /** + * ixgbevf_configure_tx - Configure 82599 VF Transmit Unit after Reset + * @adapter: board private structure +@@ -2012,6 +2054,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4969,8 +5015,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5010,6 +5058,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 71c5d93..97d2b32 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -25,6 +25,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.3.4 b/LINUX/final-patches/intel--ixgbevf--4.3.4 new file mode 100644 index 000000000..fbaf49ddf --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.3.4 @@ -0,0 +1,177 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index c8d39f4..e16565a 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -28,22 +28,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbevf.o ++obj-$(CONFIG_IXGBE) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -90,9 +90,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 84b281c..cf6004b 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -371,6 +371,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -390,6 +407,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1192,6 +1220,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + +@@ -1825,6 +1863,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1835,7 +1877,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + if (!wait_loop) + DPRINTK(HW, DEBUG, "Could not enable Tx Queue %d\n", reg_idx); + } +- ++ + /** + * ixgbevf_configure_tx - Configure 82599 VF Transmit Unit after Reset + * @adapter: board private structure +@@ -2012,6 +2054,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4969,8 +5015,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5010,6 +5058,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index b780c4c..dfd74ed 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -25,6 +25,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.3.5 b/LINUX/final-patches/intel--ixgbevf--4.3.5 new file mode 100644 index 000000000..417284dcb --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.3.5 @@ -0,0 +1,177 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index c8d39f4..e16565a 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -28,22 +28,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbevf.o ++obj-$(CONFIG_IXGBE) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -90,9 +90,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 6a3720f..2752402 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -371,6 +371,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -390,6 +407,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1192,6 +1220,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + +@@ -1825,6 +1863,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1835,7 +1877,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + if (!wait_loop) + DPRINTK(HW, DEBUG, "Could not enable Tx Queue %d\n", reg_idx); + } +- ++ + /** + * ixgbevf_configure_tx - Configure 82599 VF Transmit Unit after Reset + * @adapter: board private structure +@@ -2012,6 +2054,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4984,8 +5030,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5025,6 +5073,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 25e42e9..3374fb9 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -25,6 +25,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.3.6 b/LINUX/final-patches/intel--ixgbevf--4.3.6 new file mode 100644 index 000000000..239d7acca --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.3.6 @@ -0,0 +1,177 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index c8d39f4..e16565a 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -28,22 +28,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBE) += ixgbevf.o ++obj-$(CONFIG_IXGBE) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -90,9 +90,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 733d94f..e101128 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -371,6 +371,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -390,6 +407,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1192,6 +1220,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + +@@ -1825,6 +1863,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1835,7 +1877,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + if (!wait_loop) + DPRINTK(HW, DEBUG, "Could not enable Tx Queue %d\n", reg_idx); + } +- ++ + /** + * ixgbevf_configure_tx - Configure 82599 VF Transmit Unit after Reset + * @adapter: board private structure +@@ -2012,6 +2054,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4984,8 +5030,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5025,6 +5073,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 25e42e9..3374fb9 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -25,6 +25,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.5.1 b/LINUX/final-patches/intel--ixgbevf--4.5.1 new file mode 100644 index 000000000..6acdca530 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.5.1 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index 18d35f3..c4ae238 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -69,9 +69,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 139aaf7..c03cb9e 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -338,6 +338,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -358,6 +375,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1355,6 +1383,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2053,6 +2091,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2287,6 +2329,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5568,8 +5614,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5610,6 +5658,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 4b1c30f..46953ef 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -4,6 +4,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.5.2 b/LINUX/final-patches/intel--ixgbevf--4.5.2 new file mode 100644 index 000000000..38d9cfd79 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.5.2 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index b37fbce..f3cdb26 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -69,9 +69,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 05fb3b2..a3896de 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -338,6 +338,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -358,6 +375,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1355,6 +1383,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2053,6 +2091,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2287,6 +2329,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5570,8 +5616,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5612,6 +5660,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 55205fa..6c3a181 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -4,6 +4,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.5.3 b/LINUX/final-patches/intel--ixgbevf--4.5.3 new file mode 100644 index 000000000..9de45aa16 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.5.3 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index b37fbce..f3cdb26 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -69,9 +69,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 65645fb..4ab32c4 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -338,6 +338,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -358,6 +375,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1355,6 +1383,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2053,6 +2091,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2287,6 +2329,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5578,8 +5624,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5620,6 +5668,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 55205fa..6c3a181 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -4,6 +4,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.6.1 b/LINUX/final-patches/intel--ixgbevf--4.6.1 new file mode 100644 index 000000000..5d7ad559a --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.6.1 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index b37fbce..f3cdb26 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -69,9 +69,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 15480dc..494d9e8 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -338,6 +338,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -358,6 +375,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1356,6 +1384,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2054,6 +2092,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2288,6 +2330,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5582,8 +5628,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5624,6 +5672,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 2d1b428..e98c520 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -4,6 +4,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.6.3 b/LINUX/final-patches/intel--ixgbevf--4.6.3 new file mode 100644 index 000000000..cce068531 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.6.3 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index b37fbce..f3cdb26 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -69,9 +69,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index e7bd791..c845e6c 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -338,6 +338,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -358,6 +375,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1356,6 +1384,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2054,6 +2092,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2288,6 +2330,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5581,8 +5627,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5623,6 +5671,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 785e735..56246ec 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -4,6 +4,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.7.1 b/LINUX/final-patches/intel--ixgbevf--4.7.1 new file mode 100644 index 000000000..834fc1edc --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.7.1 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index dc49435..103a5d1 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index e74af0d..72f1225 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -343,6 +343,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -363,6 +380,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1361,6 +1389,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2066,6 +2104,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2300,6 +2342,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5599,8 +5645,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5641,6 +5689,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 503b058..7642a11 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -4,6 +4,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.8.1 b/LINUX/final-patches/intel--ixgbevf--4.8.1 new file mode 100644 index 000000000..30e56aaec --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.8.1 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index dc49435..103a5d1 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 1336492..8f43efc 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -343,6 +343,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -363,6 +380,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1361,6 +1389,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2066,6 +2104,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2300,6 +2342,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5599,8 +5645,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5641,6 +5689,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index e658a62..ede68df 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -4,6 +4,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--4.9.3 b/LINUX/final-patches/intel--ixgbevf--4.9.3 new file mode 100644 index 000000000..5975f38b7 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--4.9.3 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/ixgbevf/Makefile +index dc49435..103a5d1 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index ac1e9a2..039fb8f 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -343,6 +343,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -363,6 +380,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1361,6 +1389,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2066,6 +2104,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2300,6 +2342,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5609,8 +5655,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5651,6 +5699,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/ixgbevf/kcompat.h +index 00cdebd..e4a5497 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -4,6 +4,8 @@ + #ifndef _KCOMPAT_H_ + #define _KCOMPAT_H_ + ++#include ++ + #ifndef LINUX_VERSION_CODE + #include + #else diff --git a/LINUX/final-patches/intel--ixgbevf--5.0.2 b/LINUX/final-patches/intel--ixgbevf--5.0.2 new file mode 100644 index 000000000..7c7174855 --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--5.0.2 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/src/Makefile +index 117871c..02290b5 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/src/ixgbevf_main.c +index 32b3998..ab9efe5 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -352,6 +352,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -372,6 +389,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1390,6 +1418,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2108,6 +2146,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2342,6 +2384,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5661,8 +5707,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5703,6 +5751,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/src/kcompat.h +index ab1d295..21bc1e7 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -12,6 +12,8 @@ + + #include "kcompat_gcc.h" + ++#include ++ + #include + #include + #include diff --git a/LINUX/final-patches/intel--ixgbevf--5.1.5 b/LINUX/final-patches/intel--ixgbevf--5.1.5 new file mode 100644 index 000000000..63495755c --- /dev/null +++ b/LINUX/final-patches/intel--ixgbevf--5.1.5 @@ -0,0 +1,168 @@ +diff --git a/ixgbevf/Makefile b/src/Makefile +index 117871c..02290b5 100644 +--- a/ixgbevf/Makefile ++++ b/ixgbevf/Makefile +@@ -7,22 +7,22 @@ ifneq ($(KERNELRELEASE),) + # Makefile for the Intel(R) 10GbE PCI Express Virtual Function Driver + # + +-obj-$(CONFIG_IXGBEVF) += ixgbevf.o ++obj-$(CONFIG_IXGBEVF) += ixgbevf$(NETMAP_DRIVER_SUFFIX).o + +-define ixgbevf-y ++define ixgbevf$(NETMAP_DRIVER_SUFFIX)-y + ixgbevf_main.o + ixgbevf_ethtool.o + ixgbe_vf.o + ixgbe_mbx.o + endef +-ixgbevf-y := $(strip ${ixgbevf-y}) +-ixgbevf-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o +-ixgbevf-y += kcompat.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y := $(strip ${ixgbevf$(NETMAP_DRIVER_SUFFIX)-y}) ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-${CONFIG_PCI_HYPERV:m=y} += ixgbe_hv_vf.o ++ixgbevf$(NETMAP_DRIVER_SUFFIX)-y += kcompat.o + + else # ifneq($(KERNELRELEASE),) + # normal makefile + +-DRIVER := ixgbevf ++DRIVER := ixgbevf$(NETMAP_DRIVER_SUFFIX) + + ifeq (,$(wildcard common.mk)) + $(error Cannot find common.mk build rules) +@@ -81,9 +81,12 @@ ccc: clean + @+$(call kernelbuild,modules,coccicheck MODE=report)) + + # Build manfiles +-manfile: ++manfile: ../$(DRIVER).$(MANSECTION) + @gzip -c ../${DRIVER}.${MANSECTION} > ${DRIVER}.${MANSECTION}.gz + ++../$(DRIVER).$(MANSECTION): ++ touch $@ ++ + # Clean the module subdirectories + clean: + @+$(call kernelbuild,clean) +diff --git a/ixgbevf/ixgbevf_main.c b/src/ixgbevf_main.c +index e9d31d5..c7ec472 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -352,6 +352,23 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif + + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes +@@ -372,6 +389,17 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1390,6 +1418,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.data = NULL; + xdp.data_end = NULL; + #ifdef HAVE_XDP_BUFF_RXQ +@@ -2108,6 +2146,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -2342,6 +2384,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -5661,8 +5707,10 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, + if (netdev->features & NETIF_F_GRO) + DPRINTK(PROBE, INFO, "GRO is enabled\n"); + #endif +- + DPRINTK(PROBE, INFO, "%s\n", ixgbevf_driver_string); ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ + return 0; + + err_register: +@@ -5703,6 +5751,10 @@ static void __devexit ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + +diff --git a/ixgbevf/kcompat.h b/src/kcompat.h +index 8d5e4fd..8a8399c 100644 +--- a/ixgbevf/kcompat.h ++++ b/ixgbevf/kcompat.h +@@ -12,6 +12,8 @@ + + #include "kcompat_gcc.h" + ++#include ++ + #ifndef HAVE_XARRAY_API + #include "kcompat_xarray.h" + #endif /* !HAVE_XARRAY_API */ diff --git a/LINUX/final-patches/mellanox--mlx5--24.10 b/LINUX/final-patches/mellanox--mlx5--24.10 new file mode 100644 index 000000000..44c6c79b3 --- /dev/null +++ b/LINUX/final-patches/mellanox--mlx5--24.10 @@ -0,0 +1,453 @@ +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +index 49d9987..d9f4e09 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +@@ -10,12 +10,12 @@ ifeq ($(CONFIG_IS_AZURELINUX), y) + EXTRA_CFLAGS += -fno-exceptions + endif + +-obj-$(CONFIG_MLX5_CORE) += mlx5_core.o ++obj-$(CONFIG_MLX5_CORE) += mlx5_core$(NETMAP_DRIVER_SUFFIX).o + + # + # mlx5 core basic + # +-mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + health.o mcg.o cq.o alloc.o port.o mr.o pd.o \ + transobj.o vport.o sriov.o fs_cmd.o fs_core.o pci_irq.o \ + fs_counters.o fs_ft_pool.o rl.o lag/debugfs.o lag/lag.o dev.o events.o wq.o lib/gid.o \ +@@ -25,12 +25,11 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + sriov_sysfs.o crdump.o diag/diag_cnt.o fw_exp.o \ + eswitch_devlink_compat.o ecpf.o wc.o auxiliary_sysfs_compat.o + +-mlx5_core-y += compat.o +- ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += compat.o + # + # Netdev basic + # +-mlx5_core-$(CONFIG_MLX5_CORE_EN) += en/rqt.o en/tir.o en/rss.o en/rx_res.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN) += en/rqt.o en/tir.o en/rss.o en/rx_res.o \ + en/channels.o en_main.o en_common.o en_fs.o en_ethtool.o \ + en_tx.o en_rx.o en_dim.o en_txrx.o en/xdp.o en_stats.o en_sysfs.o en_ecn.o\ + en_selftest.o en/port.o en/monitor_stats.o en/health.o \ +@@ -42,14 +41,14 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en/rqt.o en/tir.o en/rss.o en/rx_res.o \ + # + # Netdev extra + # +-mlx5_core-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o +-mlx5_core-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o +-mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o +-mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += lag/mp.o lag/port_sel.o lib/geneve.o lib/port_tun.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += lag/mp.o lag/port_sel.o lib/geneve.o lib/port_tun.o \ + en_rep.o en/rep/bond.o en/mod_hdr.o \ + en/mapping.o lag/mpesw.o en/rep/meter.o en/rep/sysfs.o +-mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ + lib/fs_chains.o en/tc_tun.o \ + esw/indir_table.o en/tc_tun_encap.o \ + en/tc_tun_vxlan.o en/tc_tun_gre.o en/tc_tun_geneve.o \ +@@ -57,7 +56,7 @@ mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ + en/tc/post_act.o en/tc/int_port.o en/tc/meter.o \ + en/tc/post_meter.o en/tc/act_stats.o + +-mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en/tc/act/act.o en/tc/act/drop.o en/tc/act/trap.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CLS_ACT) += en/tc/act/act.o en/tc/act/drop.o en/tc/act/trap.o \ + en/tc/act/accept.o en/tc/act/mark.o en/tc/act/goto.o \ + en/tc/act/tun.o en/tc/act/csum.o en/tc/act/pedit.o \ + en/tc/act/vlan.o en/tc/act/vlan_mangle.o en/tc/act/mpls.o \ +@@ -67,60 +66,60 @@ mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en/tc/act/act.o en/tc/act/drop.o en/tc/a + + mlx5_core-$(CONFIG_MLX5_TC_CT) += en/tc_ct.o en/tc/ct_fs_dmfs.o en/tc/ct_fs_smfs.o en/tc/act/ct.o + +-mlx5_core-$(CONFIG_MLX5_TC_SAMPLE) += en/tc/sample.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_TC_SAMPLE) += en/tc/sample.o + + # + # Core extra + # +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \ + ecpf.o rdma.o esw/legacy.o \ + esw/devlink_port.o esw/vporttbl.o esw/qos.o esw/ipsec.o \ + esw/pet_offloads.o esw/vf_meter.o + +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ + esw/acl/egress_lgcy.o esw/acl/egress_ofld.o \ + esw/acl/ingress_lgcy.o esw/acl/ingress_ofld.o + + ifneq ($(CONFIG_MLX5_EN_IPSEC),) +- mlx5_core-$(CONFIG_MLX5_ESWITCH) += esw/ipsec_fs.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += esw/ipsec_fs.o + endif + +-mlx5_core-$(CONFIG_MLX5_BRIDGE) += esw/bridge.o esw/bridge_mcast.o esw/bridge_debugfs.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_BRIDGE) += esw/bridge.o esw/bridge_mcast.o esw/bridge_debugfs.o \ + en/rep/bridge.o + +-mlx5_core-$(CONFIG_HWMON) += hwmon.o +-mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_HWMON) += hwmon.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MPFS) += lib/mpfs.o + ifneq ($(CONFIG_VXLAN),) +- mlx5_core-y += lib/vxlan.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/vxlan.o + endif + ifneq ($(CONFIG_PTP_1588_CLOCK),) +- mlx5_core-y += lib/clock.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/clock.o + endif +-mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o +-mlx5_core-$(CONFIG_MLXDEVM) += mlx5_devm.o esw/devm_port.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLXDEVM) += mlx5_devm.o esw/devm_port.o + + # + # Ipoib netdev + # +-mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o + + # + # Accelerations & FPGA + # +-mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o + +-mlx5_core-$(CONFIG_MLX5_MACSEC) += en_accel/macsec.o lib/macsec_fs.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MACSEC) += en_accel/macsec.o lib/macsec_fs.o \ + en_accel/macsec_stats.o + +-mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ + en_accel/ipsec_stats.o en_accel/ipsec_fs.o \ + en_accel/ipsec_offload.o lib/ipsec_fs_roce.o + +-mlx5_core-$(CONFIG_MLX5_EN_TLS) += en_accel/ktls_stats.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_TLS) += en_accel/ktls_stats.o \ + en_accel/fs_tcp.o en_accel/ktls.o en_accel/ktls_txrx.o \ + en_accel/ktls_tx.o en_accel/ktls_rx.o + +-mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ + steering/dr_matcher.o steering/dr_rule.o \ + steering/dr_icm_pool.o steering/dr_buddy.o \ + steering/dr_ste.o steering/dr_send.o \ +@@ -133,17 +132,17 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o + # + # SF device + # +-mlx5_core-$(CONFIG_MLX5_SF) += sf/vhca_event.o sf/dev/dev.o sf/dev/driver.o irq_affinity.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SF) += sf/vhca_event.o sf/dev/dev.o sf/dev/driver.o irq_affinity.o + + # + # SF manager + # +-mlx5_core-$(CONFIG_MLX5_SF_MANAGER) += sf/cmd.o sf/hw_table.o sf/devlink.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SF_MANAGER) += sf/cmd.o sf/hw_table.o sf/devlink.o + + # + ## SF cfg driver basic + # +-mlx5_core-$(CONFIG_MLX5_SF_CFG) += sf/dev/cfg_driver.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SF_CFG) += sf/dev/cfg_driver.o + + obj-$(CONFIG_MLX5_DPLL) += mlx5_dpll.o + mlx5_dpll-y := dpll.o +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +index dea5bf6..06622b7 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +@@ -32,6 +32,10 @@ static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq) + return 0; + + msleep(20); ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif + } + + netdev_err(sq->netdev, +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index ad3931d..16e62e1 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -96,6 +96,16 @@ + #include "lib/sd.h" + #include "compat.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#define NETMAP_MLX5_MAIN ++#define DEV_NETMAP ++#include "mlx5_netmap_linux.h" ++#endif ++ + static void mlx5e_mqprio_build_default_tc_to_txq(struct netdev_tc_txq *tc_to_txq, + int ntc, int nch); + +@@ -105,6 +115,10 @@ bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev, u8 page_ + u16 umr_wqebbs, max_wqebbs; + bool striding_rq_umr; + ++#ifdef DEV_NETMAP ++ return 0; ++#endif ++ + striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && MLX5_CAP_GEN(mdev, umr_ptr_rlky) && + MLX5_CAP_ETH(mdev, reg_umr_sq); + if (!striding_rq_umr) +@@ -1665,6 +1679,12 @@ int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time) + { + unsigned long exp_time = jiffies + msecs_to_jiffies(wait_time); + ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->netdev); ++ if (nm_netmap_on(na) && na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_ON) ++ return 0; /* no need to wait when netmap has built wqes */ ++#endif ++ + u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5e_rqwq_get_size(rq)); + + do { +@@ -1767,6 +1787,10 @@ void mlx5e_free_rx_descs(struct mlx5e_rq *rq) + + while (!mlx5_wq_cyc_is_empty(wq)) { + wqe_ix = mlx5_wq_cyc_get_tail(wq); ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->netdev); ++ if (!nm_netmap_on(na) || na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + rq->dealloc_wqe(rq, wqe_ix); + mlx5_wq_cyc_pop(wq); + } +@@ -1910,6 +1934,10 @@ int mlx5e_open_rq(struct mlx5e_params *params, struct mlx5e_rq_param *param, + MLX5_CAP_GEN(mdev, enhanced_cqe_compression)) + __set_bit(MLX5E_RQ_STATE_MINI_CQE_ENHANCED, &rq->state); + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_configure_rx_ring(rq, rq->ix); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_destroy_rq: +@@ -1923,6 +1951,9 @@ err_dealloc_rq: + + void mlx5e_activate_rq(struct mlx5e_rq *rq) + { ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(rq->netdev)) || NA(rq->netdev)->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); + } + +@@ -2235,6 +2266,11 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, + INIT_WORK(&sq->dim_obj.dim.work, mlx5e_tx_dim_work); + sq->dim_obj.dim.mode = params->tx_cq_moderation.cq_period_mode; + ++#ifdef DEV_NETMAP ++ if (mlx5e_netmap_configure_tx_ring(c->priv, txq_ix)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_sq_wq_destroy: +@@ -2442,6 +2478,9 @@ void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) + mlx5e_tx_disable_queue(sq->txq); + + /* last doorbell out, godspeed .. */ ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(sq->txq->dev))) // TODO ++#endif + if (mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1)) { + u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + struct mlx5e_tx_wqe *nop; +@@ -2462,6 +2501,12 @@ void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) + + cancel_work_sync(&sq->dim_obj.dim.work); + cancel_work_sync(&sq->recover_work); ++ ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif ++ + mlx5e_destroy_sq(mdev, sq->sqn); + if (sq->rate_limit) { + rl.rate = sq->rate_limit; +@@ -3986,6 +4031,11 @@ int mlx5e_open_locked(struct net_device *netdev) + priv->profile->update_carrier(priv); + + mlx5e_queue_update_stats(priv); ++ ++#ifdef DEV_NETMAP ++ netmap_enable_all_rings(netdev); /* NOP if netmap not in use */ ++#endif ++ + return 0; + + err_close_channels: +@@ -4023,6 +4073,10 @@ int mlx5e_close_locked(struct net_device *netdev) + mlx5e_apply_traps(priv, false); + clear_bit(MLX5E_STATE_OPENED, &priv->state); + ++#ifdef DEV_NETMAP ++ netmap_disable_all_rings(netdev); ++#endif ++ + netif_carrier_off(priv->netdev); + mlx5e_destroy_debugfs(priv); + mlx5e_deactivate_priv_channels(priv); +@@ -7292,6 +7346,10 @@ void mlx5e_destroy_netdev(struct mlx5e_priv *priv) + { + struct net_device *netdev = priv->netdev; + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + mlx5e_priv_cleanup(priv); + free_netdev(netdev); + } +@@ -7544,6 +7602,11 @@ static int _mlx5e_probe(struct auxiliary_device *adev) + + mlx5e_dcbnl_init_app(priv); + mlx5_core_uplink_netdev_set(mdev, netdev); ++ ++#ifdef DEV_NETMAP ++ mlx5e_netmap_attach(priv); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_unregister_netdev: +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +index 35291db..7174e8c 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +@@ -86,6 +86,14 @@ const struct mlx5e_rx_handlers mlx5e_rx_handlers_nic = { + .handle_rx_cqe_mpwqe_shampo = mlx5e_handle_rx_cqe_mpwrq_shampo, + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline void mlx5e_read_cqe_slot(struct mlx5_cqwq *wq, + u32 cqcc, void *data) + { +@@ -261,7 +269,7 @@ static inline u32 mlx5e_decompress_cqes_cont(struct mlx5e_rq *rq, + return cqe_count; + } + +-static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, ++u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, + struct mlx5_cqwq *wq, + int budget_rem) + { +@@ -4028,6 +4036,13 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) + struct mlx5_cqwq *cqwq = &cq->wq; + int work_done; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ int dummy; ++ int nm_irq = netmap_rx_irq(rq->netdev, rq->ix, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif ++ + if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + return 0; + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +index 04459c5..08109d3 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +@@ -46,6 +46,15 @@ + #include + #endif + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ ++ + #ifdef HAVE_BASECODE_EXTRAS + static inline void mlx5e_read_cqe_slot(struct mlx5_cqwq *wq, + u32 cqcc, void *data) +@@ -975,6 +984,11 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) + + sq = container_of(cq, struct mlx5e_txqsq, cq); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ if (netmap_tx_irq(sq->netdev, sq->ch_ix) != NM_IRQ_PASS) ++ return false; ++#endif ++ + if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + return false; + +@@ -1093,23 +1107,29 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) + + sqcc += wi->num_wqebbs; + +- if (likely(wi->skb)) { +- mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); +- dev_kfree_skb_any(wi->skb); ++ if (!nm_netmap_on(NA(sq->txq->dev))) { ++ /* do not free skbs in netmap mode */ ++ if (likely(wi->skb)) { ++ mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); ++ dev_kfree_skb_any(wi->skb); + +- npkts++; +- nbytes += wi->num_bytes; +- continue; +- } ++ npkts++; ++ nbytes += wi->num_bytes; ++ continue; ++ } + +- if (unlikely(mlx5e_ktls_tx_try_handle_resync_dump_comp(sq, wi, &dma_fifo_cc))) +- continue; ++ if (unlikely(mlx5e_ktls_tx_try_handle_resync_dump_comp(sq, wi, &dma_fifo_cc))) ++ continue; + +- if (wi->num_fifo_pkts) { +- mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); +- mlx5e_tx_wi_kfree_fifo_skbs(sq, wi); ++ if (wi->num_fifo_pkts) { ++ mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); ++ mlx5e_tx_wi_kfree_fifo_skbs(sq, wi); + +- npkts += wi->num_fifo_pkts; ++ npkts += wi->num_fifo_pkts; ++ nbytes += wi->num_bytes; ++ } ++ } else { ++ npkts++; + nbytes += wi->num_bytes; + } + } diff --git a/LINUX/final-patches/mellanox--mlx5--3.3 b/LINUX/final-patches/mellanox--mlx5--3.3 new file mode 100644 index 000000000..0e9d270e6 --- /dev/null +++ b/LINUX/final-patches/mellanox--mlx5--3.3 @@ -0,0 +1,379 @@ +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +index b8ce0b5..574d21a 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +@@ -1,12 +1,13 @@ + ccflags-y += $(MLNX_CFLAGS) + +-obj-$(CONFIG_MLX5_CORE) += mlx5_core.o ++obj-$(CONFIG_MLX5_CORE) += mlx5_core$(NETMAP_DRIVER_SUFFIX).o + +-mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \ + mad.o wq.o vport.o transobj.o en_main.o \ + en_ethtool.o en_tx.o en_rx.o en_txrx.o \ + sriov.o params.o en_debugfs.o en_selftest.o en_sysfs.o en_ecn.o \ + en_dcb_nl.o fs_cmd.o fs_core.o fs_debugfs.o en_fs.o \ + eswitch.o vxlan.o en_clock.o en_sniffer.o rl.o +-mlx5_core-$(CONFIG_RFS_ACCEL) += en_arfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_RFS_ACCEL) += en_arfs.o ++ +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +index 847d804..342c754 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +@@ -32,6 +32,12 @@ + + #include "en.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_GET_RQ_TYPE(mdev) RQ_TYPE_NONE /* ensure RQ_TYPE_STRIDE not used with netmap */ ++#else ++#define NETMAP_GET_RQ_TYPE(mdev) MLX5_CAP_GEN(mdev, striding_rq) ++#endif ++ + static const char mlx5e_test_names[][ETH_GSTRING_LEN] = { + "Speed Test", + "Link Test", +@@ -468,8 +474,7 @@ static void mlx5e_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *param) + { + struct mlx5e_priv *priv = netdev_priv(dev); +- int rq_wq_type = MLX5_CAP_GEN(priv->mdev, striding_rq); +- ++ int rq_wq_type = NETMAP_GET_RQ_TYPE(priv->mdev); /* Add netmap support */ + param->rx_max_pending = + mlx5e_rx_wqes_to_packets(rq_wq_type, + 1 << mlx5_max_log_rq_size(rq_wq_type)); +@@ -485,7 +490,7 @@ static int mlx5e_set_ringparam(struct net_device *dev, + { + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5e_params new_params; +- int rq_wq_type = MLX5_CAP_GEN(priv->mdev, striding_rq); ++ int rq_wq_type = NETMAP_GET_RQ_TYPE(priv->mdev); /* Add netmap support */ + u16 min_rx_wqes; + u8 log_rq_size; + u8 log_sq_size; +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index 70fd71a..48644dc 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -61,6 +61,19 @@ struct mlx5e_channel_param { + struct mlx5e_cq_param tx_cq; + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#define NETMAP_MLX5_MAIN ++#include "mlx5_netmap_linux.h" ++ ++#define NETMAP_GET_RQ_TYPE(mdev) RQ_TYPE_NONE /* ensure RQ_TYPE_STRIDE not used with netmap */ ++#else ++#define NETMAP_GET_RQ_TYPE(mdev) MLX5_CAP_GEN(mdev, striding_rq) ++#endif ++ + static void mlx5e_update_carrier(struct mlx5e_priv *priv) + { + struct mlx5_core_dev *mdev = priv->mdev; +@@ -351,8 +364,7 @@ static int mlx5e_create_rq(struct mlx5e_channel *c, + + param->wq.db_numa_node = cpu_to_node(c->cpu); + +- rq->rq_type = MLX5_CAP_GEN(mdev, striding_rq); +- ++ rq->rq_type = NETMAP_GET_RQ_TYPE(mdev); /* Add netmap support */ + err = mlx5_wq_ll_create(mdev, ¶m->wq, rqc_wq, &rq->wq, + &rq->wq_ctrl); + if (err) +@@ -415,6 +427,10 @@ static int mlx5e_create_rq(struct mlx5e_channel *c, + rq->channel = c; + rq->ix = c->ix; + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_configure_rx_ring(rq, rq->ix); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_rq_wq_destroy: +@@ -551,6 +567,11 @@ static int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq) + struct mlx5_wq_ll *wq = &rq->wq; + int i; + ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(c->netdev))) ++ return 0; /* no need to wait when netmap has built wqes */ ++#endif ++ + for (i = 0; i < MLX5_EN_MAX_ITER; i++) { + if (wq->cur_sz >= priv->params.min_rx_wqes) + return 0; +@@ -636,7 +657,12 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, + if (err) + goto err_disable_rq; + +- set_bit(MLX5E_RQ_STATE_POST_WQES_ENABLE, &rq->state); ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(c->netdev))) ++#endif ++ { ++ set_bit(MLX5E_RQ_STATE_POST_WQES_ENABLE, &rq->state); ++ } + mlx5e_send_nop(&c->sq[0], true); /* trigger mlx5e_post_rx_wqes() */ + + return 0; +@@ -660,9 +686,15 @@ static void mlx5e_close_rq(struct mlx5e_rq *rq) + + mlx5e_modify_rq_state(rq, MLX5_RQC_STATE_RDY, MLX5_RQC_STATE_ERR); + if (!priv->internal_error) { +- for (i = 0; i < MLX5_EN_MAX_ITER && !mlx5_wq_ll_is_empty(&rq->wq); i++) ++ for (i = 0; i < MLX5_EN_MAX_ITER && !mlx5_wq_ll_is_empty(&rq->wq); i++) { + msleep(MLX5_EN_MSLEEP_QUANT); + ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(c->netdev))) ++ mlx5e_netmap_rx_flush(rq); /* handle the CQEs */ ++#endif ++ } ++ + if (i == MLX5_EN_MAX_ITER) + pr_warn("%s: aborted\n", __func__); + } +@@ -751,6 +783,11 @@ static int mlx5e_create_sq(struct mlx5e_channel *c, + sq->bf_budget = MLX5E_SQ_BF_BUDGET; + sq->edge = (sq->wq.sz_m1 + 1) - MLX5_SEND_WQE_MAX_WQEBBS; + ++#ifdef DEV_NETMAP ++ if (mlx5e_netmap_configure_tx_ring(priv, txq_ix)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_sq_wq_destroy: +@@ -913,9 +950,14 @@ static void mlx5e_close_sq(struct mlx5e_priv *priv, struct mlx5e_sq *sq) + napi_synchronize(&sq->channel->napi); /* prevent netif_tx_wake_queue */ + netif_tx_disable_queue(sq->txq); + +- /* ensure hw is notified of all pending wqes */ +- if (mlx5e_sq_has_room_for(sq, 1)) +- mlx5e_send_nop(sq, true); ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(priv->netdev))) ++#endif ++ { ++ /* ensure hw is notified of all pending wqes */ ++ if (mlx5e_sq_has_room_for(sq, 1)) ++ mlx5e_send_nop(sq, true); ++ } + + err = mlx5e_modify_sq(sq, MLX5_SQC_STATE_RDY, MLX5_SQC_STATE_ERR, false, 0); + if (!priv->internal_error && !err) { +@@ -924,6 +966,11 @@ static void mlx5e_close_sq(struct mlx5e_priv *priv, struct mlx5e_sq *sq) + test_bit(MLX5E_SQ_TX_TIMEOUT, &sq->state)) + break; + msleep(MLX5_EN_MSLEEP_QUANT); ++ ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(priv->netdev))) ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif + } + + if (i == MLX5_EN_MAX_ITER) +@@ -1400,7 +1447,7 @@ static void mlx5e_build_rq_param(struct mlx5e_priv *priv, + MLX5_SET(wq, wq, pd, priv->pdn); + MLX5_SET(rqc, rqc, counter_set_id, priv->counter_set_id); + +- if (MLX5_CAP_GEN(priv->mdev, striding_rq) == RQ_TYPE_STRIDE) { ++ if (NETMAP_GET_RQ_TYPE(priv->mdev) == RQ_TYPE_STRIDE) { + MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_STRQ); + MLX5_SET(wq, wq, log_wqe_num_of_strides, + MLX5E_PARAMS_DEFAULT_LOG_WQE_NUM_STRIDES); +@@ -1451,7 +1498,7 @@ static void mlx5e_build_rx_cq_param(struct mlx5e_priv *priv, + MLX5_SET(cqc, cqc, cqe_comp_en, 1); + } + +- if (MLX5_CAP_GEN(priv->mdev, striding_rq) == RQ_TYPE_STRIDE) { ++ if (NETMAP_GET_RQ_TYPE(priv->mdev) == RQ_TYPE_STRIDE) { + MLX5_SET(cqc, cqc, log_cq_size, priv->params.log_rq_size + + ilog2(MLX5E_PARAMS_HW_NUM_STRIDES_BASIC_VAL) + MLX5E_PARAMS_DEFAULT_LOG_WQE_NUM_STRIDES); + /* Currently disable compressed with striding */ +@@ -2185,6 +2232,10 @@ int mlx5e_open_locked(struct net_device *netdev) + #endif + mlx5e_set_rx_mode_core(priv); + ++#ifdef DEV_NETMAP ++ netmap_enable_all_rings(netdev); /* NOP if netmap not in use */ ++#endif ++ + queue_delayed_work(priv->wq, &priv->update_stats_work, 0); + queue_delayed_work(priv->wq, &priv->service_task, 0); + +@@ -2246,6 +2297,10 @@ int mlx5e_close_locked(struct net_device *netdev) + } + clear_bit(MLX5E_STATE_OPENED, &priv->state); + ++#ifdef DEV_NETMAP ++ netmap_disable_all_rings(netdev); ++#endif ++ + mlx5e_set_rx_mode_core(priv); + #if defined(HAVE_VXLAN_ENABLED) && defined(HAVE_VXLAN_DYNAMIC_PORT) + mlx5e_vxlan_cleanup(priv); +@@ -3077,16 +3132,16 @@ static void mlx5e_build_netdev_priv(struct mlx5_core_dev *mdev, + netdev_rss_key_fill(priv->params.toeplitz_hash_key, + sizeof(priv->params.toeplitz_hash_key)); + +- if (MLX5_CAP_GEN(mdev, striding_rq)) { ++ if (NETMAP_GET_RQ_TYPE(mdev)) { /* Add netmap support */ + /* TODO ethtoo for these params */ + priv->params.log_rq_size = MLX5E_PARAMS_DEFAULT_LOG_STRIDING_RQ_SIZE; + } + priv->params.min_rx_wqes = +- mlx5_min_rx_wqes(MLX5_CAP_GEN(mdev, striding_rq), ++ mlx5_min_rx_wqes(NETMAP_GET_RQ_TYPE(mdev), + BIT(priv->params.log_rq_size)); + /* TODO: add user ability to configure lro wqe size */ + /* Enable LRO by default in case of strided RQ is supported */ +- if (MLX5_CAP_GEN(mdev, striding_rq) && MLX5_CAP_ETH(mdev, lro_cap)) { ++ if (NETMAP_GET_RQ_TYPE(mdev) && MLX5_CAP_ETH(mdev, lro_cap)) { + priv->params.lro_en = true; + #ifdef CONFIG_COMPAT_LRO_ENABLED_IPOIB + priv->pflags |= MLX5E_PRIV_FLAG_HWLRO; +@@ -3353,6 +3408,10 @@ static void *mlx5e_create_netdev(struct mlx5_core_dev *mdev) + if (err) + goto err_unregister_netdev; + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_attach(priv); ++#endif /* DEV_NETMAP */ ++ + return priv; + + err_unregister_netdev: +@@ -3387,6 +3446,10 @@ static void mlx5e_destroy_netdev(struct mlx5_core_dev *mdev, void *vpriv) + struct mlx5e_priv *priv = vpriv; + struct net_device *netdev = priv->netdev; + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + mlx5e_sysfs_remove(netdev); + + if (test_bit(MLX5_INTERFACE_STATE_SHUTDOWN, &mdev->intf_state)) +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +index f8bc512..d09a7e2 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +@@ -40,19 +40,23 @@ + (priv->tstamp.hwtstamp_config.rx_filter == \ + HWTSTAMP_FILTER_ALL) + +-static inline void mlx5e_read_cqe_slot(struct mlx5e_cq *cq, u32 cc, void *data) ++/* Removed "static" from next four functions so they can ++ * be used from mlx5_netmap_linux.c for handling compressed ++ * CQEs in mlx5e_netmap_rxsync() ++ */ ++inline void mlx5e_read_cqe_slot(struct mlx5e_cq *cq, u32 cc, void *data) + { + memcpy(data, mlx5_cqwq_get_wqe(&cq->wq, (cc & cq->wq.sz_m1)), + sizeof(struct mlx5_cqe64)); + } + +-static inline void mlx5e_write_cqe_slot(struct mlx5e_cq *cq, u32 cc, void *data) ++inline void mlx5e_write_cqe_slot(struct mlx5e_cq *cq, u32 cc, void *data) + { + memcpy(mlx5_cqwq_get_wqe(&cq->wq, cc & cq->wq.sz_m1), + data, sizeof(struct mlx5_cqe64)); + } + +-static inline void mlx5e_decompress_cqe(struct mlx5e_cq *cq, ++inline void mlx5e_decompress_cqe(struct mlx5e_cq *cq, + struct mlx5_cqe64 *title, + struct mlx5_mini_cqe8 *mini, + u16 wqe_counter, int i) +@@ -65,7 +69,7 @@ static inline void mlx5e_decompress_cqe(struct mlx5e_cq *cq, + } + + #define MLX5E_MINI_ARRAY_SZ 8 +-static void mlx5e_decompress_cqes(struct mlx5e_cq *cq) ++void mlx5e_decompress_cqes(struct mlx5e_cq *cq) + { + struct mlx5_mini_cqe8 mini_array[8]; + struct mlx5_cqe64 title; +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +index 721fabc..2526e79 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +@@ -34,6 +34,14 @@ + #include + #include + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + void mlx5e_prefetch_cqe(struct mlx5e_cq *cq) + { + struct mlx5_cqwq *wq = &cq->wq; +@@ -92,6 +100,50 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) + + clear_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags); + ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(c->netdev))) { ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ struct mlx5e_rq *rq = &c->rq; ++ int dummy; ++ ++ /* Wake netmap rx client. This results in a call to ++ * mlx5e_netmap_rxsync() which will check for any ++ * received packets and process them ++ */ ++ netmap_rx_irq(rq->netdev, rq->ix, &dummy); ++ ++ for (i = 0; i < c->num_tc; i++) { ++ ++ struct mlx5e_cq *scq = &c->sq[i].cq; ++ ++ /* Wake netmap tx client. This results in a call to ++ * mlx5e_netmap_txsync() which will check if a batch ++ * of packets has finished sending and recycle the ++ * buffers ++ */ ++ netmap_tx_irq(scq->channel->netdev, scq->channel->ix); ++ } ++ ++ /* cq interrupts are not re-armed until the end of the ++ * mlx5e_netmap_*sync() functions so we don't get more ++ * interrupts if a call to those is already pending or in progress. ++ */ ++ napi_complete(napi); ++ ++ /* avoid losing completion event during/after polling cqs */ ++ if (test_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags)) { ++ napi_schedule(napi); /* request another call to this func */ ++ } ++ ++ return 0; ++ } ++#endif ++ + busy |= mlx5e_poll_rx_cq(&c->rq.cq, budget); + + busy |= mlx5e_post_rx_wqes(&c->rq); diff --git a/LINUX/final-patches/mellanox--mlx5--4.5 b/LINUX/final-patches/mellanox--mlx5--4.5 new file mode 100644 index 000000000..978a2589c --- /dev/null +++ b/LINUX/final-patches/mellanox--mlx5--4.5 @@ -0,0 +1,317 @@ +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +index bda9ec7..74cf699 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +@@ -1,8 +1,8 @@ + # SPDX-License-Identifier: GPL-2.0 +-obj-$(CONFIG_MLX5_CORE) += mlx5_core.o ++obj-$(CONFIG_MLX5_CORE) += mlx5_core$(NETMAP_DRIVER_SUFFIX).o + subdir-ccflags-y += -I$(src) + +-mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + health.o mcg.o cq.o srq.o srq_exp.o alloc.o qp.o port.o mr.o pd.o \ + mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o \ + fs_counters.o rl.o lag.o dev.o wq.o lib/gid.o lib/clock.o \ +@@ -11,25 +11,25 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + icmd.o capi.o diag/fw_tracer.o diag/diag_cnt.o \ + eswitch_devlink_compat.o + +-mlx5_core-$(CONFIG_MLX5_ACCEL) += accel/ipsec.o accel/tls.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ACCEL) += accel/ipsec.o accel/tls.o + +-mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ + fpga/ipsec.o fpga/tls.o fpga/trans.o fpga/xfer.o + +-mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + en_tx.o en_rx.o en_dim.o en_txrx.o en_stats.o vxlan.o en_sysfs.o en_ecn.o \ + en_arfs.o en_fs_ethtool.o en_selftest.o en/port.o en_debugfs.o en_sniffer.o + +-mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MPFS) += lib/mpfs.o + +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o en_rep.o en_tc.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o en_rep.o en_tc.o + +-mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o + +-mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o + +-mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ + en_accel/ipsec_stats.o + +-mlx5_core-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index f0a23e9..3d7e6e5 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -57,6 +57,16 @@ + #endif + #include "en/port.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#define NETMAP_MLX5_MAIN ++#define DEV_NETMAP ++#include "mlx5_netmap_linux.h" ++#endif ++ + struct mlx5e_rq_param { + u32 rqc[MLX5_ST_SZ_DW(rqc)]; + struct mlx5_wq_param wq; +@@ -89,6 +99,9 @@ struct mlx5e_channel_param { + + bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) + { ++#ifdef DEV_NETMAP ++ return 0; ++#endif + bool striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && + MLX5_CAP_GEN(mdev, umr_ptr_rlky) && + MLX5_CAP_ETH(mdev, reg_umr_sq); +@@ -841,6 +854,10 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, + rq->dim_obj.dim.mode = NET_DIM_CQ_PERIOD_MODE_START_FROM_EQE; + } + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_configure_rx_ring(rq, rq->ix); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_free: +@@ -1062,6 +1079,12 @@ static int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time) + unsigned long exp_time = jiffies + msecs_to_jiffies(wait_time); + struct mlx5e_channel *c = rq->channel; + ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(c->netdev); ++ if (nm_netmap_on(na) && na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_ON) ++ return 0; /* no need to wait when netmap has built wqes */ ++#endif ++ + u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5e_rqwq_get_size(rq)); + + do { +@@ -1104,6 +1127,10 @@ static void mlx5e_free_rx_descs(struct mlx5e_rq *rq) + + while (!mlx5_wq_cyc_is_empty(wq)) { + wqe_ix = mlx5_wq_cyc_get_tail(wq); ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->channel->netdev); ++ if (!nm_netmap_on(na) || na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + rq->dealloc_wqe(rq, wqe_ix); + mlx5_wq_cyc_pop(wq); + } +@@ -1213,6 +1240,9 @@ static void mlx5e_activate_rq(struct mlx5e_rq *rq) + + u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(rq->channel->netdev)) || NA(rq->channel->netdev)->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); + sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP; + nopwqe = mlx5e_post_nop(wq, sq->sqn, &sq->pc); +@@ -1415,6 +1445,11 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, + INIT_WORK(&sq->dim_obj.dim.work, mlx5e_tx_dim_work); + sq->dim_obj.dim.mode = params->tx_cq_moderation.cq_period_mode; + ++#ifdef DEV_NETMAP ++ if (mlx5e_netmap_configure_tx_ring(c->priv, txq_ix)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_sq_wq_destroy: +@@ -1628,6 +1663,9 @@ static void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) + netif_tx_disable_queue(sq->txq); + + /* last doorbell out, godspeed .. */ ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(sq->txq->dev))) // TODO ++#endif + if (mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1)) { + u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + struct mlx5e_tx_wqe *nop; +@@ -1645,6 +1683,12 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) + struct mlx5_rate_limit rl = {0}; + + cancel_work_sync(&sq->dim_obj.dim.work); ++ ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif ++ + mlx5e_destroy_sq(mdev, sq->sqn); + if (sq->rate_limit) { + rl.rate = sq->rate_limit; +@@ -1663,6 +1707,10 @@ static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq) + return 0; + + msleep(20); ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif + } + + netdev_err(sq->channel->netdev, +@@ -3306,6 +3354,10 @@ int mlx5e_open_locked(struct net_device *netdev) + if (priv->profile->update_carrier) + priv->profile->update_carrier(priv); + ++#ifdef DEV_NETMAP ++ netmap_enable_all_rings(netdev); /* NOP if netmap not in use */ ++#endif ++ + if (priv->profile->update_stats) + queue_delayed_work(priv->wq, &priv->update_stats_work, 0); + +@@ -3352,6 +3404,10 @@ int mlx5e_close_locked(struct net_device *netdev) + + clear_bit(MLX5E_STATE_OPENED, &priv->state); + ++#ifdef DEV_NETMAP ++ netmap_disable_all_rings(netdev); ++#endif ++ + if (MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_SNIFFER)) { + mlx5e_sniffer_stop(priv); + MLX5E_SET_PFLAG(&priv->channels.params, MLX5E_PFLAG_SNIFFER, 0); +@@ -5977,6 +6033,11 @@ void mlx5e_destroy_netdev(struct mlx5e_priv *priv) + const struct mlx5e_profile *profile = priv->profile; + struct net_device *netdev = priv->netdev; + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ ++ + destroy_workqueue(priv->wq); + if (profile->cleanup) + profile->cleanup(priv); +@@ -6089,6 +6150,11 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) + mlx5e_dcbnl_init_app(priv); + #endif + #endif ++ ++#ifdef DEV_NETMAP ++ mlx5e_netmap_attach(priv); ++#endif /* DEV_NETMAP */ ++ + return priv; + + err_unregister_netdev: +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +index d85e0f3..4b0068f 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +@@ -50,6 +50,14 @@ + #include "en_accel/ipsec_rxtx.h" + #include "lib/clock.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline bool mlx5e_rx_hw_stamp(struct hwtstamp_config *config) + { + return config->rx_filter == HWTSTAMP_FILTER_ALL; +@@ -155,7 +163,7 @@ static inline u32 mlx5e_decompress_cqes_cont(struct mlx5e_rq *rq, + return cqe_count; + } + +-static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, ++u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, + struct mlx5e_cq *cq, + int budget_rem) + { +@@ -1739,6 +1747,13 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) + priv = netdev_priv(rq->netdev); + #endif + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ int dummy; ++ int nm_irq = netmap_rx_irq(rq->netdev, rq->ix, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif ++ + if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + return 0; + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +index 124b676..4864b16 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +@@ -38,6 +38,15 @@ + #include "en_accel/en_accel.h" + #include "lib/clock.h" + ++ ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + #define MLX5E_SQ_NOPS_ROOM MLX5_SEND_WQE_MAX_WQEBBS + + #if defined(CONFIG_MLX5_EN_TLS) && defined(HAVE_UAPI_LINUX_TLS_H) +@@ -550,6 +559,11 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) + + sq = container_of(cq, struct mlx5e_txqsq, cq); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ if (netmap_tx_irq(sq->channel->netdev, sq->channel->ix) != NM_IRQ_PASS) ++ return false; ++#endif ++ + if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + return false; + +@@ -676,15 +690,17 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) + continue; + } + +- for (i = 0; i < wi->num_dma; i++) { +- struct mlx5e_sq_dma *dma = +- mlx5e_dma_get(sq, sq->dma_fifo_cc++); ++ if (!nm_netmap_on(NA(sq->txq->dev))) { ++ /* do not free skbs in netmap mode */ ++ for (i = 0; i < wi->num_dma; i++) { ++ struct mlx5e_sq_dma *dma = ++ mlx5e_dma_get(sq, sq->dma_fifo_cc++); + +- mlx5e_tx_dma_unmap(sq->pdev, dma); ++ mlx5e_tx_dma_unmap(sq->pdev, dma); ++ } ++ dev_kfree_skb_any(skb); + } +- +- dev_kfree_skb_any(skb); +- sq->cc += wi->num_wqebbs; ++ sq->cc += wi->num_wqebbs; + } + } + diff --git a/LINUX/final-patches/mellanox--mlx5--4.6 b/LINUX/final-patches/mellanox--mlx5--4.6 new file mode 100644 index 000000000..d591daec5 --- /dev/null +++ b/LINUX/final-patches/mellanox--mlx5--4.6 @@ -0,0 +1,350 @@ +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +index 1c4e92b..64553a8 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +@@ -6,12 +6,12 @@ + + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_MLX5_CORE) += mlx5_core.o ++obj-$(CONFIG_MLX5_CORE) += mlx5_core$(NETMAP_DRIVER_SUFFIX).o + + # + # mlx5 core basic + # +-mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + health.o mcg.o cq.o srq.o srq_exp.o alloc.o qp.o port.o mr.o pd.o \ + mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o \ + fs_counters.o rl.o lag.o dev.o wq.o lib/gid.o \ +@@ -19,50 +19,49 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + devcom.o en_diag.o params.o fs_debugfs.o nvmf.o crdump.o icmd.o capi.o diag/tracer.o diag/diag_cnt.o \ + eswitch_devlink_compat.o + +- + # + # Netdev basic + # +-mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + en_tx.o en_rx.o en_dim.o en_txrx.o en/xdp.o en_stats.o en_sysfs.o en_ecn.o \ + en_selftest.o en/port.o en_debugfs.o en_sniffer.o + + # + # Netdev extra + # +-mlx5_core-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o +-mlx5_core-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o +-mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += en_rep.o en_tc.o lag_mp.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += en_rep.o en_tc.o lag_mp.o + + # + # Core extra + # +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o ecpf.o +-mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o ecpf.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MPFS) += lib/mpfs.o + ifneq ($(CONFIG_VXLAN),) +- mlx5_core-y += lib/vxlan.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/vxlan.o + endif + ifneq ($(CONFIG_PTP_1588_CLOCK),) +- mlx5_core-y += lib/clock.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/clock.o + endif + + # + # Ipoib netdev + # +-mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o + + # + # Accelerations & FPGA + # +-mlx5_core-$(CONFIG_MLX5_ACCEL) += accel/ipsec.o accel/tls.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ACCEL) += accel/ipsec.o accel/tls.o + +-mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ + fpga/ipsec.o fpga/tls.o fpga/trans.o fpga/xfer.o + +-mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ + en_accel/ipsec_stats.o + +-mlx5_core-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o + + CFLAGS_tracepoint.o := -I$(src) +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index ca7eb20..27dc8e4 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -57,6 +57,16 @@ + #include "en/port.h" + #include "en/xdp.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#define NETMAP_MLX5_MAIN ++#define DEV_NETMAP ++#include "mlx5_netmap_linux.h" ++#endif ++ + struct mlx5e_rq_param { + u32 rqc[MLX5_ST_SZ_DW(rqc)]; + struct mlx5_wq_param wq; +@@ -89,6 +99,9 @@ struct mlx5e_channel_param { + + bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) + { ++#ifdef DEV_NETMAP ++ return 0; ++#endif + bool striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && + MLX5_CAP_GEN(mdev, umr_ptr_rlky) && + MLX5_CAP_ETH(mdev, reg_umr_sq); +@@ -877,6 +890,10 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, + rq->dim_obj.dim.mode = NET_DIM_CQ_PERIOD_MODE_START_FROM_EQE; + } + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_configure_rx_ring(rq, rq->ix); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_free: +@@ -1098,6 +1115,12 @@ static int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time) + unsigned long exp_time = jiffies + msecs_to_jiffies(wait_time); + struct mlx5e_channel *c = rq->channel; + ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(c->netdev); ++ if (nm_netmap_on(na) && na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_ON) ++ return 0; /* no need to wait when netmap has built wqes */ ++#endif ++ + u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5e_rqwq_get_size(rq)); + + do { +@@ -1140,6 +1163,10 @@ static void mlx5e_free_rx_descs(struct mlx5e_rq *rq) + + while (!mlx5_wq_cyc_is_empty(wq)) { + wqe_ix = mlx5_wq_cyc_get_tail(wq); ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->channel->netdev); ++ if (!nm_netmap_on(na) || na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + rq->dealloc_wqe(rq, wqe_ix); + mlx5_wq_cyc_pop(wq); + } +@@ -1252,6 +1279,9 @@ static void mlx5e_activate_rq(struct mlx5e_rq *rq) + + u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(rq->channel->netdev)) || NA(rq->channel->netdev)->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); + sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP; + nopwqe = mlx5e_post_nop(wq, sq->sqn, &sq->pc); +@@ -1471,6 +1501,11 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, + INIT_WORK(&sq->dim_obj.dim.work, mlx5e_tx_dim_work); + sq->dim_obj.dim.mode = params->tx_cq_moderation.cq_period_mode; + ++#ifdef DEV_NETMAP ++ if (mlx5e_netmap_configure_tx_ring(c->priv, txq_ix)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_sq_wq_destroy: +@@ -1685,6 +1720,9 @@ static void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) + netif_tx_disable_queue(sq->txq); + + /* last doorbell out, godspeed .. */ ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(sq->txq->dev))) // TODO ++#endif + if (mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1)) { + u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + struct mlx5e_tx_wqe *nop; +@@ -1702,6 +1740,12 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) + struct mlx5_rate_limit rl = {0}; + + cancel_work_sync(&sq->dim_obj.dim.work); ++ ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif ++ + mlx5e_destroy_sq(mdev, sq->sqn); + if (sq->rate_limit) { + rl.rate = sq->rate_limit; +@@ -1720,6 +1764,10 @@ static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq) + return 0; + + msleep(20); ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif + } + + netdev_err(sq->channel->netdev, +@@ -3539,6 +3587,10 @@ int mlx5e_open_locked(struct net_device *netdev) + if (priv->profile->update_carrier) + priv->profile->update_carrier(priv); + ++#ifdef DEV_NETMAP ++ netmap_enable_all_rings(netdev); /* NOP if netmap not in use */ ++#endif ++ + return 0; + + err_clear_state_opened_flag: +@@ -3583,6 +3635,10 @@ int mlx5e_close_locked(struct net_device *netdev) + + clear_bit(MLX5E_STATE_OPENED, &priv->state); + ++#ifdef DEV_NETMAP ++ netmap_disable_all_rings(netdev); ++#endif ++ + if (MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_SNIFFER)) { + mlx5e_sniffer_stop(priv); + MLX5E_SET_PFLAG(&priv->channels.params, MLX5E_PFLAG_SNIFFER, 0); +@@ -6405,6 +6461,10 @@ void mlx5e_destroy_netdev(struct mlx5e_priv *priv) + const struct mlx5e_profile *profile = priv->profile; + struct net_device *netdev = priv->netdev; + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + if (profile->cleanup) + profile->cleanup(priv); + free_netdev(netdev); +@@ -6505,6 +6565,11 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) + mlx5e_dcbnl_init_app(priv); + #endif + #endif ++ ++#ifdef DEV_NETMAP ++ mlx5e_netmap_attach(priv); ++#endif /* DEV_NETMAP */ ++ + return priv; + + err_unregister_netdev: +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +index c4e7420..b61fd09 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +@@ -49,6 +49,14 @@ + #include "lib/clock.h" + #include "en/xdp.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline bool mlx5e_rx_hw_stamp(struct hwtstamp_config *config) + { + return config->rx_filter == HWTSTAMP_FILTER_ALL; +@@ -154,7 +162,7 @@ static inline u32 mlx5e_decompress_cqes_cont(struct mlx5e_rq *rq, + return cqe_count; + } + +-static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, ++u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, + struct mlx5e_cq *cq, + int budget_rem) + { +@@ -1630,6 +1638,13 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) + priv = netdev_priv(rq->netdev); + #endif + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ int dummy; ++ int nm_irq = netmap_rx_irq(rq->netdev, rq->ix, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif ++ + if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + return 0; + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +index b54766b..fe80042 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +@@ -38,6 +38,15 @@ + #include "en_accel/en_accel.h" + #include "lib/clock.h" + ++ ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + #define MLX5E_SQ_NOPS_ROOM MLX5_SEND_WQE_MAX_WQEBBS + + #if defined(CONFIG_MLX5_EN_TLS) && defined(HAVE_UAPI_LINUX_TLS_H) +@@ -630,6 +639,11 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) + + sq = container_of(cq, struct mlx5e_txqsq, cq); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ if (netmap_tx_irq(sq->channel->netdev, sq->channel->ix) != NM_IRQ_PASS) ++ return false; ++#endif ++ + if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + return false; + +@@ -760,15 +774,17 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) + continue; + } + +- for (i = 0; i < wi->num_dma; i++) { +- struct mlx5e_sq_dma *dma = +- mlx5e_dma_get(sq, sq->dma_fifo_cc++); ++ if (!nm_netmap_on(NA(sq->txq->dev))) { ++ /* do not free skbs in netmap mode */ ++ for (i = 0; i < wi->num_dma; i++) { ++ struct mlx5e_sq_dma *dma = ++ mlx5e_dma_get(sq, sq->dma_fifo_cc++); + +- mlx5e_tx_dma_unmap(sq->pdev, dma); ++ mlx5e_tx_dma_unmap(sq->pdev, dma); ++ } ++ dev_kfree_skb_any(skb); + } +- +- dev_kfree_skb_any(skb); +- sq->cc += wi->num_wqebbs; ++ sq->cc += wi->num_wqebbs; + } + } + diff --git a/LINUX/final-patches/mellanox--mlx5--5.0 b/LINUX/final-patches/mellanox--mlx5--5.0 new file mode 100644 index 000000000..a4a510e01 --- /dev/null +++ b/LINUX/final-patches/mellanox--mlx5--5.0 @@ -0,0 +1,380 @@ +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +index 88f7ea5..ed5507a 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +@@ -6,12 +6,12 @@ + + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_MLX5_CORE) += mlx5_core.o ++obj-$(CONFIG_MLX5_CORE) += mlx5_core$(NETMAP_DRIVER_SUFFIX).o + + # + # mlx5 core basic + # +-mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + health.o mcg.o cq.o alloc.o qp.o port.o mr.o pd.o \ + transobj.o vport.o sriov.o fs_cmd.o fs_core.o pci_irq.o \ + fs_counters.o rl.o lag.o dev.o events.o wq.o lib/gid.o lib/dm.o \ +@@ -20,11 +20,11 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + fw_exp.o sriov_sysfs.o mst_dump.o en_diag.o params.o crdump.o \ + icmd.o capi.o diag/diag_cnt.o eswitch_devlink_compat.o devlink.o + +-mlx5_core-$(CONFIG_ENABLE_MLX5_FS_DEBUGFS) += fs_debugfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_ENABLE_MLX5_FS_DEBUGFS) += fs_debugfs.o + # + # Netdev basic + # +-mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + en_tx.o en_rx.o en_dim.o en_txrx.o en/xdp.o en_stats.o en_sysfs.o en_ecn.o \ + en_selftest.o en/port.o en/monitor_stats.o en/health.o \ + en/reporter_tx.o en/reporter_rx.o en/params.o en_debugfs.o en_sniffer.o +@@ -32,16 +32,16 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + # + # Netdev extra + # +-mlx5_core-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o +-mlx5_core-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o +-mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += en_rep.o en_tc.o en/tc_tun.o lib/port_tun.o lag_mp.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += en_rep.o en_tc.o en/tc_tun.o lib/port_tun.o lag_mp.o \ + miniflow.o miniflow_aging.o en_bond.o lib/geneve.o \ + en/tc_tun_vxlan.o en/tc_tun_gre.o en/tc_tun_geneve.o + +-mlx5_core-$(CONFIG_MLX5_EN_ACCEL_FS) += en_accel/fs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_ACCEL_FS) += en_accel/fs.o + +-mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ + steering/dr_matcher.o steering/dr_rule.o \ + steering/dr_icm_pool.o \ + steering/dr_ste.o steering/dr_send.o \ +@@ -51,38 +51,39 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o + # + # Core extra + # +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o ecpf.o rdma.o +-mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o ecpf.o rdma.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MPFS) += lib/mpfs.o + ifneq ($(CONFIG_VXLAN),) +- mlx5_core-y += lib/vxlan.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/vxlan.o + endif + ifneq ($(CONFIG_PTP_1588_CLOCK),) +- mlx5_core-y += lib/clock.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/clock.o + endif + + # + # Ipoib netdev + # +-mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o + + # + # Accelerations & FPGA + # +-mlx5_core-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o +-mlx5_core-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o +-mlx5_core-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o + +-mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ + fpga/trans.o fpga/xfer.o + +-mlx5_core-$(CONFIG_MLX5_IPSEC) += en_accel/ipsec_steering.o en_accel/ipsec_offload.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_IPSEC) += en_accel/ipsec_steering.o en_accel/ipsec_offload.o + +-mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ + en_accel/ipsec_stats.o + +-mlx5_core-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ ++mlx5_core-$(NETMAP_DRIVER_SUFFIX)$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ + en_accel/ktls.o en_accel/ktls_tx.o ++ + # + # Mdev basic + # +-mlx5_core-$(CONFIG_MLX5_MDEV) += meddev/sf.o meddev/mdev.o meddev/mdev_driver.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MDEV) += meddev/sf.o meddev/mdev.o meddev/mdev_driver.o +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +index a34b25a..8ac923a 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +@@ -13,6 +13,10 @@ static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq) + return 0; + + msleep(20); ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif + } + + netdev_err(sq->channel->netdev, +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index 06a1fb0..a1ed0d6 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -69,6 +69,16 @@ + #include "lib/mlx5.h" + #include "en_accel/ipsec_steering.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#define NETMAP_MLX5_MAIN ++#define DEV_NETMAP ++#include "mlx5_netmap_linux.h" ++#endif ++ + struct mlx5e_rq_param { + u32 rqc[MLX5_ST_SZ_DW(rqc)]; + struct mlx5_wq_param wq; +@@ -102,6 +112,9 @@ struct mlx5e_channel_param { + + bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) + { ++#ifdef DEV_NETMAP ++ return 0; ++#endif + bool striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && + MLX5_CAP_GEN(mdev, umr_ptr_rlky) && + MLX5_CAP_ETH(mdev, reg_umr_sq); +@@ -1000,6 +1013,12 @@ static int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time) + unsigned long exp_time = jiffies + msecs_to_jiffies(wait_time); + struct mlx5e_channel *c = rq->channel; + ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(c->netdev); ++ if (nm_netmap_on(na) && na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_ON) ++ return 0; /* no need to wait when netmap has built wqes */ ++#endif ++ + u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5e_rqwq_get_size(rq)); + + do { +@@ -1047,6 +1066,10 @@ void mlx5e_free_rx_descs(struct mlx5e_rq *rq) + + while (!mlx5_wq_cyc_is_empty(wq)) { + wqe_ix = mlx5_wq_cyc_get_tail(wq); ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->channel->netdev); ++ if (!nm_netmap_on(na) || na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + rq->dealloc_wqe(rq, wqe_ix); + mlx5_wq_cyc_pop(wq); + } +@@ -1152,6 +1175,10 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, + #endif + __set_bit(MLX5E_RQ_STATE_NO_CSUM_COMPLETE, &c->rq.state); + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_configure_rx_ring(rq, rq->ix); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_destroy_rq: +@@ -1164,6 +1191,9 @@ err_free_rq: + + void mlx5e_activate_rq(struct mlx5e_rq *rq) + { ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(rq->channel->netdev)) || NA(rq->channel->netdev)->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); + mlx5e_trigger_irq(&rq->channel->icosq); + } +@@ -1427,6 +1457,11 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, + INIT_WORK(&sq->dim_obj.dim.work, mlx5e_tx_dim_work); + sq->dim_obj.dim.mode = params->tx_cq_moderation.cq_period_mode; + ++#ifdef DEV_NETMAP ++ if (mlx5e_netmap_configure_tx_ring(c->priv, txq_ix)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_sq_wq_destroy: +@@ -1620,6 +1655,9 @@ static void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) + mlx5e_tx_disable_queue(sq->txq); + + /* last doorbell out, godspeed .. */ ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(sq->txq->dev))) // TODO ++#endif + if (mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1)) { + u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + struct mlx5e_tx_wqe_info *wi; +@@ -1642,6 +1680,12 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) + + cancel_work_sync(&sq->dim_obj.dim.work); + cancel_work_sync(&sq->recover_work); ++ ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif ++ + mlx5e_destroy_sq(mdev, sq->sqn); + if (sq->rate_limit) { + rl.rate = sq->rate_limit; +@@ -3485,6 +3529,11 @@ int mlx5e_open_locked(struct net_device *netdev) + priv->profile->update_carrier(priv); + + mlx5e_queue_update_stats(priv); ++ ++#ifdef DEV_NETMAP ++ netmap_enable_all_rings(netdev); /* NOP if netmap not in use */ ++#endif ++ + return 0; + + err_clear_state_opened_flag: +@@ -3529,6 +3578,10 @@ int mlx5e_close_locked(struct net_device *netdev) + + clear_bit(MLX5E_STATE_OPENED, &priv->state); + ++#ifdef DEV_NETMAP ++ netmap_disable_all_rings(netdev); ++#endif ++ + if (MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_SNIFFER)) { + mlx5e_sniffer_stop(priv); + MLX5E_SET_PFLAG(&priv->channels.params, MLX5E_PFLAG_SNIFFER, 0); +@@ -6556,6 +6609,10 @@ void mlx5e_destroy_netdev(struct mlx5e_priv *priv) + const struct mlx5e_profile *profile = priv->profile; + struct net_device *netdev = priv->netdev; + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + if (profile->cleanup) + profile->cleanup(priv); + free_netdev(netdev); +@@ -6675,6 +6732,11 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) + mlx5e_dcbnl_init_app(priv); + #endif + #endif ++ ++#ifdef DEV_NETMAP ++ mlx5e_netmap_attach(priv); ++#endif /* DEV_NETMAP */ ++ + return priv; + + err_unregister_netdev: +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +index 535367a..e68cac8 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +@@ -51,6 +51,14 @@ + #include "en/xdp.h" + #include "en/health.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline bool mlx5e_rx_hw_stamp(struct hwtstamp_config *config) + { + return config->rx_filter == HWTSTAMP_FILTER_ALL; +@@ -169,7 +177,7 @@ static inline u32 mlx5e_decompress_cqes_cont(struct mlx5e_rq *rq, + return cqe_count; + } + +-static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, ++u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, + struct mlx5_cqwq *wq, + int budget_rem) + { +@@ -1754,6 +1762,13 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) + priv = netdev_priv(rq->netdev); + #endif + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ int dummy; ++ int nm_irq = netmap_rx_irq(rq->netdev, rq->ix, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif ++ + if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + return 0; + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +index 591ee79..da75df5 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +@@ -41,8 +41,16 @@ + #include "en_accel/ktls.h" + #include "lib/clock.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline void mlx5e_read_cqe_slot(struct mlx5_cqwq *wq, +- u32 cqcc, void *data) ++ u32 cqcc, void *data) + { + u32 ci = mlx5_cqwq_ctr2ix(wq, cqcc); + +@@ -706,6 +714,11 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) + + sq = container_of(cq, struct mlx5e_txqsq, cq); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ if (netmap_tx_irq(sq->channel->netdev, sq->channel->ix) != NM_IRQ_PASS) ++ return false; ++#endif ++ + if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + return false; + +@@ -845,14 +858,16 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) + continue; + } + +- for (i = 0; i < wi->num_dma; i++) { +- struct mlx5e_sq_dma *dma = +- mlx5e_dma_get(sq, dma_fifo_cc++); ++ if (!nm_netmap_on(NA(sq->txq->dev))) { ++ /* do not free skbs in netmap mode */ ++ for (i = 0; i < wi->num_dma; i++) { ++ struct mlx5e_sq_dma *dma = ++ mlx5e_dma_get(sq, sq->dma_fifo_cc++); + +- mlx5e_tx_dma_unmap(sq->pdev, dma); ++ mlx5e_tx_dma_unmap(sq->pdev, dma); ++ } ++ dev_kfree_skb_any(skb); + } +- +- dev_kfree_skb_any(skb); + sqcc += wi->num_wqebbs; + } + diff --git a/LINUX/final-patches/mellanox--mlx5--5.1 b/LINUX/final-patches/mellanox--mlx5--5.1 new file mode 100644 index 000000000..76fb1927d --- /dev/null +++ b/LINUX/final-patches/mellanox--mlx5--5.1 @@ -0,0 +1,390 @@ +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +index f781b2b..1f772c2 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +@@ -8,12 +8,12 @@ + + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_MLX5_CORE) += mlx5_core.o ++obj-$(CONFIG_MLX5_CORE) += mlx5_core$(NETMAP_DRIVER_SUFFIX).o + + # + # mlx5 core basic + # +-mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + health.o mcg.o cq.o alloc.o port.o mr.o pd.o \ + transobj.o vport.o sriov.o fs_cmd.o fs_core.o pci_irq.o \ + fs_counters.o rl.o lag.o dev.o events.o wq.o lib/gid.o \ +@@ -22,12 +22,12 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + sriov_sysfs.o mst_dump.o en_diag.o params.o crdump.o \ + diag/diag_cnt.o eswitch_devlink_compat.o + +-mlx5_core-y += compat.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += compat.o + + # + # Netdev basic + # +-mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + en_tx.o en_rx.o en_dim.o en_txrx.o en/xdp.o en_stats.o en_sysfs.o en_ecn.o\ + en_selftest.o en/port.o en/monitor_stats.o en/health.o \ + en/reporter_tx.o en/reporter_rx.o en/params.o en_debugfs.o \ +@@ -36,60 +36,60 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + # + # Netdev extra + # +-mlx5_core-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o +-mlx5_core-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o +-mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o +-mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += en_rep.o lib/geneve.o lib/port_tun.o lag_mp.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += en_rep.o lib/geneve.o lib/port_tun.o lag_mp.o \ + en/mod_hdr.o en/rep/bond.o +-mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ + en/mapping.o esw/chains.o en/tc_tun.o \ + en/tc_tun_vxlan.o en/tc_tun_gre.o en/tc_tun_geneve.o \ + diag/en_tc_tracepoint.o +-mlx5_core-$(CONFIG_MLX5_TC_CT) += en/tc_ct.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_TC_CT) += en/tc_ct.o + + # + # Core extra + # +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \ + ecpf.o rdma.o +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ + esw/acl/egress_lgcy.o esw/acl/egress_ofld.o \ + esw/acl/ingress_lgcy.o esw/acl/ingress_ofld.o + +-mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MPFS) += lib/mpfs.o + ifneq ($(CONFIG_VXLAN),) +- mlx5_core-y += lib/vxlan.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/vxlan.o + endif + ifneq ($(CONFIG_PTP_1588_CLOCK),) +- mlx5_core-y += lib/clock.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/clock.o + endif +-mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o + + # + # Ipoib netdev + # +-mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o + + # + # Accelerations & FPGA + # +-mlx5_core-$(CONFIG_MLX5_IPSEC) += accel/ipsec_offload.o +-mlx5_core-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o +-mlx5_core-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o +-mlx5_core-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_IPSEC) += accel/ipsec_offload.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o + +-mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ + fpga/tls.o fpga/trans.o fpga/xfer.o + +-mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ + en_accel/ipsec_stats.o en_accel/ipsec_fs.o esw/ipsec.o + +-mlx5_core-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ + en_accel/fs_tcp.o en_accel/ktls.o en_accel/ktls_txrx.o \ + en_accel/ktls_tx.o en_accel/ktls_rx.o + +-mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ + steering/dr_matcher.o steering/dr_rule.o \ + steering/dr_icm_pool.o \ + steering/dr_ste.o \ +@@ -101,4 +101,4 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o + # + # Mdev basic + # +-mlx5_core-$(CONFIG_MLX5_MDEV) += meddev/sf.o meddev/mdev.o meddev/mdev_driver.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MDEV) += meddev/sf.o meddev/mdev.o meddev/mdev_driver.o +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +index 62a38bc..d45ba17 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +@@ -13,6 +13,10 @@ static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq) + return 0; + + msleep(20); ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif + } + + netdev_err(sq->channel->netdev, +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index d40b62e..b5767c5 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -78,8 +78,21 @@ + #include "fpga/ipsec.h" + #include "compat.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#define NETMAP_MLX5_MAIN ++#define DEV_NETMAP ++#include "mlx5_netmap_linux.h" ++#endif ++ + bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) + { ++#ifdef DEV_NETMAP ++ return 0; ++#endif + bool striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && + MLX5_CAP_GEN(mdev, umr_ptr_rlky) && + MLX5_CAP_ETH(mdev, reg_umr_sq); +@@ -1061,6 +1074,12 @@ int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time) + unsigned long exp_time = jiffies + msecs_to_jiffies(wait_time); + struct mlx5e_channel *c = rq->channel; + ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(c->netdev); ++ if (nm_netmap_on(na) && na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_ON) ++ return 0; /* no need to wait when netmap has built wqes */ ++#endif ++ + u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5e_rqwq_get_size(rq)); + + do { +@@ -1125,6 +1144,10 @@ void mlx5e_free_rx_descs(struct mlx5e_rq *rq) + + while (!mlx5_wq_cyc_is_empty(wq)) { + wqe_ix = mlx5_wq_cyc_get_tail(wq); ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->channel->netdev); ++ if (!nm_netmap_on(na) || na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + rq->dealloc_wqe(rq, wqe_ix); + mlx5_wq_cyc_pop(wq); + } +@@ -1233,6 +1256,10 @@ int mlx5e_open_rq(struct mlx5e_channel *c, struct mlx5e_params *params, + if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_SKB_XMIT_MORE)) + __set_bit(MLX5E_RQ_STATE_SKB_XMIT_MORE, &c->rq.state); + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_configure_rx_ring(rq, rq->ix); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_destroy_rq: +@@ -1245,6 +1272,9 @@ err_free_rq: + + void mlx5e_activate_rq(struct mlx5e_rq *rq) + { ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(rq->channel->netdev)) || NA(rq->channel->netdev)->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); + mlx5e_trigger_irq(&rq->channel->icosq); + } +@@ -1526,6 +1556,11 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, + INIT_WORK(&sq->dim_obj.dim.work, mlx5e_tx_dim_work); + sq->dim_obj.dim.mode = params->tx_cq_moderation.cq_period_mode; + ++#ifdef DEV_NETMAP ++ if (mlx5e_netmap_configure_tx_ring(c->priv, txq_ix)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_sq_wq_destroy: +@@ -1723,6 +1758,9 @@ static void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) + mlx5e_tx_disable_queue(sq->txq); + + /* last doorbell out, godspeed .. */ ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(sq->txq->dev))) // TODO ++#endif + if (mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1)) { + u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + struct mlx5e_tx_wqe *nop; +@@ -1744,6 +1782,12 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) + + cancel_work_sync(&sq->dim_obj.dim.work); + cancel_work_sync(&sq->recover_work); ++ ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif ++ + mlx5e_destroy_sq(mdev, sq->sqn); + if (sq->rate_limit) { + rl.rate = sq->rate_limit; +@@ -3699,6 +3743,11 @@ int mlx5e_open_locked(struct net_device *netdev) + priv->profile->update_carrier(priv); + + mlx5e_queue_update_stats(priv); ++ ++#ifdef DEV_NETMAP ++ netmap_enable_all_rings(netdev); /* NOP if netmap not in use */ ++#endif ++ + return 0; + + err_clear_state_opened_flag: +@@ -3732,6 +3781,10 @@ int mlx5e_close_locked(struct net_device *netdev) + + clear_bit(MLX5E_STATE_OPENED, &priv->state); + ++#ifdef DEV_NETMAP ++ netmap_disable_all_rings(netdev); ++#endif ++ + netif_carrier_off(priv->netdev); + mlx5e_destroy_debugfs(priv); + #if defined(CONFIG_MLX5_EN_SPECIAL_SQ) && (defined(HAVE_NDO_SET_TX_MAXRATE) || defined(HAVE_NDO_SET_TX_MAXRATE_EXTENDED)) +@@ -7048,6 +7101,10 @@ void mlx5e_destroy_netdev(struct mlx5e_priv *priv) + { + struct net_device *netdev = priv->netdev; + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + mlx5e_netdev_cleanup(netdev, priv); + free_netdev(netdev); + } +@@ -7159,6 +7216,10 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) + #endif + #endif + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_attach(priv); ++#endif /* DEV_NETMAP */ ++ + if (MLX5_ESWITCH_MANAGER(mdev)) + mlx5e_rep_register_vport_reps(mdev, priv); + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +index b5f7ba9..e03a9d3 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +@@ -63,6 +63,14 @@ static inline void mlx5e_set_skb_driver_xmit_more(struct sk_buff *skb, + skb->cb[47] = MLX5_XMIT_MORE_SKB_CB; + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline bool mlx5e_rx_hw_stamp(struct hwtstamp_config *config) + { + return config->rx_filter == HWTSTAMP_FILTER_ALL; +@@ -181,7 +189,7 @@ static inline u32 mlx5e_decompress_cqes_cont(struct mlx5e_rq *rq, + return cqe_count; + } + +-static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, ++u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, + struct mlx5_cqwq *wq, + int budget_rem) + { +@@ -1930,6 +1938,13 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) + priv = netdev_priv(rq->netdev); + #endif + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ int dummy; ++ int nm_irq = netmap_rx_irq(rq->netdev, rq->ix, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif ++ + if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + return 0; + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +index 4739f3b..f101fd4 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +@@ -40,8 +40,16 @@ + #include "en_accel/en_accel.h" + #include "lib/clock.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline void mlx5e_read_cqe_slot(struct mlx5_cqwq *wq, +- u32 cqcc, void *data) ++ u32 cqcc, void *data) + { + u32 ci = mlx5_cqwq_ctr2ix(wq, cqcc); + +@@ -695,6 +703,11 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) + + sq = container_of(cq, struct mlx5e_txqsq, cq); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ if (netmap_tx_irq(sq->channel->netdev, sq->channel->ix) != NM_IRQ_PASS) ++ return false; ++#endif ++ + if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + return false; + +@@ -833,14 +846,18 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) + continue; + } + +- for (i = 0; i < wi->num_dma; i++) { +- struct mlx5e_sq_dma *dma = +- mlx5e_dma_get(sq, dma_fifo_cc++); + +- mlx5e_tx_dma_unmap(sq->pdev, dma); +- } ++ if (!nm_netmap_on(NA(sq->txq->dev))) { ++ /* do not free skbs in netmap mode */ ++ for (i = 0; i < wi->num_dma; i++) { ++ struct mlx5e_sq_dma *dma = ++ mlx5e_dma_get(sq, dma_fifo_cc++); ++ ++ mlx5e_tx_dma_unmap(sq->pdev, dma); ++ } ++ dev_kfree_skb_any(skb); ++ } + +- dev_kfree_skb_any(skb); + npkts++; + nbytes += wi->num_bytes; + sqcc += wi->num_wqebbs; diff --git a/LINUX/final-patches/mellanox--mlx5--5.2 b/LINUX/final-patches/mellanox--mlx5--5.2 new file mode 100644 index 000000000..65f023a71 --- /dev/null +++ b/LINUX/final-patches/mellanox--mlx5--5.2 @@ -0,0 +1,410 @@ +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +index 1788bba..d91fe51 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +@@ -6,12 +6,12 @@ + + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_MLX5_CORE) += mlx5_core.o ++obj-$(CONFIG_MLX5_CORE) += mlx5_core$(NETMAP_DRIVER_SUFFIX).o + + # + # mlx5 core basic + # +-mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + health.o mcg.o cq.o alloc.o port.o mr.o pd.o \ + transobj.o vport.o sriov.o fs_cmd.o fs_core.o pci_irq.o \ + fs_counters.o rl.o lag.o dev.o events.o wq.o lib/gid.o \ +@@ -20,12 +20,12 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + mst_dump.o en_diag.o sriov_sysfs.o crdump.o diag/diag_cnt.o \ + eswitch_devlink_compat.o params.o fw_exp.o fw_reset.o + +-mlx5_core-y += compat.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += compat.o + + # + # Netdev basic + # +-mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + en_tx.o en_rx.o en_dim.o en_txrx.o en/xdp.o en_stats.o en_sysfs.o en_ecn.o \ + en_selftest.o en/port.o en/monitor_stats.o en/health.o \ + en/reporter_tx.o en/reporter_rx.o en/params.o en/xsk/umem.o \ +@@ -34,62 +34,62 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + # + # Netdev extra + # +-mlx5_core-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o +-mlx5_core-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o +-mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o +-mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += lag_mp.o lib/geneve.o lib/port_tun.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += lag_mp.o lib/geneve.o lib/port_tun.o \ + en_rep.o en/rep/bond.o en/mod_hdr.o +-mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ + en/mapping.o lib/fs_chains.o en/tc_tun.o \ + en/tc_tun_vxlan.o en/tc_tun_gre.o en/tc_tun_geneve.o \ + en/tc_tun_mplsoudp.o diag/en_tc_tracepoint.o \ + en/tc_sample.o esw/indir_table.o en/tc_tun_common.o +-mlx5_core-$(CONFIG_MLX5_TC_CT) += en/tc_ct.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_TC_CT) += en/tc_ct.o + + # + # Core extra + # +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \ + ecpf.o rdma.o +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ + esw/acl/egress_lgcy.o esw/acl/egress_ofld.o \ + esw/acl/ingress_lgcy.o esw/acl/ingress_ofld.o \ + esw/vporttbl.o + +-mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MPFS) += lib/mpfs.o + ifneq ($(CONFIG_VXLAN),) +- mlx5_core-y += lib/vxlan.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/vxlan.o + endif + ifneq ($(CONFIG_PTP_1588_CLOCK),) +- mlx5_core-y += lib/clock.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/clock.o + endif +-mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o + + # + # Ipoib netdev + # +-mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o + + # + # Accelerations & FPGA + # +-mlx5_core-$(CONFIG_MLX5_IPSEC) += accel/ipsec_offload.o +-mlx5_core-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o +-mlx5_core-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o +-mlx5_core-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_IPSEC) += accel/ipsec_offload.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o + +-mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ + fpga/tls.o fpga/trans.o fpga/xfer.o + +-mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ + en_accel/ipsec_stats.o en_accel/ipsec_fs.o esw/ipsec.o en/aso.o + +-mlx5_core-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ + en_accel/fs_tcp.o en_accel/ktls.o en_accel/ktls_txrx.o \ + en_accel/ktls_tx.o en_accel/ktls_rx.o + +-mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ + steering/dr_matcher.o steering/dr_rule.o \ + steering/dr_icm_pool.o \ + steering/dr_ste.o steering/dr_send.o \ +@@ -100,4 +100,4 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o + # + # Mdev basic + # +-mlx5_core-$(CONFIG_MLX5_MDEV) += meddev/sf.o meddev/mdev.o meddev/mdev_driver.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MDEV) += meddev/sf.o meddev/mdev.o meddev/mdev_driver.o +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +index a6dceb6..a3cb81d 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +@@ -14,6 +14,10 @@ static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq) + return 0; + + msleep(20); ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif + } + + netdev_err(sq->netdev, +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index f2d56be..6738e06 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -82,8 +82,21 @@ + #include "en/ptp.h" + #include "compat.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#define NETMAP_MLX5_MAIN ++#define DEV_NETMAP ++#include "mlx5_netmap_linux.h" ++#endif ++ + bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) + { ++#ifdef DEV_NETMAP ++ return 0; ++#endif + bool striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && + MLX5_CAP_GEN(mdev, umr_ptr_rlky) && + MLX5_CAP_ETH(mdev, reg_umr_sq); +@@ -1038,6 +1051,12 @@ int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time) + { + unsigned long exp_time = jiffies + msecs_to_jiffies(wait_time); + ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->netdev); ++ if (nm_netmap_on(na) && na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_ON) ++ return 0; /* no need to wait when netmap has built wqes */ ++#endif ++ + u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5e_rqwq_get_size(rq)); + + do { +@@ -1102,6 +1121,10 @@ void mlx5e_free_rx_descs(struct mlx5e_rq *rq) + + while (!mlx5_wq_cyc_is_empty(wq)) { + wqe_ix = mlx5_wq_cyc_get_tail(wq); ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->netdev); ++ if (!nm_netmap_on(na) || na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + rq->dealloc_wqe(rq, wqe_ix); + mlx5_wq_cyc_pop(wq); + } +@@ -1222,6 +1245,10 @@ int mlx5e_open_rq(struct mlx5e_channel *c, struct mlx5e_params *params, + if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_SKB_XMIT_MORE)) + __set_bit(MLX5E_RQ_STATE_SKB_XMIT_MORE, &c->rq.state); + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_configure_rx_ring(rq, rq->ix); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_destroy_rq: +@@ -1234,6 +1261,9 @@ err_free_rq: + + void mlx5e_activate_rq(struct mlx5e_rq *rq) + { ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(rq->netdev)) || NA(rq->netdev)->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); + mlx5e_trigger_irq(rq->icosq); + } +@@ -1509,6 +1539,11 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, + INIT_WORK(&sq->dim_obj.dim.work, mlx5e_tx_dim_work); + sq->dim_obj.dim.mode = params->tx_cq_moderation.cq_period_mode; + ++#ifdef DEV_NETMAP ++ if (mlx5e_netmap_configure_tx_ring(c->priv, txq_ix)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_sq_wq_destroy: +@@ -1697,6 +1732,9 @@ void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) + mlx5e_tx_disable_queue(sq->txq); + + /* last doorbell out, godspeed .. */ ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(sq->txq->dev))) // TODO ++#endif + if (mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1)) { + u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + struct mlx5e_tx_wqe *nop; +@@ -1717,6 +1755,12 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) + + cancel_work_sync(&sq->dim_obj.dim.work); + cancel_work_sync(&sq->recover_work); ++ ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif ++ + mlx5e_destroy_sq(mdev, sq->sqn); + if (sq->rate_limit) { + rl.rate = sq->rate_limit; +@@ -3805,6 +3849,11 @@ int mlx5e_open_locked(struct net_device *netdev) + priv->profile->update_carrier(priv); + + mlx5e_queue_update_stats(priv); ++ ++#ifdef DEV_NETMAP ++ netmap_enable_all_rings(netdev); /* NOP if netmap not in use */ ++#endif ++ + return 0; + + err_clear_state_opened_flag: +@@ -3838,6 +3887,10 @@ int mlx5e_close_locked(struct net_device *netdev) + + clear_bit(MLX5E_STATE_OPENED, &priv->state); + ++#ifdef DEV_NETMAP ++ netmap_disable_all_rings(netdev); ++#endif ++ + netif_carrier_off(priv->netdev); + mlx5e_destroy_debugfs(priv); + #if defined(CONFIG_MLX5_EN_SPECIAL_SQ) && (defined(HAVE_NDO_SET_TX_MAXRATE) || defined(HAVE_NDO_SET_TX_MAXRATE_EXTENDED)) +@@ -7010,6 +7063,10 @@ void mlx5e_destroy_netdev(struct mlx5e_priv *priv) + { + struct net_device *netdev = priv->netdev; + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + mlx5e_netdev_cleanup(netdev, priv); + free_netdev(netdev); + } +@@ -7123,6 +7180,10 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) + + mlx5e_dcbnl_init_app(priv); + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_attach(priv); ++#endif /* DEV_NETMAP */ ++ + if (MLX5_ESWITCH_MANAGER(mdev)) + mlx5e_rep_register_vport_reps(mdev, priv); + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +index 7e88cbf..ad7257f 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +@@ -81,6 +81,14 @@ const struct mlx5e_rx_handlers mlx5e_rx_handlers_nic = { + .handle_rx_cqe_mpwqe = mlx5e_handle_rx_cqe_mpwrq, + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline bool mlx5e_rx_hw_stamp(struct hwtstamp_config *config) + { + return config->rx_filter == HWTSTAMP_FILTER_ALL; +@@ -209,7 +217,7 @@ static inline u32 mlx5e_decompress_cqes_cont(struct mlx5e_rq *rq, + return cqe_count; + } + +-static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, ++u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, + struct mlx5_cqwq *wq, + int budget_rem) + { +@@ -1911,6 +1919,13 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) + priv = netdev_priv(rq->netdev); + #endif + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ int dummy; ++ int nm_irq = netmap_rx_irq(rq->netdev, rq->ix, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif ++ + if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + return 0; + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +index 84a9d6f..e4ad5be 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +@@ -42,8 +42,16 @@ + #include "en_accel/en_accel.h" + #include "lib/clock.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline void mlx5e_read_cqe_slot(struct mlx5_cqwq *wq, +- u32 cqcc, void *data) ++ u32 cqcc, void *data) + { + u32 ci = mlx5_cqwq_ctr2ix(wq, cqcc); + +@@ -1026,6 +1034,11 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) + + sq = container_of(cq, struct mlx5e_txqsq, cq); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ if (netmap_tx_irq(sq->netdev, sq->ch_ix) != NM_IRQ_PASS) ++ return false; ++#endif ++ + if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + return false; + +@@ -1148,23 +1161,29 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) + + sqcc += wi->num_wqebbs; + +- if (likely(wi->skb)) { +- mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); +- dev_kfree_skb_any(wi->skb); ++ if (!nm_netmap_on(NA(sq->txq->dev))) { ++ /* do not free skbs in netmap mode */ ++ if (likely(wi->skb)) { ++ mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); ++ dev_kfree_skb_any(wi->skb); + +- npkts++; +- nbytes += wi->num_bytes; +- continue; +- } ++ npkts++; ++ nbytes += wi->num_bytes; ++ continue; ++ } + +- if (unlikely(mlx5e_ktls_tx_try_handle_resync_dump_comp(sq, wi, &dma_fifo_cc))) +- continue; ++ if (unlikely(mlx5e_ktls_tx_try_handle_resync_dump_comp(sq, wi, &dma_fifo_cc))) ++ continue; + +- if (wi->num_fifo_pkts) { +- mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); +- mlx5e_tx_wi_kfree_fifo_skbs(sq, wi); ++ if (wi->num_fifo_pkts) { ++ mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); ++ mlx5e_tx_wi_kfree_fifo_skbs(sq, wi); + +- npkts += wi->num_fifo_pkts; ++ npkts += wi->num_fifo_pkts; ++ nbytes += wi->num_bytes; ++ } ++ } else { ++ npkts++; + nbytes += wi->num_bytes; + } + } diff --git a/LINUX/final-patches/mellanox--mlx5--5.3 b/LINUX/final-patches/mellanox--mlx5--5.3 new file mode 100644 index 000000000..552bb82d0 --- /dev/null +++ b/LINUX/final-patches/mellanox--mlx5--5.3 @@ -0,0 +1,411 @@ +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +index 971de67..40a2aef 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +@@ -6,12 +6,12 @@ + + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_MLX5_CORE) += mlx5_core.o ++obj-$(CONFIG_MLX5_CORE) += mlx5_core$(NETMAP_DRIVER_SUFFIX).o + + # + # mlx5 core basic + # +-mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + health.o mcg.o cq.o alloc.o port.o mr.o pd.o \ + transobj.o vport.o sriov.o fs_cmd.o fs_core.o pci_irq.o \ + fs_counters.o rl.o lag.o dev.o events.o wq.o lib/gid.o \ +@@ -20,12 +20,12 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + mst_dump.o en_diag.o sriov_sysfs.o crdump.o diag/diag_cnt.o \ + eswitch_devlink_compat.o params.o fw_exp.o fw_reset.o + +-mlx5_core-y += compat.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += compat.o + + # + # Netdev basic + # +-mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + en_tx.o en_rx.o en_dim.o en_txrx.o en/xdp.o en_stats.o en_sysfs.o en_ecn.o \ + en_selftest.o en/port.o en/monitor_stats.o en/health.o \ + en/reporter_tx.o en/reporter_rx.o en/params.o en/xsk/umem.o \ +@@ -35,63 +35,63 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + # + # Netdev extra + # +-mlx5_core-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o +-mlx5_core-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o +-mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o +-mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += lag_mp.o lib/geneve.o lib/port_tun.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += lag_mp.o lib/geneve.o lib/port_tun.o \ + en_rep.o en/rep/bond.o en/mod_hdr.o \ + en/flow_meter_aso.o +-mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ + en/mapping.o lib/fs_chains.o en/tc_tun.o \ + en/tc_tun_vxlan.o en/tc_tun_gre.o en/tc_tun_geneve.o \ + en/tc_tun_mplsoudp.o diag/en_tc_tracepoint.o \ + en/tc_sample.o esw/indir_table.o en/tc_tun_common.o +-mlx5_core-$(CONFIG_MLX5_TC_CT) += en/tc_ct.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_TC_CT) += en/tc_ct.o + + # + # Core extra + # +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \ + ecpf.o rdma.o esw/vf_meter.o +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ + esw/acl/egress_lgcy.o esw/acl/egress_ofld.o \ + esw/acl/ingress_lgcy.o esw/acl/ingress_ofld.o \ + esw/vporttbl.o esw/pet_offloads.o \ + +-mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MPFS) += lib/mpfs.o + ifneq ($(CONFIG_VXLAN),) +- mlx5_core-y += lib/vxlan.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/vxlan.o + endif + ifneq ($(CONFIG_PTP_1588_CLOCK),) +- mlx5_core-y += lib/clock.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/clock.o + endif +-mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o + + # + # Ipoib netdev + # +-mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o + + # + # Accelerations & FPGA + # +-mlx5_core-$(CONFIG_MLX5_IPSEC) += accel/ipsec_offload.o +-mlx5_core-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o +-mlx5_core-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o +-mlx5_core-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_IPSEC) += accel/ipsec_offload.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o + +-mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o + +-mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ + en_accel/ipsec_stats.o en_accel/ipsec_fs.o esw/ipsec.o \ + en/ipsec_aso.o + +-mlx5_core-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ + en_accel/fs_tcp.o en_accel/ktls.o en_accel/ktls_txrx.o \ + en_accel/ktls_tx.o en_accel/ktls_rx.o + +-mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ + steering/dr_matcher.o steering/dr_rule.o \ + steering/dr_icm_pool.o \ + steering/dr_ste.o steering/dr_send.o \ +@@ -102,4 +102,4 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o + # + # Mdev basic + # +-mlx5_core-$(CONFIG_MLX5_MDEV) += meddev/sf.o meddev/mdev.o meddev/mdev_driver.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MDEV) += meddev/sf.o meddev/mdev.o meddev/mdev_driver.o +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +index a6dceb6..a3cb81d 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +@@ -14,6 +14,10 @@ static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq) + return 0; + + msleep(20); ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif + } + + netdev_err(sq->netdev, +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index 47260b9..25857eb 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -84,8 +84,21 @@ + #include "fpga/ipsec.h" + #include "compat.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#define NETMAP_MLX5_MAIN ++#define DEV_NETMAP ++#include "mlx5_netmap_linux.h" ++#endif ++ + bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) + { ++#ifdef DEV_NETMAP ++ return 0; ++#endif + bool striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && + MLX5_CAP_GEN(mdev, umr_ptr_rlky) && + MLX5_CAP_ETH(mdev, reg_umr_sq); +@@ -1051,6 +1064,12 @@ int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time) + { + unsigned long exp_time = jiffies + msecs_to_jiffies(wait_time); + ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->netdev); ++ if (nm_netmap_on(na) && na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_ON) ++ return 0; /* no need to wait when netmap has built wqes */ ++#endif ++ + u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5e_rqwq_get_size(rq)); + + do { +@@ -1115,6 +1134,10 @@ void mlx5e_free_rx_descs(struct mlx5e_rq *rq) + + while (!mlx5_wq_cyc_is_empty(wq)) { + wqe_ix = mlx5_wq_cyc_get_tail(wq); ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->netdev); ++ if (!nm_netmap_on(na) || na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + rq->dealloc_wqe(rq, wqe_ix); + mlx5_wq_cyc_pop(wq); + } +@@ -1248,6 +1271,10 @@ int mlx5e_open_rq(struct mlx5e_channel *c, struct mlx5e_params *params, + if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_SKB_XMIT_MORE)) + __set_bit(MLX5E_RQ_STATE_SKB_XMIT_MORE, &c->rq.state); + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_configure_rx_ring(rq, rq->ix); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_destroy_rq: +@@ -1261,6 +1288,9 @@ err_dealloc_rq: + + void mlx5e_activate_rq(struct mlx5e_rq *rq) + { ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(rq->netdev)) || NA(rq->netdev)->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); + mlx5e_trigger_irq(rq->icosq); + } +@@ -1544,6 +1574,11 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, + INIT_WORK(&sq->dim_obj.dim.work, mlx5e_tx_dim_work); + sq->dim_obj.dim.mode = params->tx_cq_moderation.cq_period_mode; + ++#ifdef DEV_NETMAP ++ if (mlx5e_netmap_configure_tx_ring(c->priv, txq_ix)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_sq_wq_destroy: +@@ -1738,6 +1773,9 @@ void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) + mlx5e_tx_disable_queue(sq->txq); + + /* last doorbell out, godspeed .. */ ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(sq->txq->dev))) // TODO ++#endif + if (mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1)) { + u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + struct mlx5e_tx_wqe *nop; +@@ -1758,6 +1796,12 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) + + cancel_work_sync(&sq->dim_obj.dim.work); + cancel_work_sync(&sq->recover_work); ++ ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif ++ + mlx5e_destroy_sq(mdev, sq->sqn); + if (sq->rate_limit) { + rl.rate = sq->rate_limit; +@@ -3883,6 +3927,11 @@ int mlx5e_open_locked(struct net_device *netdev) + priv->profile->update_carrier(priv); + + mlx5e_queue_update_stats(priv); ++ ++#ifdef DEV_NETMAP ++ netmap_enable_all_rings(netdev); /* NOP if netmap not in use */ ++#endif ++ + return 0; + + err_clear_state_opened_flag: +@@ -3916,6 +3965,10 @@ int mlx5e_close_locked(struct net_device *netdev) + + clear_bit(MLX5E_STATE_OPENED, &priv->state); + ++#ifdef DEV_NETMAP ++ netmap_disable_all_rings(netdev); ++#endif ++ + netif_carrier_off(priv->netdev); + mlx5e_destroy_debugfs(priv); + #if defined(CONFIG_MLX5_EN_SPECIAL_SQ) && (defined(HAVE_NDO_SET_TX_MAXRATE) || defined(HAVE_NDO_SET_TX_MAXRATE_EXTENDED)) +@@ -7129,6 +7182,10 @@ void mlx5e_destroy_netdev(struct mlx5e_priv *priv) + { + struct net_device *netdev = priv->netdev; + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + mlx5e_netdev_cleanup(netdev, priv); + free_netdev(netdev); + } +@@ -7242,6 +7299,10 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) + + mlx5e_dcbnl_init_app(priv); + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_attach(priv); ++#endif /* DEV_NETMAP */ ++ + if (MLX5_ESWITCH_MANAGER(mdev)) + mlx5e_rep_register_vport_reps(mdev, priv); + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +index 78cc065..bf0f358 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +@@ -79,6 +79,14 @@ const struct mlx5e_rx_handlers mlx5e_rx_handlers_nic = { + .handle_rx_cqe_mpwqe = mlx5e_handle_rx_cqe_mpwrq, + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline bool mlx5e_rx_hw_stamp(struct hwtstamp_config *config) + { + return config->rx_filter == HWTSTAMP_FILTER_ALL; +@@ -207,7 +215,7 @@ static inline u32 mlx5e_decompress_cqes_cont(struct mlx5e_rq *rq, + return cqe_count; + } + +-static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, ++u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, + struct mlx5_cqwq *wq, + int budget_rem) + { +@@ -1992,6 +2000,13 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) + priv = netdev_priv(rq->netdev); + #endif + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ int dummy; ++ int nm_irq = netmap_rx_irq(rq->netdev, rq->ix, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif ++ + if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + return 0; + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +index 6785213..c521cc8 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +@@ -41,8 +41,16 @@ + #include "ipoib/ipoib.h" + #include "en_accel/en_accel.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline void mlx5e_read_cqe_slot(struct mlx5_cqwq *wq, +- u32 cqcc, void *data) ++ u32 cqcc, void *data) + { + u32 ci = mlx5_cqwq_ctr2ix(wq, cqcc); + +@@ -1024,6 +1032,11 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) + + sq = container_of(cq, struct mlx5e_txqsq, cq); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ if (netmap_tx_irq(sq->netdev, sq->ch_ix) != NM_IRQ_PASS) ++ return false; ++#endif ++ + if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + return false; + +@@ -1146,23 +1159,29 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) + + sqcc += wi->num_wqebbs; + +- if (likely(wi->skb)) { +- mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); +- dev_kfree_skb_any(wi->skb); ++ if (!nm_netmap_on(NA(sq->txq->dev))) { ++ /* do not free skbs in netmap mode */ ++ if (likely(wi->skb)) { ++ mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); ++ dev_kfree_skb_any(wi->skb); + +- npkts++; +- nbytes += wi->num_bytes; +- continue; +- } ++ npkts++; ++ nbytes += wi->num_bytes; ++ continue; ++ } + +- if (unlikely(mlx5e_ktls_tx_try_handle_resync_dump_comp(sq, wi, &dma_fifo_cc))) +- continue; ++ if (unlikely(mlx5e_ktls_tx_try_handle_resync_dump_comp(sq, wi, &dma_fifo_cc))) ++ continue; + +- if (wi->num_fifo_pkts) { +- mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); +- mlx5e_tx_wi_kfree_fifo_skbs(sq, wi); ++ if (wi->num_fifo_pkts) { ++ mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); ++ mlx5e_tx_wi_kfree_fifo_skbs(sq, wi); + +- npkts += wi->num_fifo_pkts; ++ npkts += wi->num_fifo_pkts; ++ nbytes += wi->num_bytes; ++ } ++ } else { ++ npkts++; + nbytes += wi->num_bytes; + } + } diff --git a/LINUX/final-patches/mellanox--mlx5--5.4 b/LINUX/final-patches/mellanox--mlx5--5.4 new file mode 100644 index 000000000..0e0304698 --- /dev/null +++ b/LINUX/final-patches/mellanox--mlx5--5.4 @@ -0,0 +1,426 @@ +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +index 544058a..6f34c0d 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +@@ -7,12 +7,12 @@ + + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_MLX5_CORE) += mlx5_core.o ++obj-$(CONFIG_MLX5_CORE) += mlx5_core$(NETMAP_DRIVER_SUFFIX).o + + # + # mlx5 core basic + # +-mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + health.o mcg.o cq.o alloc.o port.o mr.o pd.o \ + transobj.o vport.o sriov.o fs_cmd.o fs_core.o pci_irq.o \ + fs_counters.o fs_ft_pool.o rl.o lag.o dev.o events.o wq.o lib/gid.o \ +@@ -21,12 +21,12 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + mst_dump.o en_diag.o sriov_sysfs.o crdump.o diag/diag_cnt.o \ + eswitch_devlink_compat.o params.o fw_exp.o fw_reset.o + +-mlx5_core-y += compat.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += compat.o + + # + # Netdev basic + # +-mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + en_tx.o en_rx.o en_dim.o en_txrx.o en/xdp.o en_stats.o en_sysfs.o en_ecn.o \ + en_selftest.o en/port.o en/monitor_stats.o en/health.o \ + en/reporter_tx.o en/reporter_rx.o en/params.o en/xsk/umem.o \ +@@ -36,65 +36,65 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + # + # Netdev extra + # +-mlx5_core-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o +-mlx5_core-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o +-mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o +-mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += lag_mp.o lib/geneve.o lib/port_tun.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += lag_mp.o lib/geneve.o lib/port_tun.o \ + en_rep.o en/rep/bond.o en/mod_hdr.o \ + en/flow_meter_aso.o +-mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ + en/mapping.o lib/fs_chains.o en/tc_tun.o \ + en/tc_tun_vxlan.o en/tc_tun_gre.o en/tc_tun_geneve.o \ + en/tc_tun_mplsoudp.o diag/en_tc_tracepoint.o \ + en/tc_sample.o esw/indir_table.o en/tc_tun_common.o +-mlx5_core-$(CONFIG_MLX5_TC_CT) += en/tc_ct.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_TC_CT) += en/tc_ct.o + + # + # Core extra + # +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \ + ecpf.o rdma.o esw/vf_meter.o esw/legacy.o +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ + esw/acl/egress_lgcy.o esw/acl/egress_ofld.o \ + esw/acl/ingress_lgcy.o esw/acl/ingress_ofld.o \ + esw/vporttbl.o esw/devlink_port.o esw/pet_offloads.o \ + esw/qos.o + +-mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MPFS) += lib/mpfs.o + ifneq ($(CONFIG_VXLAN),) +- mlx5_core-y += lib/vxlan.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/vxlan.o + endif + ifneq ($(CONFIG_PTP_1588_CLOCK),) +- mlx5_core-y += lib/clock.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/clock.o + endif +-mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o +-mlx5_core-$(CONFIG_MLXDEVM) += mlx5_devm.o esw/devm_port.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLXDEVM) += mlx5_devm.o esw/devm_port.o + + # + # Ipoib netdev + # +-mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o + + # + # Accelerations & FPGA + # +-mlx5_core-$(CONFIG_MLX5_IPSEC) += accel/ipsec_offload.o +-mlx5_core-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o +-mlx5_core-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o +-mlx5_core-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_IPSEC) += accel/ipsec_offload.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o + +-mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o + +-mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ + en_accel/ipsec_stats.o en_accel/ipsec_fs.o esw/ipsec.o \ + en/ipsec_aso.o + +-mlx5_core-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ + en_accel/fs_tcp.o en_accel/ktls.o en_accel/ktls_txrx.o \ + en_accel/ktls_tx.o en_accel/ktls_rx.o + +-mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ + steering/dr_matcher.o steering/dr_rule.o \ + steering/dr_icm_pool.o \ + steering/dr_ste.o steering/dr_send.o \ +@@ -104,14 +104,14 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o + # + # SF device + # +-mlx5_core-$(CONFIG_MLX5_SF) += sf/vhca_event.o sf/dev/dev.o sf/dev/driver.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SF) += sf/vhca_event.o sf/dev/dev.o sf/dev/driver.o + + # + # SF manager + # +-mlx5_core-$(CONFIG_MLX5_SF_MANAGER) += sf/cmd.o sf/hw_table.o sf/devlink.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SF_MANAGER) += sf/cmd.o sf/hw_table.o sf/devlink.o + + # + # SF cfg driver basic + # +-mlx5_core-$(CONFIG_MLX5_SF_CFG) += sf/dev/cfg_driver.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SF_CFG) += sf/dev/cfg_driver.o +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +index 6491456..d8e07e1 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +@@ -15,6 +15,10 @@ static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq) + return 0; + + msleep(20); ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif + } + + netdev_err(sq->netdev, +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index a4667bb..709804d 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -85,8 +85,21 @@ + #include "fpga/ipsec.h" + #include "compat.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#define NETMAP_MLX5_MAIN ++#define DEV_NETMAP ++#include "mlx5_netmap_linux.h" ++#endif ++ + bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) + { ++#ifdef DEV_NETMAP ++ return 0; ++#endif + bool striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && + MLX5_CAP_GEN(mdev, umr_ptr_rlky) && + MLX5_CAP_ETH(mdev, reg_umr_sq); +@@ -1075,6 +1088,12 @@ int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time) + { + unsigned long exp_time = jiffies + msecs_to_jiffies(wait_time); + ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->netdev); ++ if (nm_netmap_on(na) && na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_ON) ++ return 0; /* no need to wait when netmap has built wqes */ ++#endif ++ + u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5e_rqwq_get_size(rq)); + + do { +@@ -1140,6 +1159,10 @@ void mlx5e_free_rx_descs(struct mlx5e_rq *rq) + + while (!mlx5_wq_cyc_is_empty(wq)) { + wqe_ix = mlx5_wq_cyc_get_tail(wq); ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->netdev); ++ if (!nm_netmap_on(na) || na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + rq->dealloc_wqe(rq, wqe_ix); + mlx5_wq_cyc_pop(wq); + } +@@ -1272,6 +1295,10 @@ int mlx5e_open_rq(struct mlx5e_channel *c, struct mlx5e_params *params, + if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_SKB_XMIT_MORE)) + __set_bit(MLX5E_RQ_STATE_SKB_XMIT_MORE, &c->rq.state); + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_configure_rx_ring(rq, rq->ix); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_destroy_rq: +@@ -1285,6 +1312,9 @@ err_dealloc_rq: + + void mlx5e_activate_rq(struct mlx5e_rq *rq) + { ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(rq->netdev)) || NA(rq->netdev)->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); + mlx5e_trigger_irq(rq->icosq); + } +@@ -1569,6 +1599,11 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, + INIT_WORK(&sq->dim_obj.dim.work, mlx5e_tx_dim_work); + sq->dim_obj.dim.mode = params->tx_cq_moderation.cq_period_mode; + ++#ifdef DEV_NETMAP ++ if (mlx5e_netmap_configure_tx_ring(c->priv, txq_ix)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_sq_wq_destroy: +@@ -1763,6 +1798,9 @@ void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) + mlx5e_tx_disable_queue(sq->txq); + + /* last doorbell out, godspeed .. */ ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(sq->txq->dev))) // TODO ++#endif + if (mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1)) { + u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + struct mlx5e_tx_wqe *nop; +@@ -1783,6 +1821,12 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) + + cancel_work_sync(&sq->dim_obj.dim.work); + cancel_work_sync(&sq->recover_work); ++ ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif ++ + mlx5e_destroy_sq(mdev, sq->sqn); + if (sq->rate_limit) { + rl.rate = sq->rate_limit; +@@ -3984,6 +4028,11 @@ int mlx5e_open_locked(struct net_device *netdev) + priv->profile->update_carrier(priv); + + mlx5e_queue_update_stats(priv); ++ ++#ifdef DEV_NETMAP ++ netmap_enable_all_rings(netdev); /* NOP if netmap not in use */ ++#endif ++ + return 0; + + err_clear_state_opened_flag: +@@ -4019,6 +4068,10 @@ int mlx5e_close_locked(struct net_device *netdev) + mlx5e_apply_traps(priv, false); + clear_bit(MLX5E_STATE_OPENED, &priv->state); + ++#ifdef DEV_NETMAP ++ netmap_disable_all_rings(netdev); ++#endif ++ + netif_carrier_off(priv->netdev); + mlx5e_destroy_debugfs(priv); + #if defined(CONFIG_MLX5_EN_SPECIAL_SQ) && (defined(HAVE_NDO_SET_TX_MAXRATE) || defined(HAVE_NDO_SET_TX_MAXRATE_EXTENDED)) +@@ -7339,6 +7392,10 @@ void mlx5e_destroy_netdev(struct mlx5e_priv *priv) + { + struct net_device *netdev = priv->netdev; + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + mlx5e_netdev_cleanup(netdev, priv); + free_netdev(netdev); + } +@@ -7450,6 +7507,10 @@ static int mlx5e_probe(struct auxiliary_device *adev, + mlx5e_dcbnl_init_app(priv); + mlx5_uplink_netdev_set(mdev, netdev); + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_attach(priv); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_unregister_netdev: +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +index 8237d9a..194370c 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +@@ -81,6 +81,14 @@ const struct mlx5e_rx_handlers mlx5e_rx_handlers_nic = { + .handle_rx_cqe_mpwqe = mlx5e_handle_rx_cqe_mpwrq, + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline bool mlx5e_rx_hw_stamp(struct hwtstamp_config *config) + { + return config->rx_filter == HWTSTAMP_FILTER_ALL; +@@ -209,7 +217,7 @@ static inline u32 mlx5e_decompress_cqes_cont(struct mlx5e_rq *rq, + return cqe_count; + } + +-static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, ++u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, + struct mlx5_cqwq *wq, + int budget_rem) + { +@@ -2000,6 +2008,13 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) + priv = netdev_priv(rq->netdev); + #endif + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ int dummy; ++ int nm_irq = netmap_rx_irq(rq->netdev, rq->ix, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif ++ + if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + return 0; + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +index 2cf0c41..36a8835 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +@@ -41,8 +41,16 @@ + #include "ipoib/ipoib.h" + #include "en_accel/en_accel.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline void mlx5e_read_cqe_slot(struct mlx5_cqwq *wq, +- u32 cqcc, void *data) ++ u32 cqcc, void *data) + { + u32 ci = mlx5_cqwq_ctr2ix(wq, cqcc); + +@@ -1023,6 +1031,11 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) + + sq = container_of(cq, struct mlx5e_txqsq, cq); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ if (netmap_tx_irq(sq->netdev, sq->ch_ix) != NM_IRQ_PASS) ++ return false; ++#endif ++ + if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + return false; + +@@ -1145,23 +1158,29 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) + + sqcc += wi->num_wqebbs; + +- if (likely(wi->skb)) { +- mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); +- dev_kfree_skb_any(wi->skb); ++ if (!nm_netmap_on(NA(sq->txq->dev))) { ++ /* do not free skbs in netmap mode */ ++ if (likely(wi->skb)) { ++ mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); ++ dev_kfree_skb_any(wi->skb); + +- npkts++; +- nbytes += wi->num_bytes; +- continue; +- } ++ npkts++; ++ nbytes += wi->num_bytes; ++ continue; ++ } + +- if (unlikely(mlx5e_ktls_tx_try_handle_resync_dump_comp(sq, wi, &dma_fifo_cc))) +- continue; ++ if (unlikely(mlx5e_ktls_tx_try_handle_resync_dump_comp(sq, wi, &dma_fifo_cc))) ++ continue; + +- if (wi->num_fifo_pkts) { +- mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); +- mlx5e_tx_wi_kfree_fifo_skbs(sq, wi); ++ if (wi->num_fifo_pkts) { ++ mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); ++ mlx5e_tx_wi_kfree_fifo_skbs(sq, wi); + +- npkts += wi->num_fifo_pkts; ++ npkts += wi->num_fifo_pkts; ++ nbytes += wi->num_bytes; ++ } ++ } else { ++ npkts++; + nbytes += wi->num_bytes; + } + } diff --git a/LINUX/final-patches/mellanox--mlx5--5.8 b/LINUX/final-patches/mellanox--mlx5--5.8 new file mode 100644 index 000000000..ba8eb6cc8 --- /dev/null +++ b/LINUX/final-patches/mellanox--mlx5--5.8 @@ -0,0 +1,446 @@ +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +index 2a88532..37d3415 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/Makefile +@@ -6,12 +6,12 @@ + + subdir-ccflags-y += -I$(src) + +-obj-$(CONFIG_MLX5_CORE) += mlx5_core.o ++obj-$(CONFIG_MLX5_CORE) += mlx5_core$(NETMAP_DRIVER_SUFFIX).o + + # + # mlx5 core basic + # +-mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + health.o mcg.o cq.o alloc.o port.o mr.o pd.o \ + transobj.o vport.o sriov.o fs_cmd.o fs_core.o pci_irq.o \ + fs_counters.o fs_ft_pool.o rl.o lag/lag.o lag/debugfs.o dev.o events.o wq.o lib/gid.o \ +@@ -21,11 +21,11 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ + diag/diag_cnt.o params.o fw_exp.o lib/tout.o eswitch_devlink_compat.o \ + ecpf.o lib/aso.o + +-mlx5_core-y += compat.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += compat.o + # + # Netdev basic + # +-mlx5_core-$(CONFIG_MLX5_CORE_EN) += en/rqt.o en/tir.o en/rss.o en/rx_res.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN) += en/rqt.o en/tir.o en/rss.o en/rx_res.o \ + en/channels.o en_main.o en_common.o en_fs.o en_ethtool.o \ + en_tx.o en_rx.o en_dim.o en_txrx.o en/xdp.o en_stats.o en_sysfs.o en_ecn.o\ + en_selftest.o en/port.o en/monitor_stats.o en/health.o \ +@@ -37,14 +37,14 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en/rqt.o en/tir.o en/rss.o en/rx_res.o \ + # + # Netdev extra + # +-mlx5_core-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o +-mlx5_core-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o +-mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o +-mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += lag/mp.o lag/port_sel.o lib/geneve.o lib/port_tun.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += lag/mp.o lag/port_sel.o lib/geneve.o lib/port_tun.o \ + en_rep.o en/rep/bond.o en/mod_hdr.o \ + en/mapping.o en/rep/meter.o en/rep/sysfs.o +-mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ + lib/fs_chains.o en/tc_tun.o \ + esw/indir_table.o en/tc_tun_encap.o \ + en/tc_tun_vxlan.o en/tc_tun_gre.o en/tc_tun_geneve.o \ +@@ -52,7 +52,7 @@ mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ + en/tc/post_act.o en/tc/int_port.o \ + en/tc/post_meter.o + +-mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en/tc/act/act.o en/tc/act/drop.o en/tc/act/trap.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CLS_ACT) += en/tc/act/act.o en/tc/act/drop.o en/tc/act/trap.o \ + en/tc/act/accept.o en/tc/act/mark.o en/tc/act/goto.o \ + en/tc/act/tun.o en/tc/act/csum.o en/tc/act/pedit.o \ + en/tc/act/vlan.o en/tc/act/vlan_mangle.o en/tc/act/mpls.o \ +@@ -60,60 +60,60 @@ mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en/tc/act/act.o en/tc/act/drop.o en/tc/a + en/tc/act/ct.o en/tc/act/sample.o en/tc/act/ptype.o \ + en/tc/act/redirect_ingress.o en/tc/act/prio.o en/tc/act/police.o + +-mlx5_core-$(CONFIG_MLX5_TC_CT) += en/tc_ct.o en/tc/ct_fs_dmfs.o en/tc/ct_fs_smfs.o +-mlx5_core-$(CONFIG_MLX5_TC_SAMPLE) += en/tc/sample.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_TC_CT) += en/tc_ct.o en/tc/ct_fs_dmfs.o en/tc/ct_fs_smfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_TC_SAMPLE) += en/tc/sample.o + + # + # Core extra + # +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \ + ecpf.o rdma.o esw/legacy.o esw/vf_meter.o \ + esw/debugfs.o esw/devlink_port.o esw/vporttbl.o esw/qos.o \ + esw/pet_offloads.o + +-mlx5_core-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ + esw/acl/egress_lgcy.o esw/acl/egress_ofld.o \ + esw/acl/ingress_lgcy.o esw/acl/ingress_ofld.o + +-mlx5_core-$(CONFIG_MLX5_BRIDGE) += esw/bridge.o en/rep/bridge.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_BRIDGE) += esw/bridge.o en/rep/bridge.o + +-mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_MPFS) += lib/mpfs.o + ifneq ($(CONFIG_VXLAN),) +- mlx5_core-y += lib/vxlan.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/vxlan.o + endif + ifneq ($(CONFIG_PTP_1588_CLOCK),) +- mlx5_core-y += lib/clock.o ++ mlx5_core$(NETMAP_DRIVER_SUFFIX)-y += lib/clock.o + endif +-mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o +-mlx5_core-$(CONFIG_MLXDEVM) += mlx5_devm.o esw/devm_port.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLXDEVM) += mlx5_devm.o esw/devm_port.o + + # + # Ipoib netdev + # +-mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o + + # + # Accelerations & FPGA + # +-mlx5_core-$(CONFIG_MLX5_IPSEC) += accel/ipsec_offload.o +-mlx5_core-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o +-mlx5_core-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o +-mlx5_core-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_IPSEC) += accel/ipsec_offload.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o + +-mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o + +-mlx5_core-$(CONFIG_MLX5_EN_MACSEC) += en_accel/macsec.o en_accel/macsec_fs.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_MACSEC) += en_accel/macsec.o en_accel/macsec_fs.o \ + en_accel/macsec_stats.o + +-mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ + en_accel/ipsec_stats.o en_accel/ipsec_fs.o esw/ipsec.o \ + en/ipsec_aso.o + +-mlx5_core-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ + en_accel/fs_tcp.o en_accel/ktls.o en_accel/ktls_txrx.o \ + en_accel/ktls_tx.o en_accel/ktls_rx.o + +-mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o \ + steering/dr_matcher.o steering/dr_rule.o \ + steering/dr_icm_pool.o steering/dr_buddy.o \ + steering/dr_ste.o steering/dr_send.o \ +@@ -125,14 +125,14 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o + # + # SF device + # +-mlx5_core-$(CONFIG_MLX5_SF) += sf/vhca_event.o sf/dev/dev.o sf/dev/driver.o irq_affinity.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SF) += sf/vhca_event.o sf/dev/dev.o sf/dev/driver.o irq_affinity.o + + # + # SF manager + # +-mlx5_core-$(CONFIG_MLX5_SF_MANAGER) += sf/cmd.o sf/hw_table.o sf/devlink.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SF_MANAGER) += sf/cmd.o sf/hw_table.o sf/devlink.o + + # + ## SF cfg driver basic + # +-mlx5_core-$(CONFIG_MLX5_SF_CFG) += sf/dev/cfg_driver.o ++mlx5_core$(NETMAP_DRIVER_SUFFIX)-$(CONFIG_MLX5_SF_CFG) += sf/dev/cfg_driver.o +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +index e1cd67b..d9f90b6 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +@@ -18,6 +18,10 @@ static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq) + return 0; + + msleep(20); ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif + } + + netdev_err(sq->netdev, +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index 03f309e..37a8368 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -88,8 +88,22 @@ + #include "fpga/ipsec.h" + #include "compat.h" + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#define NETMAP_MLX5_MAIN ++#define DEV_NETMAP ++#include "mlx5_netmap_linux.h" ++#endif ++ + bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) + { ++#ifdef DEV_NETMAP ++ return 0; ++#endif ++ + bool striding_rq_umr, inline_umr; + u16 max_wqe_sz_cap; + +@@ -1239,6 +1253,12 @@ int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time) + { + unsigned long exp_time = jiffies + msecs_to_jiffies(wait_time); + ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->netdev); ++ if (nm_netmap_on(na) && na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_ON) ++ return 0; /* no need to wait when netmap has built wqes */ ++#endif ++ + u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5e_rqwq_get_size(rq)); + + do { +@@ -1321,6 +1341,10 @@ void mlx5e_free_rx_descs(struct mlx5e_rq *rq) + + while (!mlx5_wq_cyc_is_empty(wq)) { + wqe_ix = mlx5_wq_cyc_get_tail(wq); ++#ifdef DEV_NETMAP ++ struct netmap_adapter *na = NA(rq->netdev); ++ if (!nm_netmap_on(na) || na->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + rq->dealloc_wqe(rq, wqe_ix); + mlx5_wq_cyc_pop(wq); + } +@@ -1458,6 +1482,10 @@ int mlx5e_open_rq(struct mlx5e_priv *priv, struct mlx5e_params *params, + if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_SKB_XMIT_MORE)) + __set_bit(MLX5E_RQ_STATE_SKB_XMIT_MORE, &rq->state); + ++#ifdef DEV_NETMAP ++ mlx5e_netmap_configure_rx_ring(rq, rq->ix); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_destroy_rq: +@@ -1471,6 +1499,9 @@ err_dealloc_rq: + + void mlx5e_activate_rq(struct mlx5e_rq *rq) + { ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(rq->netdev)) || NA(rq->netdev)->rx_rings[rq->ix]->nr_mode == NKR_NETMAP_OFF) ++#endif + set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); + } + +@@ -1765,6 +1796,11 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, + INIT_WORK(&sq->dim_obj.dim.work, mlx5e_tx_dim_work); + sq->dim_obj.dim.mode = params->tx_cq_moderation.cq_period_mode; + ++#ifdef DEV_NETMAP ++ if (mlx5e_netmap_configure_tx_ring(c->priv, txq_ix)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_sq_wq_destroy: +@@ -1978,6 +2014,9 @@ void mlx5e_stop_txqsq(struct mlx5e_txqsq *sq) + mlx5e_tx_disable_queue(sq->txq); + + /* last doorbell out, godspeed .. */ ++#ifdef DEV_NETMAP ++ if (!nm_netmap_on(NA(sq->txq->dev))) // TODO ++#endif + if (mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1)) { + u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + struct mlx5e_tx_wqe *nop; +@@ -1998,6 +2037,12 @@ void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) + + cancel_work_sync(&sq->dim_obj.dim.work); + cancel_work_sync(&sq->recover_work); ++ ++#ifdef DEV_NETMAP ++ if (nm_netmap_on(NA(sq->txq->dev))) // TODO ++ mlx5e_netmap_tx_flush(sq); /* handle any CQEs */ ++#endif ++ + mlx5e_destroy_sq(mdev, sq->sqn); + if (sq->rate_limit) { + rl.rate = sq->rate_limit; +@@ -3569,6 +3614,11 @@ int mlx5e_open_locked(struct net_device *netdev) + priv->profile->update_carrier(priv); + + mlx5e_queue_update_stats(priv); ++ ++#ifdef DEV_NETMAP ++ netmap_enable_all_rings(netdev); /* NOP if netmap not in use */ ++#endif ++ + return 0; + + err_clear_state_opened_flag: +@@ -3604,6 +3654,10 @@ int mlx5e_close_locked(struct net_device *netdev) + mlx5e_apply_traps(priv, false); + clear_bit(MLX5E_STATE_OPENED, &priv->state); + ++#ifdef DEV_NETMAP ++ netmap_disable_all_rings(netdev); ++#endif ++ + netif_carrier_off(priv->netdev); + if (!mlx5e_is_uplink_rep(priv) && !mlx5e_is_vport_rep(priv)) + mlx5e_destroy_debugfs(priv); +@@ -7279,6 +7333,10 @@ void mlx5e_destroy_netdev(struct mlx5e_priv *priv) + { + struct net_device *netdev = priv->netdev; + ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + mlx5e_priv_cleanup(priv); + free_netdev(netdev); + } +@@ -7391,6 +7449,11 @@ static int mlx5e_probe(struct auxiliary_device *adev, + + mlx5e_dcbnl_init_app(priv); + mlx5_uplink_netdev_set(mdev, netdev); ++ ++#ifdef DEV_NETMAP ++ mlx5e_netmap_attach(priv); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_unregister_netdev: +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +index 7ac3a9d..c077d87 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +@@ -94,6 +94,14 @@ const struct mlx5e_rx_handlers mlx5e_rx_handlers_nic = { + #endif + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline bool mlx5e_rx_hw_stamp(struct hwtstamp_config *config) + { + return config->rx_filter == HWTSTAMP_FILTER_ALL; +@@ -228,7 +236,7 @@ static inline u32 mlx5e_decompress_cqes_cont(struct mlx5e_rq *rq, + return cqe_count; + } + +-static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, ++u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, + struct mlx5_cqwq *wq, + int budget_rem) + { +@@ -2598,6 +2606,13 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) + priv = netdev_priv(rq->netdev); + #endif + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ int dummy; ++ int nm_irq = netmap_rx_irq(rq->netdev, rq->ix, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif ++ + if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + return 0; + +diff --git a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +index 20a529b..2c532db 100644 +--- a/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c ++++ b/mlx5/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +@@ -43,6 +43,14 @@ + #include "en/ptp.h" + #include + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * mlx5_netmap_linux.h contains functions for netmap support ++ * that extend the standard driver. ++ */ ++#include "mlx5_netmap_linux.h" ++#endif ++ + static inline void mlx5e_read_cqe_slot(struct mlx5_cqwq *wq, + u32 cqcc, void *data) + { +@@ -996,6 +1004,11 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) + + sq = container_of(cq, struct mlx5e_txqsq, cq); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++ if (netmap_tx_irq(sq->netdev, sq->ch_ix) != NM_IRQ_PASS) ++ return false; ++#endif ++ + if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + return false; + +@@ -1118,23 +1131,29 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) + + sqcc += wi->num_wqebbs; + +- if (likely(wi->skb)) { +- mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); +- dev_kfree_skb_any(wi->skb); ++ if (!nm_netmap_on(NA(sq->txq->dev))) { ++ /* do not free skbs in netmap mode */ ++ if (likely(wi->skb)) { ++ mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); ++ dev_kfree_skb_any(wi->skb); + +- npkts++; +- nbytes += wi->num_bytes; +- continue; +- } ++ npkts++; ++ nbytes += wi->num_bytes; ++ continue; ++ } + +- if (unlikely(mlx5e_ktls_tx_try_handle_resync_dump_comp(sq, wi, &dma_fifo_cc))) +- continue; ++ if (unlikely(mlx5e_ktls_tx_try_handle_resync_dump_comp(sq, wi, &dma_fifo_cc))) ++ continue; + +- if (wi->num_fifo_pkts) { +- mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); +- mlx5e_tx_wi_kfree_fifo_skbs(sq, wi); ++ if (wi->num_fifo_pkts) { ++ mlx5e_tx_wi_dma_unmap(sq, wi, &dma_fifo_cc); ++ mlx5e_tx_wi_kfree_fifo_skbs(sq, wi); + +- npkts += wi->num_fifo_pkts; ++ npkts += wi->num_fifo_pkts; ++ nbytes += wi->num_bytes; ++ } ++ } else { ++ npkts++; + nbytes += wi->num_bytes; + } + } diff --git a/LINUX/final-patches/vanilla--e1000--20620--31200 b/LINUX/final-patches/vanilla--e1000--20620--31200 deleted file mode 100644 index 0d8bb0577..000000000 --- a/LINUX/final-patches/vanilla--e1000--20620--31200 +++ /dev/null @@ -1,75 +0,0 @@ -diff --git a/e1000/e1000_main.c b/e1000/e1000_main.c -index bcd192c..013f528 100644 ---- a/e1000/e1000_main.c -+++ b/e1000/e1000_main.c -@@ -190,6 +190,10 @@ static struct pci_error_handlers e1000_err_handler = { - .resume = e1000_io_resume, - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - static struct pci_driver e1000_driver = { - .name = e1000_driver_name, - .id_table = e1000_pci_tbl, -@@ -375,6 +379,10 @@ static void e1000_configure(struct e1000_adapter *adapter) - e1000_configure_tx(adapter); - e1000_setup_rctl(adapter); - e1000_configure_rx(adapter); -+#ifdef DEV_NETMAP -+ if (e1000_netmap_init_buffers(adapter)) -+ return; -+#endif /* DEV_NETMAP */ - /* call E1000_DESC_UNUSED which always leaves - * at least 1 descriptor unused to make sure - * next_to_use != next_to_clean */ -@@ -1035,6 +1043,10 @@ static int __devinit e1000_probe(struct pci_dev *pdev, - adapter->wol = adapter->eeprom_wol; - device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); - -+#ifdef DEV_NETMAP -+ e1000_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ -+ - /* print bus type/speed/width info */ - DPRINTK(PROBE, INFO, "(PCI%s:%s:%s) ", - ((hw->bus_type == e1000_bus_type_pcix) ? "-X" : ""), -@@ -1113,6 +1125,10 @@ static void __devexit e1000_remove(struct pci_dev *pdev) - - kfree(adapter->tx_ring); - kfree(adapter->rx_ring); -+ -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ - - iounmap(hw->hw_addr); - if (hw->flash_address) -@@ -3429,6 +3445,10 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, - unsigned int count = 0; - unsigned int total_tx_bytes=0, total_tx_packets=0; - -+#ifdef DEV_NETMAP -+ if (netmap_tx_irq(netdev, 0) != NM_IRQ_PASS) -+ return 1; /* cleaned ok */ -+#endif /* DEV_NETMAP */ - i = tx_ring->next_to_clean; - eop = tx_ring->buffer_info[i].next_to_watch; - eop_desc = E1000_TX_DESC(*tx_ring, eop); -@@ -3795,6 +3815,15 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, - bool cleaned = false; - unsigned int total_rx_bytes=0, total_rx_packets=0; - -+#ifdef DEV_NETMAP -+ int nm_irq = netmap_rx_irq(netdev, 0, work_done); -+ if (nm_irq != NM_IRQ_PASS) { -+ if (nm_irq == NM_IRQ_RESCHED) { -+ *work_done = work_to_do; -+ } -+ return 1; -+ } -+#endif /* DEV_NETMAP */ - i = rx_ring->next_to_clean; - rx_desc = E1000_RX_DESC(*rx_ring, i); - buffer_info = &rx_ring->buffer_info[i]; diff --git a/LINUX/final-patches/vanilla--e1000--31200--99999 b/LINUX/final-patches/vanilla--e1000--40000--99999 similarity index 79% rename from LINUX/final-patches/vanilla--e1000--31200--99999 rename to LINUX/final-patches/vanilla--e1000--40000--99999 index eeca93fd1..c2c2c3b26 100644 --- a/LINUX/final-patches/vanilla--e1000--31200--99999 +++ b/LINUX/final-patches/vanilla--e1000--40000--99999 @@ -1,5 +1,5 @@ diff --git a/e1000/e1000_main.c b/e1000/e1000_main.c -index 24f3986..c28425f 100644 +index 7f997d36948f..816877487878 100644 --- a/e1000/e1000_main.c +++ b/e1000/e1000_main.c @@ -200,6 +200,10 @@ static const struct pci_error_handlers e1000_err_handler = { @@ -57,7 +57,23 @@ index 24f3986..c28425f 100644 i = tx_ring->next_to_clean; eop = tx_ring->buffer_info[i].next_to_watch; eop_desc = E1000_TX_DESC(*tx_ring, eop); -@@ -4355,6 +4375,15 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, +@@ -4135,6 +4155,15 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter, + bool cleaned = false; + unsigned int total_rx_bytes=0, total_rx_packets=0; + ++#ifdef DEV_NETMAP ++ int nm_irq = netmap_rx_irq(netdev, 0, work_done); ++ if (nm_irq != NM_IRQ_PASS) { ++ if (nm_irq == NM_IRQ_RESCHED) { ++ *work_done = work_to_do; ++ } ++ return 1; ++ } ++#endif /* DEV_NETMAP */ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC(*rx_ring, i); + buffer_info = &rx_ring->buffer_info[i]; +@@ -4355,6 +4384,15 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, bool cleaned = false; unsigned int total_rx_bytes=0, total_rx_packets=0; diff --git a/LINUX/final-patches/vanilla--e1000e--20620--30100 b/LINUX/final-patches/vanilla--e1000e--20620--30100 deleted file mode 100644 index 85fb9c8e4..000000000 --- a/LINUX/final-patches/vanilla--e1000e--20620--30100 +++ /dev/null @@ -1,69 +0,0 @@ -diff --git a/e1000e/netdev.c b/e1000e/netdev.c -index fad8f9e..cd4abfd 100644 ---- a/e1000e/netdev.c -+++ b/e1000e/netdev.c -@@ -87,6 +87,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) - return ring->count + ring->next_to_clean - ring->next_to_use - 1; - } - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - /** - * e1000_receive_skb - helper function to handle Rx indications - * @adapter: board private structure -@@ -446,6 +450,10 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, - bool cleaned = 0; - unsigned int total_rx_bytes = 0, total_rx_packets = 0; - -+#ifdef DEV_NETMAP -+ if (netmap_rx_irq(netdev, 0, work_done)) -+ return 1; /* seems to be ignored */ -+#endif /* DEV_NETMAP */ - i = rx_ring->next_to_clean; - rx_desc = E1000_RX_DESC(*rx_ring, i); - buffer_info = &rx_ring->buffer_info[i]; -@@ -624,6 +632,10 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter) - unsigned int count = 0; - unsigned int total_tx_bytes = 0, total_tx_packets = 0; - -+#ifdef DEV_NETMAP -+ if (netmap_tx_irq(netdev, 0)) -+ return 1; /* cleaned ok */ -+#endif /* DEV_NETMAP */ - i = tx_ring->next_to_clean; - eop = tx_ring->buffer_info[i].next_to_watch; - eop_desc = E1000_TX_DESC(*tx_ring, eop); -@@ -2632,6 +2644,10 @@ static void e1000_configure(struct e1000_adapter *adapter) - e1000_configure_tx(adapter); - e1000_setup_rctl(adapter); - e1000_configure_rx(adapter); -+#ifdef DEV_NETMAP -+ if (e1000e_netmap_init_buffers(adapter)) -+ return; -+#endif /* DEV_NETMAP */ - adapter->alloc_rx_buf(adapter, e1000_desc_unused(adapter->rx_ring)); - } - -@@ -5227,6 +5243,9 @@ static int __devinit e1000_probe(struct pci_dev *pdev, - if (err) - goto err_register; - -+#ifdef DEV_NETMAP -+ e1000_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ - /* carrier off reporting is important to ethtool even BEFORE open */ - netif_carrier_off(netdev); - -@@ -5300,6 +5319,10 @@ static void __devexit e1000_remove(struct pci_dev *pdev) - kfree(adapter->tx_ring); - kfree(adapter->rx_ring); - -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - iounmap(adapter->hw.hw_addr); - if (adapter->hw.flash_address) - iounmap(adapter->hw.flash_address); diff --git a/LINUX/final-patches/vanilla--e1000e--30100--30400 b/LINUX/final-patches/vanilla--e1000e--30100--30400 deleted file mode 100644 index 7163c8fbf..000000000 --- a/LINUX/final-patches/vanilla--e1000e--30100--30400 +++ /dev/null @@ -1,69 +0,0 @@ -diff --git a/e1000e/netdev.c b/e1000e/netdev.c -index 2198e61..588f1ec 100644 ---- a/e1000e/netdev.c -+++ b/e1000e/netdev.c -@@ -452,6 +452,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) - return ring->count + ring->next_to_clean - ring->next_to_use - 1; - } - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - /** - * e1000_receive_skb - helper function to handle Rx indications - * @adapter: board private structure -@@ -849,6 +853,10 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, - bool cleaned = 0; - unsigned int total_rx_bytes = 0, total_rx_packets = 0; - -+#ifdef DEV_NETMAP -+ if (netmap_rx_irq(netdev, 0, work_done)) -+ return 1; /* seems to be ignored */ -+#endif /* DEV_NETMAP */ - i = rx_ring->next_to_clean; - rx_desc = E1000_RX_DESC(*rx_ring, i); - buffer_info = &rx_ring->buffer_info[i]; -@@ -1066,6 +1074,10 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter) - unsigned int count = 0; - unsigned int total_tx_bytes = 0, total_tx_packets = 0; - -+#ifdef DEV_NETMAP -+ if (netmap_tx_irq(netdev, 0)) -+ return 1; /* cleaned ok */ -+#endif /* DEV_NETMAP */ - i = tx_ring->next_to_clean; - eop = tx_ring->buffer_info[i].next_to_watch; - eop_desc = E1000_TX_DESC(*tx_ring, eop); -@@ -3177,6 +3189,10 @@ static void e1000_configure(struct e1000_adapter *adapter) - e1000_configure_tx(adapter); - e1000_setup_rctl(adapter); - e1000_configure_rx(adapter); -+#ifdef DEV_NETMAP -+ if (e1000e_netmap_init_buffers(adapter)) -+ return; -+#endif /* DEV_NETMAP */ - adapter->alloc_rx_buf(adapter, e1000_desc_unused(adapter->rx_ring), - GFP_KERNEL); - } -@@ -6147,6 +6163,9 @@ static int __devinit e1000_probe(struct pci_dev *pdev, - if (err) - goto err_register; - -+#ifdef DEV_NETMAP -+ e1000_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ - /* carrier off reporting is important to ethtool even BEFORE open */ - netif_carrier_off(netdev); - -@@ -6234,6 +6253,10 @@ static void __devexit e1000_remove(struct pci_dev *pdev) - kfree(adapter->tx_ring); - kfree(adapter->rx_ring); - -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - iounmap(adapter->hw.hw_addr); - if (adapter->hw.flash_address) - iounmap(adapter->hw.flash_address); diff --git a/LINUX/final-patches/vanilla--e1000e--30400--30900 b/LINUX/final-patches/vanilla--e1000e--30400--30900 deleted file mode 100644 index 8c8534cf5..000000000 --- a/LINUX/final-patches/vanilla--e1000e--30400--30900 +++ /dev/null @@ -1,69 +0,0 @@ -diff --git a/e1000e/netdev.c b/e1000e/netdev.c -index 9520a6a..bf94805 100644 ---- a/e1000e/netdev.c -+++ b/e1000e/netdev.c -@@ -467,6 +467,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) - return ring->count + ring->next_to_clean - ring->next_to_use - 1; - } - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - /** - * e1000_receive_skb - helper function to handle Rx indications - * @adapter: board private structure -@@ -875,6 +879,10 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring, int *work_done, - bool cleaned = false; - unsigned int total_rx_bytes = 0, total_rx_packets = 0; - -+#ifdef DEV_NETMAP -+ if (netmap_rx_irq(netdev, 0, work_done)) -+ return 1; /* seems to be ignored */ -+#endif /* DEV_NETMAP */ - i = rx_ring->next_to_clean; - rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); - staterr = le32_to_cpu(rx_desc->wb.upper.status_error); -@@ -1129,6 +1137,10 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) - unsigned int total_tx_bytes = 0, total_tx_packets = 0; - unsigned int bytes_compl = 0, pkts_compl = 0; - -+#ifdef DEV_NETMAP -+ if (netmap_tx_irq(netdev, 0)) -+ return 1; /* cleaned ok */ -+#endif /* DEV_NETMAP */ - i = tx_ring->next_to_clean; - eop = tx_ring->buffer_info[i].next_to_watch; - eop_desc = E1000_TX_DESC(*tx_ring, eop); -@@ -3358,6 +3370,10 @@ static void e1000_configure(struct e1000_adapter *adapter) - e1000e_setup_rss_hash(adapter); - e1000_setup_rctl(adapter); - e1000_configure_rx(adapter); -+#ifdef DEV_NETMAP -+ if (e1000e_netmap_init_buffers(adapter)) -+ return; -+#endif /* DEV_NETMAP */ - adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); - } - -@@ -6417,6 +6433,9 @@ static int __devinit e1000_probe(struct pci_dev *pdev, - if (err) - goto err_register; - -+#ifdef DEV_NETMAP -+ e1000_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ - /* carrier off reporting is important to ethtool even BEFORE open */ - netif_carrier_off(netdev); - -@@ -6504,6 +6523,10 @@ static void __devexit e1000_remove(struct pci_dev *pdev) - kfree(adapter->tx_ring); - kfree(adapter->rx_ring); - -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - iounmap(adapter->hw.hw_addr); - if (adapter->hw.flash_address) - iounmap(adapter->hw.flash_address); diff --git a/LINUX/final-patches/vanilla--e1000e--30900--99999 b/LINUX/final-patches/vanilla--e1000e--40000--99999 similarity index 61% rename from LINUX/final-patches/vanilla--e1000e--30900--99999 rename to LINUX/final-patches/vanilla--e1000e--40000--99999 index 742729b4f..075d9792e 100644 --- a/LINUX/final-patches/vanilla--e1000e--30900--99999 +++ b/LINUX/final-patches/vanilla--e1000e--40000--99999 @@ -1,8 +1,8 @@ diff --git a/e1000e/netdev.c b/e1000e/netdev.c -index 7e615e2..32ce408 100644 +index 1e8c40fd5c3d..7d560555ecff 100644 --- a/e1000e/netdev.c +++ b/e1000e/netdev.c -@@ -473,6 +473,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) +@@ -493,6 +493,10 @@ static int e1000_desc_unused(struct e1000_ring *ring) return ring->count + ring->next_to_clean - ring->next_to_use - 1; } @@ -13,7 +13,7 @@ index 7e615e2..32ce408 100644 /** * e1000e_systim_to_hwtstamp - convert system time value to hw time stamp * @adapter: board private structure -@@ -914,6 +918,10 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring, int *work_done, +@@ -935,6 +939,10 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring, int *work_done, bool cleaned = false; unsigned int total_rx_bytes = 0, total_rx_packets = 0; @@ -24,7 +24,7 @@ index 7e615e2..32ce408 100644 i = rx_ring->next_to_clean; rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); staterr = le32_to_cpu(rx_desc->wb.upper.status_error); -@@ -1203,6 +1211,10 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) +@@ -1223,6 +1231,10 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) unsigned int total_tx_bytes = 0, total_tx_packets = 0; unsigned int bytes_compl = 0, pkts_compl = 0; @@ -35,7 +35,29 @@ index 7e615e2..32ce408 100644 i = tx_ring->next_to_clean; eop = tx_ring->buffer_info[i].next_to_watch; eop_desc = E1000_TX_DESC(*tx_ring, eop); -@@ -3685,6 +3697,10 @@ static void e1000_configure(struct e1000_adapter *adapter) +@@ -1524,6 +1536,10 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_ring *rx_ring, int *work_done, + unsigned int total_rx_bytes = 0, total_rx_packets = 0; + struct skb_shared_info *shinfo; + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(netdev, 0, work_done)) ++ return 1; /* seems to be ignored */ ++#endif /* DEV_NETMAP */ + i = rx_ring->next_to_clean; + rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); +@@ -3128,6 +3144,10 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter) + adapter->rx_ps_pages = pages; + else + adapter->rx_ps_pages = 0; ++#ifdef DEV_NETMAP ++ /* Keep packet-split disabled with netmap. */ ++ adapter->rx_ps_pages = 0; ++#endif /* DEV_NETMAP */ + + if (adapter->rx_ps_pages) { + u32 psrctl = 0; +@@ -3733,6 +3753,10 @@ static void e1000_configure(struct e1000_adapter *adapter) e1000e_setup_rss_hash(adapter); e1000_setup_rctl(adapter); e1000_configure_rx(adapter); @@ -46,7 +68,7 @@ index 7e615e2..32ce408 100644 adapter->alloc_rx_buf(rx_ring, e1000_desc_unused(rx_ring), GFP_KERNEL); } -@@ -6768,6 +6784,9 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +@@ -7020,6 +7044,9 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto err_register; @@ -56,7 +78,7 @@ index 7e615e2..32ce408 100644 /* carrier off reporting is important to ethtool even BEFORE open */ netif_carrier_off(netdev); -@@ -6866,6 +6885,10 @@ static void e1000_remove(struct pci_dev *pdev) +@@ -7115,6 +7142,10 @@ static void e1000_remove(struct pci_dev *pdev) kfree(adapter->tx_ring); kfree(adapter->rx_ring); diff --git a/LINUX/final-patches/vanilla--forcedeth.c--20626--99999 b/LINUX/final-patches/vanilla--forcedeth.c--40000--99999 similarity index 70% rename from LINUX/final-patches/vanilla--forcedeth.c--20626--99999 rename to LINUX/final-patches/vanilla--forcedeth.c--40000--99999 index e9723a2aa..7888b0130 100644 --- a/LINUX/final-patches/vanilla--forcedeth.c--20626--99999 +++ b/LINUX/final-patches/vanilla--forcedeth.c--40000--99999 @@ -1,8 +1,8 @@ diff --git a/forcedeth.c b/forcedeth.c -index 9c0b1ba..b081d6b 100644 +index a41bb5e6b954..52ff9d8fb7bc 100644 --- a/forcedeth.c +++ b/forcedeth.c -@@ -1865,12 +1865,25 @@ static void nv_init_tx(struct net_device *dev) +@@ -1962,12 +1962,25 @@ static void nv_init_tx(struct net_device *dev) } } @@ -28,7 +28,7 @@ index 9c0b1ba..b081d6b 100644 if (!nv_optimized(np)) return nv_alloc_rx(dev); -@@ -3386,6 +3399,11 @@ static irqreturn_t nv_nic_irq_tx(int foo, void *data) +@@ -3660,6 +3673,11 @@ static irqreturn_t nv_nic_irq_tx(int foo, void *data) int i; unsigned long flags; @@ -39,8 +39,8 @@ index 9c0b1ba..b081d6b 100644 + for (i = 0;; i++) { events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_TX_ALL; - writel(NVREG_IRQ_TX_ALL, base + NvRegMSIXIrqStatus); -@@ -3497,6 +3515,11 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data) + writel(events, base + NvRegMSIXIrqStatus); +@@ -3772,6 +3790,11 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data) int i; unsigned long flags; @@ -51,8 +51,8 @@ index 9c0b1ba..b081d6b 100644 + for (i = 0;; i++) { events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_RX_ALL; - writel(NVREG_IRQ_RX_ALL, base + NvRegMSIXIrqStatus); -@@ -5645,6 +5668,10 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i + writel(events, base + NvRegMSIXIrqStatus); +@@ -5988,6 +6011,10 @@ static int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) goto out_error; } @@ -62,8 +62,8 @@ index 9c0b1ba..b081d6b 100644 + netif_carrier_off(dev); - dev_info(&pci_dev->dev, "ifname %s, PHY OUI 0x%x @ %d, addr %pM\n", -@@ -5728,6 +5755,10 @@ static void __devexit nv_remove(struct pci_dev *pci_dev) + /* Some NICs freeze when TX pause is enabled while NIC is +@@ -6084,6 +6111,10 @@ static void nv_remove(struct pci_dev *pci_dev) unregister_netdev(dev); diff --git a/LINUX/final-patches/vanilla--i40e--30c00--40100 b/LINUX/final-patches/vanilla--i40e--40000--40100 similarity index 64% rename from LINUX/final-patches/vanilla--i40e--30c00--40100 rename to LINUX/final-patches/vanilla--i40e--40000--40100 index 9e3b301de..3e141786b 100644 --- a/LINUX/final-patches/vanilla--i40e--30c00--40100 +++ b/LINUX/final-patches/vanilla--i40e--40000--40100 @@ -1,8 +1,8 @@ diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c -index 221aa47..4e61898 100644 +index dadda3c5d658..ae060c279662 100644 --- a/i40e/i40e_main.c +++ b/i40e/i40e_main.c -@@ -86,6 +86,11 @@ MODULE_DESCRIPTION("Intel(R) Ethernet Connection XL710 Network Driver"); +@@ -90,6 +90,11 @@ MODULE_DESCRIPTION("Intel(R) Ethernet Connection XL710 Network Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); @@ -14,7 +14,7 @@ index 221aa47..4e61898 100644 /** * i40e_allocate_dma_mem_d - OS specific memory alloc for shared code * @hw: pointer to the HW structure -@@ -2124,6 +2129,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) +@@ -2509,6 +2514,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) /* cache tail off for easier writes later */ ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); @@ -25,7 +25,18 @@ index 221aa47..4e61898 100644 return 0; } -@@ -2207,6 +2216,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) +@@ -2574,6 +2583,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -2596,6 +2609,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); writel(0, ring->tail); @@ -37,7 +48,7 @@ index 221aa47..4e61898 100644 i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); return 0; -@@ -5876,6 +5890,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) +@@ -8040,6 +8058,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) return -ENODEV; } @@ -49,7 +60,7 @@ index 221aa47..4e61898 100644 uplink_seid = vsi->uplink_seid; if (vsi->type != I40E_VSI_SRIOV) { if (vsi->netdev_registered) { -@@ -6124,6 +6143,11 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, +@@ -8367,6 +8390,11 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, break; } @@ -62,12 +73,12 @@ index 221aa47..4e61898 100644 err_rings: diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c -index 49d2cfa..83f3c56 100644 +index bbf1b1247ac4..0cfe40a333ab 100644 --- a/i40e/i40e_txrx.c +++ b/i40e/i40e_txrx.c -@@ -27,6 +27,10 @@ - +@@ -28,6 +28,10 @@ #include "i40e.h" + #include "i40e_prototype.h" +#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) +#include @@ -76,24 +87,24 @@ index 49d2cfa..83f3c56 100644 static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, u32 td_tag) { -@@ -329,6 +333,11 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) +@@ -682,6 +686,11 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) unsigned int total_packets = 0; unsigned int total_bytes = 0; +#ifdef DEV_NETMAP -+ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) + return true; +#endif /* DEV_NETMAP */ + tx_buf = &tx_ring->tx_bi[i]; tx_desc = I40E_TX_DESC(tx_ring, i); - -@@ -897,6 +906,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) - u32 rx_error, rx_status; - u64 qword; + i -= tx_ring->count; +@@ -1432,6 +1441,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + if (budget <= 0) + return 0; +#ifdef DEV_NETMAP -+ { ++ if (rx_ring->netdev) { + int dummy, nm_irq; + nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); + if (nm_irq != NM_IRQ_PASS) @@ -103,4 +114,4 @@ index 49d2cfa..83f3c56 100644 + rx_desc = I40E_RX_DESC(rx_ring, i); qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); - rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) + rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >> diff --git a/LINUX/final-patches/vanilla--i40e--40100--40300 b/LINUX/final-patches/vanilla--i40e--40100--40300 index 048bcfc69..3804d7f56 100644 --- a/LINUX/final-patches/vanilla--i40e--40100--40300 +++ b/LINUX/final-patches/vanilla--i40e--40100--40300 @@ -1,5 +1,5 @@ diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c -index 5b5bea1..c8a32923 100644 +index 5b5bea159bd5..45bfb58a5c5f 100644 --- a/i40e/i40e_main.c +++ b/i40e/i40e_main.c @@ -91,6 +91,11 @@ MODULE_DESCRIPTION("Intel(R) Ethernet Connection XL710 Network Driver"); @@ -25,7 +25,18 @@ index 5b5bea1..c8a32923 100644 return 0; } -@@ -2614,6 +2623,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) +@@ -2592,6 +2601,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -2614,6 +2627,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); writel(0, ring->tail); @@ -37,7 +48,7 @@ index 5b5bea1..c8a32923 100644 if (ring_is_ps_enabled(ring)) { i40e_alloc_rx_headers(ring); i40e_alloc_rx_buffers_ps(ring, I40E_DESC_UNUSED(ring)); -@@ -8515,6 +8529,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) +@@ -8515,6 +8533,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) return -ENODEV; } @@ -49,7 +60,7 @@ index 5b5bea1..c8a32923 100644 uplink_seid = vsi->uplink_seid; if (vsi->type != I40E_VSI_SRIOV) { if (vsi->netdev_registered) { -@@ -8850,6 +8869,11 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, +@@ -8850,6 +8873,11 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, break; } @@ -62,7 +73,7 @@ index 5b5bea1..c8a32923 100644 err_rings: diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c -index 9d95042d..80f8a88 100644 +index 9d95042d5a0f..a9223583a90c 100644 --- a/i40e/i40e_txrx.c +++ b/i40e/i40e_txrx.c @@ -29,6 +29,10 @@ @@ -81,14 +92,30 @@ index 9d95042d..80f8a88 100644 unsigned int total_bytes = 0; +#ifdef DEV_NETMAP -+ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) + return true; +#endif /* DEV_NETMAP */ + tx_buf = &tx_ring->tx_bi[i]; tx_desc = I40E_TX_DESC(tx_ring, i); i -= tx_ring->count; -@@ -1874,6 +1883,15 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) +@@ -1528,6 +1537,15 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) + u8 rx_ptype; + u64 qword; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++ } ++#endif /* DEV_NETMAP */ ++ + if (budget <= 0) + return 0; + +@@ -1874,6 +1892,15 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) budget_per_ring = max(budget/q_vector->num_ringpairs, 1); i40e_for_each_ring(ring, q_vector->rx) { diff --git a/LINUX/final-patches/vanilla--i40e--40300--40400 b/LINUX/final-patches/vanilla--i40e--40300--40400 index 153fcc710..6b0e2be39 100644 --- a/LINUX/final-patches/vanilla--i40e--40300--40400 +++ b/LINUX/final-patches/vanilla--i40e--40300--40400 @@ -1,5 +1,5 @@ diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c -index 3dd26cd..05b8960 100644 +index 3dd26cdd0bf2..ebed661a1148 100644 --- a/i40e/i40e_main.c +++ b/i40e/i40e_main.c @@ -94,6 +94,11 @@ MODULE_DESCRIPTION("Intel(R) Ethernet Connection XL710 Network Driver"); @@ -25,7 +25,18 @@ index 3dd26cd..05b8960 100644 return 0; } -@@ -2702,6 +2711,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) +@@ -2680,6 +2689,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -2702,6 +2715,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); writel(0, ring->tail); @@ -37,7 +48,7 @@ index 3dd26cd..05b8960 100644 if (ring_is_ps_enabled(ring)) { i40e_alloc_rx_headers(ring); i40e_alloc_rx_buffers_ps(ring, I40E_DESC_UNUSED(ring)); -@@ -8757,6 +8771,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) +@@ -8757,6 +8775,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) return -ENODEV; } @@ -49,7 +60,7 @@ index 3dd26cd..05b8960 100644 uplink_seid = vsi->uplink_seid; if (vsi->type != I40E_VSI_SRIOV) { if (vsi->netdev_registered) { -@@ -9101,6 +9120,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, +@@ -9101,6 +9124,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, (vsi->type == I40E_VSI_VMDQ2)) { ret = i40e_vsi_config_rss(vsi); } @@ -63,7 +74,7 @@ index 3dd26cd..05b8960 100644 err_rings: diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c -index 738aca6..77e14b3 100644 +index 738aca68f665..dc591bc2889b 100644 --- a/i40e/i40e_txrx.c +++ b/i40e/i40e_txrx.c @@ -29,6 +29,10 @@ @@ -82,14 +93,30 @@ index 738aca6..77e14b3 100644 unsigned int total_bytes = 0; +#ifdef DEV_NETMAP -+ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) + return true; +#endif /* DEV_NETMAP */ + tx_buf = &tx_ring->tx_bi[i]; tx_desc = I40E_TX_DESC(tx_ring, i); i -= tx_ring->count; -@@ -1929,6 +1938,15 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) +@@ -1526,6 +1535,15 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) + u8 rx_ptype; + u64 qword; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++ } ++#endif /* DEV_NETMAP */ ++ + if (budget <= 0) + return 0; + +@@ -1929,6 +1947,15 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) budget_per_ring = max(budget/q_vector->num_ringpairs, 1); i40e_for_each_ring(ring, q_vector->rx) { diff --git a/LINUX/final-patches/vanilla--i40e--40400--40700 b/LINUX/final-patches/vanilla--i40e--40400--40700 index e8669c0f4..2718d6752 100644 --- a/LINUX/final-patches/vanilla--i40e--40400--40700 +++ b/LINUX/final-patches/vanilla--i40e--40400--40700 @@ -1,5 +1,5 @@ diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c -index 4a9873ec..2803d11 100644 +index 4a9873ec28c7..58c0ca3401d3 100644 --- a/i40e/i40e_main.c +++ b/i40e/i40e_main.c @@ -97,6 +97,11 @@ MODULE_DESCRIPTION("Intel(R) Ethernet Connection XL710 Network Driver"); @@ -25,7 +25,18 @@ index 4a9873ec..2803d11 100644 return 0; } -@@ -2893,6 +2902,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) +@@ -2871,6 +2880,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -2893,6 +2906,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); writel(0, ring->tail); @@ -37,7 +48,7 @@ index 4a9873ec..2803d11 100644 if (ring_is_ps_enabled(ring)) { i40e_alloc_rx_headers(ring); i40e_alloc_rx_buffers_ps(ring, I40E_DESC_UNUSED(ring)); -@@ -9031,6 +9045,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) +@@ -9031,6 +9049,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) return -ENODEV; } @@ -49,7 +60,7 @@ index 4a9873ec..2803d11 100644 uplink_seid = vsi->uplink_seid; if (vsi->type != I40E_VSI_SRIOV) { if (vsi->netdev_registered) { -@@ -9377,6 +9396,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, +@@ -9377,6 +9400,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, (vsi->type == I40E_VSI_VMDQ2)) { ret = i40e_vsi_config_rss(vsi); } @@ -63,7 +74,7 @@ index 4a9873ec..2803d11 100644 err_rings: diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c -index 635b3ac..baed465 100644 +index 635b3ac17877..ad7d41e84bd4 100644 --- a/i40e/i40e_txrx.c +++ b/i40e/i40e_txrx.c @@ -29,6 +29,10 @@ @@ -82,14 +93,30 @@ index 635b3ac..baed465 100644 unsigned int total_bytes = 0; +#ifdef DEV_NETMAP -+ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) + return true; +#endif /* DEV_NETMAP */ + tx_buf = &tx_ring->tx_bi[i]; tx_desc = I40E_TX_DESC(tx_ring, i); i -= tx_ring->count; -@@ -1912,6 +1921,14 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) +@@ -1477,6 +1486,15 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) + u8 rx_ptype; + u64 qword; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++ } ++#endif /* DEV_NETMAP */ ++ + if (budget <= 0) + return 0; + +@@ -1912,6 +1930,14 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) i40e_for_each_ring(ring, q_vector->rx) { int cleaned; diff --git a/LINUX/final-patches/vanilla--i40e--40700--99999 b/LINUX/final-patches/vanilla--i40e--40700--40e00 similarity index 71% rename from LINUX/final-patches/vanilla--i40e--40700--99999 rename to LINUX/final-patches/vanilla--i40e--40700--40e00 index 912df8274..9e3f8736b 100644 --- a/LINUX/final-patches/vanilla--i40e--40700--99999 +++ b/LINUX/final-patches/vanilla--i40e--40700--40e00 @@ -1,5 +1,5 @@ diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c -index 501f15d..df24012 100644 +index 501f15d9f4d6..0073159583d7 100644 --- a/i40e/i40e_main.c +++ b/i40e/i40e_main.c @@ -110,6 +110,10 @@ MODULE_LICENSE("GPL"); @@ -24,7 +24,18 @@ index 501f15d..df24012 100644 return 0; } -@@ -2902,6 +2910,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) +@@ -2880,6 +2888,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -2902,6 +2914,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); writel(0, ring->tail); @@ -36,7 +47,7 @@ index 501f15d..df24012 100644 i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); return 0; -@@ -9511,6 +9524,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) +@@ -9511,6 +9528,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) return -ENODEV; } @@ -48,7 +59,7 @@ index 501f15d..df24012 100644 uplink_seid = vsi->uplink_seid; if (vsi->type != I40E_VSI_SRIOV) { if (vsi->netdev_registered) { -@@ -9911,6 +9929,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, +@@ -9911,6 +9933,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, (vsi->type == I40E_VSI_VMDQ2)) { ret = i40e_vsi_config_rss(vsi); } @@ -62,7 +73,7 @@ index 501f15d..df24012 100644 err_rings: diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c -index a8868e1..9186a17 100644 +index a8868e1bf832..274851fe9c52 100644 --- a/i40e/i40e_txrx.c +++ b/i40e/i40e_txrx.c @@ -29,6 +29,10 @@ @@ -81,22 +92,24 @@ index a8868e1..9186a17 100644 unsigned int budget = vsi->work_limit; +#ifdef DEV_NETMAP -+ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) + return true; +#endif /* DEV_NETMAP */ + tx_buf = &tx_ring->tx_bi[i]; tx_desc = I40E_TX_DESC(tx_ring, i); i -= tx_ring->count; -@@ -1753,6 +1762,13 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) +@@ -1753,6 +1762,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); bool failure = false; +#ifdef DEV_NETMAP -+ int dummy; -+ if (rx_ring->netdev && -+ netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy) != NM_IRQ_PASS) -+ return 1; ++ if (rx_ring->netdev) { ++ int dummy; ++ if (rx_ring->netdev && ++ netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy) != NM_IRQ_PASS) ++ return 1; ++ } +#endif /* DEV_NETMAP */ + while (likely(total_rx_packets < budget)) { diff --git a/LINUX/final-patches/vanilla--i40e--40e00--41000 b/LINUX/final-patches/vanilla--i40e--40e00--41000 new file mode 100644 index 000000000..40be9499f --- /dev/null +++ b/LINUX/final-patches/vanilla--i40e--40e00--41000 @@ -0,0 +1,117 @@ +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 6498da8806cb..7fbe7d5a62f9 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -113,6 +113,10 @@ MODULE_LICENSE("GPL"); + MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif + + /** + * i40e_allocate_dma_mem_d - OS specific memory alloc for shared code +@@ -2980,6 +2984,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3031,6 +3039,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3059,6 +3071,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -10051,6 +10068,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -10418,6 +10440,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 120c68f78951..22ee8ace0ba9 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -31,6 +31,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -745,6 +749,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2069,6 +2078,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); + bool failure = false, xdp_xmit = false; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy; ++ if (rx_ring->netdev && ++ netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy) != NM_IRQ_PASS) ++ return 1; ++ } ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < (unsigned int)budget)) { + struct i40e_rx_buffer *rx_buffer; + union i40e_rx_desc *rx_desc; diff --git a/LINUX/final-patches/vanilla--i40e--41000--41400 b/LINUX/final-patches/vanilla--i40e--41000--41400 new file mode 100644 index 000000000..581a556e5 --- /dev/null +++ b/LINUX/final-patches/vanilla--i40e--41000--41400 @@ -0,0 +1,117 @@ +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index e31adbc75f9c..64564a6a1301 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -122,6 +122,10 @@ MODULE_LICENSE("GPL"); + MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif + + /** + * i40e_allocate_dma_mem_d - OS specific memory alloc for shared code +@@ -3195,6 +3199,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3246,6 +3254,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3274,6 +3286,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); + + return 0; +@@ -12152,6 +12169,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -12519,6 +12541,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index e554aa6cf070..c4882f93db78 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -32,6 +32,10 @@ + #include "i40e_trace.h" + #include "i40e_prototype.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, + u32 td_tag) + { +@@ -799,6 +803,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2135,6 +2144,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false, xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy; ++ if (rx_ring->netdev && ++ netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy) != NM_IRQ_PASS) ++ return 1; ++ } ++#endif /* DEV_NETMAP */ ++ + xdp.rxq = &rx_ring->xdp_rxq; + + while (likely(total_rx_packets < (unsigned int)budget)) { diff --git a/LINUX/final-patches/vanilla--i40e--41400--50800 b/LINUX/final-patches/vanilla--i40e--41400--50800 new file mode 100644 index 000000000..7146a833e --- /dev/null +++ b/LINUX/final-patches/vanilla--i40e--41400--50800 @@ -0,0 +1,116 @@ +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 0e5dc74b4ef2..604130f5879d 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -95,6 +95,10 @@ MODULE_LICENSE("GPL v2"); + MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif + + /** + * i40e_allocate_dma_mem_d - OS specific memory alloc for shared code +@@ -3170,6 +3174,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3254,6 +3262,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3282,6 +3294,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ + ok = ring->xsk_umem ? + i40e_alloc_rx_buffers_zc(ring, I40E_DESC_UNUSED(ring)) : + !i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); +@@ -12675,6 +12691,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -13042,6 +13063,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index d0a95424ce58..07fb1bcc909c 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -10,6 +10,10 @@ + #include "i40e_txrx_common.h" + #include "i40e_xsk.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -781,6 +785,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2334,6 +2343,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy; ++ if (rx_ring->netdev && ++ netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy) != NM_IRQ_PASS) ++ return 1; ++ } ++#endif /* DEV_NETMAP */ ++ + xdp.rxq = &rx_ring->xdp_rxq; + + while (likely(total_rx_packets < (unsigned int)budget)) { diff --git a/LINUX/final-patches/vanilla--i40e--50800--50a00 b/LINUX/final-patches/vanilla--i40e--50800--50a00 new file mode 100644 index 000000000..15c620697 --- /dev/null +++ b/LINUX/final-patches/vanilla--i40e--50800--50a00 @@ -0,0 +1,117 @@ +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 56ecd6c3f236..0f52cbe03dcd 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -104,6 +104,10 @@ MODULE_LICENSE("GPL v2"); + MODULE_VERSION(DRV_VERSION); + + static struct workqueue_struct *i40e_wq; ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif + + /** + * i40e_allocate_dma_mem_d - OS specific memory alloc for shared code +@@ -3241,6 +3245,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3330,6 +3338,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3358,6 +3370,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + if (ring->xsk_umem) { + xsk_buff_set_rxq_info(ring->xsk_umem, &ring->xdp_rxq); + ok = i40e_alloc_rx_buffers_zc(ring, I40E_DESC_UNUSED(ring)); +@@ -13363,6 +13380,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -13730,6 +13752,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index f9555c847f73..ab0089042e0b 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -10,6 +10,10 @@ + #include "i40e_txrx_common.h" + #include "i40e_xsk.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -782,6 +786,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2320,6 +2329,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy; ++ if (rx_ring->netdev && ++ netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy) != NM_IRQ_PASS) ++ return 1; ++ } ++#endif /* DEV_NETMAP */ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/vanilla--i40e--50a00--50f00 b/LINUX/final-patches/vanilla--i40e--50a00--50f00 new file mode 100644 index 000000000..8ff538012 --- /dev/null +++ b/LINUX/final-patches/vanilla--i40e--50a00--50f00 @@ -0,0 +1,117 @@ +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 1337686bd099..71244892b7a8 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -95,6 +95,10 @@ MODULE_DESCRIPTION("Intel(R) Ethernet Connection XL710 Network Driver"); + MODULE_LICENSE("GPL v2"); + + static struct workqueue_struct *i40e_wq; ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif + + /** + * i40e_allocate_dma_mem_d - OS specific memory alloc for shared code +@@ -3254,6 +3258,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3344,6 +3352,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3372,6 +3384,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + if (ring->xsk_pool) { + xsk_pool_set_rxq_info(ring->xsk_pool, &ring->xdp_rxq); + ok = i40e_alloc_rx_buffers_zc(ring, I40E_DESC_UNUSED(ring)); +@@ -13281,6 +13298,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -13646,6 +13668,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 3f5825fa67c9..102e9b18eff0 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -10,6 +10,10 @@ + #include "i40e_txrx_common.h" + #include "i40e_xsk.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -782,6 +786,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2340,6 +2349,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + bool failure = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy; ++ if (rx_ring->netdev && ++ netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy) != NM_IRQ_PASS) ++ return 1; ++ } ++#endif /* DEV_NETMAP */ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/vanilla--i40e--50f00--51000 b/LINUX/final-patches/vanilla--i40e--50f00--51000 new file mode 100644 index 000000000..5ec51cbcc --- /dev/null +++ b/LINUX/final-patches/vanilla--i40e--50f00--51000 @@ -0,0 +1,115 @@ +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index e04b540cedc8..26d4d4edb438 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -98,6 +98,10 @@ MODULE_DESCRIPTION("Intel(R) Ethernet Connection XL710 Network Driver"); + MODULE_LICENSE("GPL v2"); + + static struct workqueue_struct *i40e_wq; ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif + + /** + * i40e_allocate_dma_mem_d - OS specific memory alloc for shared code +@@ -3255,6 +3259,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3356,6 +3364,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3386,6 +3398,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + if (ring->xsk_pool) { + xsk_pool_set_rxq_info(ring->xsk_pool, &ring->xdp_rxq); + ok = i40e_alloc_rx_buffers_zc(ring, I40E_DESC_UNUSED(ring)); +@@ -13772,6 +13789,11 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -14137,6 +14159,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 10a83e5385c7..1fcf408e2ef3 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -10,6 +10,10 @@ + #include "i40e_txrx_common.h" + #include "i40e_xsk.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -938,6 +942,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2448,6 +2457,13 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + int xdp_res = 0; + ++#ifdef DEV_NETMAP ++ int dummy; ++ if (rx_ring->netdev && ++ netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy) != NM_IRQ_PASS) ++ return 1; ++#endif /* DEV_NETMAP */ ++ + #if (PAGE_SIZE < 8192) + frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/vanilla--i40e--51000--60400 b/LINUX/final-patches/vanilla--i40e--51000--60400 new file mode 100644 index 000000000..92f96618c --- /dev/null +++ b/LINUX/final-patches/vanilla--i40e--51000--60400 @@ -0,0 +1,116 @@ +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index 61afc220fc6c..45c3feb5efb0 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -98,6 +98,10 @@ MODULE_DESCRIPTION("Intel(R) Ethernet Connection XL710 Network Driver"); + MODULE_LICENSE("GPL v2"); + + static struct workqueue_struct *i40e_wq; ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif + + static void netdev_hw_addr_refcnt(struct i40e_mac_filter *f, + struct net_device *netdev, int delta) +@@ -3297,6 +3301,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3398,6 +3406,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3428,6 +3440,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + if (ring->xsk_pool) { + xsk_pool_set_rxq_info(ring->xsk_pool, &ring->xdp_rxq); + ok = i40e_alloc_rx_buffers_zc(ring, I40E_DESC_UNUSED(ring)); +@@ -13859,6 +13876,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -14227,6 +14250,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 10a83e5385c7..1fcf408e2ef3 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -10,6 +10,10 @@ + #include "i40e_txrx_common.h" + #include "i40e_xsk.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -938,6 +942,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2448,6 +2457,13 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) + struct xdp_buff xdp; + int xdp_res = 0; + ++#ifdef DEV_NETMAP ++ int dummy; ++ if (rx_ring->netdev && ++ netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy) != NM_IRQ_PASS) ++ return 1; ++#endif /* DEV_NETMAP */ ++ + #if (PAGE_SIZE < 8192) + frame_sz = i40e_rx_frame_truesize(rx_ring, 0); + #endif diff --git a/LINUX/final-patches/vanilla--i40e--60400--99999 b/LINUX/final-patches/vanilla--i40e--60400--99999 new file mode 100644 index 000000000..57bde714b --- /dev/null +++ b/LINUX/final-patches/vanilla--i40e--60400--99999 @@ -0,0 +1,116 @@ +diff --git a/i40e/i40e_main.c b/i40e/i40e_main.c +index b847bd105b16..605cc4551b5e 100644 +--- a/i40e/i40e_main.c ++++ b/i40e/i40e_main.c +@@ -100,6 +100,10 @@ MODULE_DESCRIPTION("Intel(R) Ethernet Connection XL710 Network Driver"); + MODULE_LICENSE("GPL v2"); + + static struct workqueue_struct *i40e_wq; ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_I40E_MAIN ++#include ++#endif + + static void netdev_hw_addr_refcnt(struct i40e_mac_filter *f, + struct net_device *netdev, int delta) +@@ -3548,6 +3552,10 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) + /* cache tail off for easier writes later */ + ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); + ++#ifdef DEV_NETMAP ++ i40e_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -3644,6 +3652,10 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + /* set the prefena field to 1 because the manual says to */ + rx_ctx.prefena = 1; + ++#ifdef DEV_NETMAP ++ i40e_netmap_preconfigure_rx_ring(ring, &rx_ctx); ++#endif /* DEV_NETMAP */ ++ + /* clear the context in the HMC */ + err = i40e_clear_lan_rx_queue_context(hw, pf_q); + if (err) { +@@ -3680,6 +3692,11 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) + ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (i40e_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + if (ring->xsk_pool) { + xsk_pool_set_rxq_info(ring->xsk_pool, &ring->xdp_rxq); + ok = i40e_alloc_rx_buffers_zc(ring, I40E_DESC_UNUSED(ring)); +@@ -14209,6 +14226,12 @@ int i40e_vsi_release(struct i40e_vsi *vsi) + return -ENODEV; + } + set_bit(__I40E_VSI_RELEASING, vsi->state); ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ netmap_detach(vsi->netdev); ++#endif ++ + uplink_seid = vsi->uplink_seid; + if (vsi->type != I40E_VSI_SRIOV) { + if (vsi->netdev_registered) { +@@ -14577,6 +14600,12 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, + (vsi->type == I40E_VSI_VMDQ2)) { + ret = i40e_vsi_config_rss(vsi); + } ++ ++#ifdef DEV_NETMAP ++ if (vsi->netdev_registered) ++ i40e_netmap_attach(vsi); ++#endif ++ + return vsi; + + err_rings: +diff --git a/i40e/i40e_txrx.c b/i40e/i40e_txrx.c +index 8b8bf4880faa..c0aa5b742342 100644 +--- a/i40e/i40e_txrx.c ++++ b/i40e/i40e_txrx.c +@@ -11,6 +11,10 @@ + #include "i40e_txrx_common.h" + #include "i40e_xsk.h" + ++#if defined(CONFIG_NETMAP) || defined (CONFIG_NETMAP_MODULE) ++#include ++#endif /* DEV_NETMAP */ ++ + #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) + /** + * i40e_fdir - Generate a Flow Director descriptor based on fdata +@@ -938,6 +942,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, + unsigned int total_bytes = 0, total_packets = 0; + unsigned int budget = vsi->work_limit; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ + tx_buf = &tx_ring->tx_bi[i]; + tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2503,6 +2512,13 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, + bool failure = false; + int xdp_res = 0; + ++#ifdef DEV_NETMAP ++ int dummy; ++ if (rx_ring->netdev && ++ netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy) != NM_IRQ_PASS) ++ return 1; ++#endif /* DEV_NETMAP */ ++ + xdp_prog = READ_ONCE(rx_ring->xdp_prog); + + while (likely(total_rx_packets < (unsigned int)budget)) { diff --git a/LINUX/final-patches/vanilla--ice--50500--50800 b/LINUX/final-patches/vanilla--ice--50500--50800 new file mode 100644 index 000000000..583c9e6a8 --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--50500--50800 @@ -0,0 +1,128 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index 77d6a0291e97..2dddf309f5c9 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -3,6 +3,10 @@ + + #include "ice_base.h" + #include "ice_dcb_lib.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif + + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI +@@ -396,6 +400,10 @@ int ice_setup_rx_ctx(struct ice_ring *ring) + wr32(hw, QRXFLXP_CNTXT(pf_q), regval); + } + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Absolute queue number out of 2K needs to be passed */ + err = ice_write_rxq_ctx(hw, &rlan_ctx, pf_q); + if (err) { +@@ -418,6 +426,11 @@ int ice_setup_rx_ctx(struct ice_ring *ring) + ring->tail = hw->hw_addr + QRX_TAIL(pf_q); + writel(0, ring->tail); + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + err = ring->xsk_umem ? + ice_alloc_rx_bufs_slow_zc(ring, ICE_DESC_UNUSED(ring)) : + ice_alloc_rx_bufs(ring, ICE_DESC_UNUSED(ring)); +@@ -656,6 +669,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *ring, + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); + ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 69bff085acf7..c0c88851609d 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -41,6 +41,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all), hw debug_mask (0x8XXXX + MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + #endif /* !CONFIG_DYNAMIC_DEBUG */ + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + static struct workqueue_struct *ice_wq; + static const struct net_device_ops ice_netdev_safe_mode_ops; + static const struct net_device_ops ice_netdev_ops; +@@ -3365,6 +3370,9 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + /* print PCI link speed and width */ + pcie_print_link_status(pf->pdev); + ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + err_alloc_sw_unroll: +@@ -3396,6 +3404,9 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) + break; +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 2c212f64d99f..30eeb4bbcc77 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -15,6 +15,10 @@ + + #define ICE_RX_HDR_SIZE 256 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_unmap_and_free_tx_buf - Release a Tx buffer + * @ring: the ring that owns the buffer +@@ -122,6 +126,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + tx_buf = &tx_ring->tx_buf[i]; + tx_desc = ICE_TX_DESC(tx_ring, i); +@@ -992,6 +1000,16 @@ static int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + + xdp.rxq = &rx_ring->xdp_rxq; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* start the loop to process Rx packets bounded by 'budget' */ + while (likely(total_rx_pkts < (unsigned int)budget)) { + union ice_32b_rx_flex_desc *rx_desc; diff --git a/LINUX/final-patches/vanilla--ice--50800--50c00 b/LINUX/final-patches/vanilla--ice--50800--50c00 new file mode 100644 index 000000000..0e22be056 --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--50800--50c00 @@ -0,0 +1,130 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index d620d26d42ed..a103c5442483 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -5,6 +5,10 @@ + #include "ice_base.h" + #include "ice_lib.h" + #include "ice_dcb_lib.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif + + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI +@@ -385,6 +389,10 @@ int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -433,6 +441,11 @@ int ice_setup_rx_ctx(struct ice_ring *ring) + return 0; + } + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + ice_alloc_rx_bufs(ring, num_bufs); + + return 0; +@@ -682,6 +695,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *ring, + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); + ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 4cbd49c87568..07d22e56f33b 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -43,6 +43,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all), hw debug_mask (0x8XXXX + MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + #endif /* !CONFIG_DYNAMIC_DEBUG */ + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + static struct workqueue_struct *ice_wq; + static const struct net_device_ops ice_netdev_safe_mode_ops; + static const struct net_device_ops ice_netdev_ops; +@@ -3502,6 +3507,10 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + probe_done: + /* ready to go, so clear down state bit */ + clear_bit(__ICE_DOWN, pf->state); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + err_alloc_sw_unroll: +@@ -3537,6 +3546,9 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) + break; +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index abdb137c8bb7..0962a043fc0b 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -18,6 +18,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -208,6 +212,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + tx_buf = &tx_ring->tx_buf[i]; + tx_desc = ICE_TX_DESC(tx_ring, i); +@@ -1094,6 +1102,17 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + bool failure; + + xdp.rxq = &rx_ring->xdp_rxq; ++ ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = ice_rx_frame_truesize(rx_ring, 0); diff --git a/LINUX/final-patches/vanilla--ice--50c00--50d00 b/LINUX/final-patches/vanilla--ice--50c00--50d00 new file mode 100644 index 000000000..65500e09e --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--50c00--50d00 @@ -0,0 +1,129 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index 1148d768f8ed..7c6f966a0167 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -5,6 +5,10 @@ + #include "ice_base.h" + #include "ice_lib.h" + #include "ice_dcb_lib.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif + + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI +@@ -401,6 +405,10 @@ int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -453,6 +461,11 @@ int ice_setup_rx_ctx(struct ice_ring *ring) + return 0; + } + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + ice_alloc_rx_bufs(ring, num_bufs); + + return 0; +@@ -702,6 +715,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *ring, + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); + ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +diff --git a/ice/ice_main.c b/ice/ice_main.c +index d821c687f239..316669305b98 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -35,6 +35,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all), hw debug_mask (0x8XXXX + MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + #endif /* !CONFIG_DYNAMIC_DEBUG */ + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + static struct workqueue_struct *ice_wq; + static const struct net_device_ops ice_netdev_safe_mode_ops; + static const struct net_device_ops ice_netdev_ops; +@@ -4263,6 +4268,10 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + probe_done: + /* ready to go, so clear down state bit */ + clear_bit(__ICE_DOWN, pf->state); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + err_send_version_unroll: +@@ -4362,6 +4371,9 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) + break; +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index b91dcfd12727..a2e0687c1771 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -18,6 +18,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -208,6 +212,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + tx_buf = &tx_ring->tx_buf[i]; + tx_desc = ICE_TX_DESC(tx_ring, i); +@@ -1062,6 +1070,16 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + struct xdp_buff xdp; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ + #if (PAGE_SIZE < 8192) + frame_sz = ice_rx_frame_truesize(rx_ring, 0); diff --git a/LINUX/final-patches/vanilla--ice--50d00--50e00 b/LINUX/final-patches/vanilla--ice--50d00--50e00 new file mode 100644 index 000000000..354388294 --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--50d00--50e00 @@ -0,0 +1,129 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index 5985a7e5ca8a..7db16b634a4e 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -5,6 +5,10 @@ + #include "ice_base.h" + #include "ice_lib.h" + #include "ice_dcb_lib.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif + + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI +@@ -424,6 +428,10 @@ int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -476,6 +484,11 @@ int ice_setup_rx_ctx(struct ice_ring *ring) + return 0; + } + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + ice_alloc_rx_bufs(ring, num_bufs); + + return 0; +@@ -728,6 +741,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *ring, + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); + ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 0eb2307325d3..41b3bb90bd8b 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -35,6 +35,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all), hw debug_mask (0x8XXXX + MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + #endif /* !CONFIG_DYNAMIC_DEBUG */ + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + static struct workqueue_struct *ice_wq; + static const struct net_device_ops ice_netdev_safe_mode_ops; + static const struct net_device_ops ice_netdev_ops; +@@ -4282,6 +4287,10 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + + /* ready to go, so clear down state bit */ + clear_bit(ICE_DOWN, pf->state); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + err_netdev_reg: +@@ -4382,6 +4391,9 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) + break; +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 04748aa4c7c8..b02a895af69e 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -18,6 +18,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -208,6 +212,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + tx_buf = &tx_ring->tx_buf[i]; + tx_desc = ICE_TX_DESC(tx_ring, i); +@@ -1067,6 +1075,16 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + struct xdp_buff xdp; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ + #if (PAGE_SIZE < 8192) + frame_sz = ice_rx_frame_truesize(rx_ring, 0); diff --git a/LINUX/final-patches/vanilla--ice--50e00--51000 b/LINUX/final-patches/vanilla--ice--50e00--51000 new file mode 100644 index 000000000..5cf2745fc --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--50e00--51000 @@ -0,0 +1,128 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index c36057efc7ae..5dec738c6fe9 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -5,6 +5,10 @@ + #include "ice_base.h" + #include "ice_lib.h" + #include "ice_dcb_lib.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif + + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI +@@ -395,6 +399,10 @@ static int ice_setup_rx_ctx(struct ice_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -512,6 +520,11 @@ int ice_vsi_cfg_rxq(struct ice_ring *ring) + return 0; + } + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + ice_alloc_rx_bufs(ring, num_bufs); + + return 0; +@@ -764,6 +777,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_ring *ring, + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); + ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +diff --git a/ice/ice_main.c b/ice/ice_main.c +index fe2ded775f25..e305a1cceea6 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -43,6 +43,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + + static DEFINE_IDA(ice_aux_ida); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + static struct workqueue_struct *ice_wq; + static const struct net_device_ops ice_netdev_safe_mode_ops; + static const struct net_device_ops ice_netdev_ops; +@@ -4497,6 +4502,9 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + dev_warn(dev, "RDMA is not supported on this device\n"); + } + ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + err_init_aux_unroll: +@@ -4600,6 +4608,9 @@ static void ice_remove(struct pci_dev *pdev) + if (!pf) + return; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) + break; +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 6ee8e0032d52..912416091772 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -19,6 +19,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -209,6 +213,10 @@ static bool ice_clean_tx_irq(struct ice_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + tx_buf = &tx_ring->tx_buf[i]; + tx_desc = ICE_TX_DESC(tx_ring, i); +@@ -1071,6 +1079,16 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget) + struct xdp_buff xdp; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ + #if (PAGE_SIZE < 8192) + frame_sz = ice_rx_frame_truesize(rx_ring, 0); diff --git a/LINUX/final-patches/vanilla--ice--51000--51100 b/LINUX/final-patches/vanilla--ice--51000--51100 new file mode 100644 index 000000000..3afd804fb --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--51000--51100 @@ -0,0 +1,130 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index fafe020e46ee..a405f76a8824 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -5,6 +5,10 @@ + #include "ice_base.h" + #include "ice_lib.h" + #include "ice_dcb_lib.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif + + static bool ice_alloc_rx_buf_zc(struct ice_rx_ring *rx_ring) + { +@@ -446,6 +450,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -568,6 +576,11 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + ice_alloc_rx_bufs(ring, num_bufs); + + return 0; +@@ -834,6 +847,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); + ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 73c61cdb036f..130eaaac16a1 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -47,6 +47,11 @@ static DEFINE_IDA(ice_aux_ida); + DEFINE_STATIC_KEY_FALSE(ice_xdp_locking_key); + EXPORT_SYMBOL(ice_xdp_locking_key); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + static struct workqueue_struct *ice_wq; + static const struct net_device_ops ice_netdev_safe_mode_ops; + static const struct net_device_ops ice_netdev_ops; +@@ -4742,6 +4747,10 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + } + + ice_devlink_register(pf); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + err_init_aux_unroll: +@@ -4841,6 +4850,10 @@ static void ice_remove(struct pci_dev *pdev) + struct ice_pf *pf = pci_get_drvdata(pdev); + int i; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + ice_devlink_unregister(pf); + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index dccf09eefc75..c62ae39cf418 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -21,6 +21,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -218,6 +222,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + tx_buf = &tx_ring->tx_buf[i]; + tx_desc = ICE_TX_DESC(tx_ring, i); +@@ -1104,6 +1112,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + struct xdp_buff xdp; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ + #if (PAGE_SIZE < 8192) + frame_sz = ice_rx_frame_truesize(rx_ring, 0); diff --git a/LINUX/final-patches/vanilla--ice--51100--51200 b/LINUX/final-patches/vanilla--ice--51100--51200 new file mode 100644 index 000000000..5352cc9e0 --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--51100--51200 @@ -0,0 +1,130 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index 1a5ece3bce79..b815458ab34a 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -5,6 +5,10 @@ + #include "ice_base.h" + #include "ice_lib.h" + #include "ice_dcb_lib.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif + + static bool ice_alloc_rx_buf_zc(struct ice_rx_ring *rx_ring) + { +@@ -446,6 +450,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -568,6 +576,11 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + ice_alloc_rx_bufs(ring, num_bufs); + + return 0; +@@ -834,6 +847,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); + ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +diff --git a/ice/ice_main.c b/ice/ice_main.c +index b7e8744b0c0a..eecab6226fe5 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -47,6 +47,11 @@ static DEFINE_IDA(ice_aux_ida); + DEFINE_STATIC_KEY_FALSE(ice_xdp_locking_key); + EXPORT_SYMBOL(ice_xdp_locking_key); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + static struct workqueue_struct *ice_wq; + static const struct net_device_ops ice_netdev_safe_mode_ops; + static const struct net_device_ops ice_netdev_ops; +@@ -4757,6 +4762,10 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + } + + ice_devlink_register(pf); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + err_init_aux_unroll: +@@ -4857,6 +4866,10 @@ static void ice_remove(struct pci_dev *pdev) + struct ice_pf *pf = pci_get_drvdata(pdev); + int i; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + ice_devlink_unregister(pf); + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 3e38695f1c9d..5d1e4cba1b56 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -22,6 +22,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -219,6 +223,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + if (!ice_ring_is_xdp(tx_ring)) +@@ -1112,6 +1120,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + struct xdp_buff xdp; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ + #if (PAGE_SIZE < 8192) + frame_sz = ice_rx_frame_truesize(rx_ring, 0); diff --git a/LINUX/final-patches/vanilla--ice--51300--60300 b/LINUX/final-patches/vanilla--ice--51300--60300 new file mode 100644 index 000000000..cbbcc9dc9 --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--51300--60300 @@ -0,0 +1,131 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index 136d7911adb4..a9caf17a5160 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_lib.h" + #include "ice_dcb_lib.h" + #include "ice_sriov.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + + static bool ice_alloc_rx_buf_zc(struct ice_rx_ring *rx_ring) + { +@@ -461,6 +466,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -583,6 +592,11 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + ice_alloc_rx_bufs(ring, num_bufs); + + return 0; +@@ -849,6 +863,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); + ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 9f02b60459f1..0186182d5e16 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -48,6 +48,11 @@ static DEFINE_IDA(ice_aux_ida); + DEFINE_STATIC_KEY_FALSE(ice_xdp_locking_key); + EXPORT_SYMBOL(ice_xdp_locking_key); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -4872,6 +4877,10 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) + } + + ice_devlink_register(pf); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif + return 0; + + err_init_aux_unroll: +@@ -4972,6 +4981,10 @@ static void ice_remove(struct pci_dev *pdev) + struct ice_pf *pf = pci_get_drvdata(pdev); + int i; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + ice_devlink_unregister(pf); + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 836dce840712..b5a272c8cfe4 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -23,6 +23,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -222,6 +226,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + netdev_txq_bql_complete_prefetchw(txring_txq(tx_ring)); +@@ -1117,6 +1125,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + struct xdp_buff xdp; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ + #if (PAGE_SIZE < 8192) + frame_sz = ice_rx_frame_truesize(rx_ring, 0); diff --git a/LINUX/final-patches/vanilla--ice--60300--60b00 b/LINUX/final-patches/vanilla--ice--60300--60b00 new file mode 100644 index 000000000..ca0781998 --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--60300--60b00 @@ -0,0 +1,133 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index 1911d644dfa8..366a70eda634 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_lib.h" + #include "ice_dcb_lib.h" + #include "ice_sriov.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI +@@ -445,6 +450,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + /* Rx queue threshold in units of 64 */ + rlan_ctx.lrxqthresh = 1; + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -568,6 +577,11 @@ int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + ice_alloc_rx_bufs(ring, num_bufs); + + return 0; +@@ -834,6 +848,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); + ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 0d8b8c6f9bd3..b58bc2ce4b95 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -48,6 +48,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + DEFINE_STATIC_KEY_FALSE(ice_xdp_locking_key); + EXPORT_SYMBOL(ice_xdp_locking_key); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -5055,6 +5060,12 @@ static int ice_init(struct ice_pf *pf) + /* since everything is good, start the service timer */ + mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period)); + ++ ice_devlink_register(pf); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_link: +@@ -5318,6 +5329,10 @@ static void ice_remove(struct pci_dev *pdev) + struct ice_pf *pf = pci_get_drvdata(pdev); + int i; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) + break; +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 4fcf2d07eb85..0ad1b7528bfb 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -23,6 +23,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -222,6 +226,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + netdev_txq_bql_complete_prefetchw(txring_txq(tx_ring)); +@@ -1162,6 +1170,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + bool failure; + u32 first; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ + #if (PAGE_SIZE < 8192) + xdp->frame_sz = ice_rx_frame_truesize(rx_ring, 0); diff --git a/LINUX/final-patches/vanilla--ice--60b00--61100 b/LINUX/final-patches/vanilla--ice--60b00--61100 new file mode 100644 index 000000000..fc1be9715 --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--60b00--61100 @@ -0,0 +1,133 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index c158749a80e0..5c364110a07c 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_lib.h" + #include "ice_dcb_lib.h" + #include "ice_sriov.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI +@@ -456,6 +461,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + rxdid = ICE_RXDID_FLEX_NIC_2; + } + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -622,6 +631,11 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + ice_alloc_rx_bufs(ring, num_bufs); + + return 0; +@@ -942,6 +956,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); + ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +diff --git a/ice/ice_main.c b/ice/ice_main.c +index ea780d468579..194f0b4ca3bb 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -51,6 +51,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + DEFINE_STATIC_KEY_FALSE(ice_xdp_locking_key); + EXPORT_SYMBOL(ice_xdp_locking_key); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -5134,6 +5139,12 @@ static int ice_init(struct ice_pf *pf) + /* since everything is good, start the service timer */ + mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period)); + ++ ice_devlink_register(pf); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_link: +@@ -5436,6 +5447,10 @@ static void ice_remove(struct pci_dev *pdev) + struct ice_pf *pf = pci_get_drvdata(pdev); + int i; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) + break; +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index c9bc3f1add5d..dc1247b501ef 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -23,6 +23,10 @@ + #define FDIR_DESC_RXDID 0x40 + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -222,6 +226,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + netdev_txq_bql_complete_prefetchw(txring_txq(tx_ring)); +@@ -1130,6 +1138,16 @@ int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + bool failure; + u32 first; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + xdp_prog = READ_ONCE(rx_ring->xdp_prog); + if (xdp_prog) { + xdp_ring = rx_ring->xdp_ring; diff --git a/LINUX/final-patches/vanilla--ice--61100--61200 b/LINUX/final-patches/vanilla--ice--61100--61200 new file mode 100644 index 000000000..ab74a7897 --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--61100--61200 @@ -0,0 +1,133 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index c5da8e9cc0a0..149ede8c074a 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_lib.h" + #include "ice_dcb_lib.h" + #include "ice_sriov.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI +@@ -462,6 +467,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + rxdid = ICE_RXDID_FLEX_NIC_2; + } + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -623,6 +632,11 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + if (ring->vsi->type == ICE_VSI_CTRL) + ice_init_ctrl_rx_descs(ring, num_bufs); + else +@@ -947,6 +961,10 @@ ice_vsi_cfg_txq(struct ice_vsi *vsi, struct ice_tx_ring *ring, + if (pf_q == le16_to_cpu(txq->txq_id)) + ring->txq_teid = le32_to_cpu(txq->q_teid); + ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 77781277aa8e..fb0a37d71402 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -53,6 +53,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + DEFINE_STATIC_KEY_FALSE(ice_xdp_locking_key); + EXPORT_SYMBOL(ice_xdp_locking_key); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -5106,6 +5111,12 @@ static int ice_init(struct ice_pf *pf) + /* since everything is good, start the service timer */ + mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period)); + ++ ice_devlink_register(pf); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_link: +@@ -5452,6 +5463,10 @@ static void ice_remove(struct pci_dev *pdev) + struct ice_pf *pf = pci_get_drvdata(pdev); + int i; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) + break; +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 41e7e29879a3..5cde03b399df 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -22,6 +22,10 @@ + + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -221,6 +225,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + netdev_txq_bql_complete_prefetchw(txring_txq(tx_ring)); +@@ -1261,6 +1269,16 @@ static int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + u32 xdp_xmit = 0; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + xdp_prog = READ_ONCE(rx_ring->xdp_prog); + if (xdp_prog) { + xdp_ring = rx_ring->xdp_ring; diff --git a/LINUX/final-patches/vanilla--ice--61200--61300 b/LINUX/final-patches/vanilla--ice--61200--61300 new file mode 100644 index 000000000..70ee51b6c --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--61200--61300 @@ -0,0 +1,134 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index 2d35a278c555..73ad54aa5ce7 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -6,6 +6,11 @@ + #include "ice_lib.h" + #include "ice_dcb_lib.h" + #include "ice_sriov.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI +@@ -566,6 +571,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + rxdid = ICE_RXDID_FLEX_NIC_2; + } + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -727,6 +736,11 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + if (ring->vsi->type == ICE_VSI_CTRL) + ice_init_ctrl_rx_descs(ring, num_bufs); + else +@@ -1109,6 +1123,11 @@ ice_vsi_cfg_txq(const struct ice_vsi *vsi, struct ice_tx_ring *ring, + goto err_cfg_tstamp; + } + } ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_cfg_tstamp: +diff --git a/ice/ice_main.c b/ice/ice_main.c +index 86f5859e88ef..b48951ac689a 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -54,6 +54,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + DEFINE_STATIC_KEY_FALSE(ice_xdp_locking_key); + EXPORT_SYMBOL(ice_xdp_locking_key); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -5083,6 +5088,12 @@ static int ice_init(struct ice_pf *pf) + /* since everything is good, start the service timer */ + mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period)); + ++ ice_devlink_register(pf); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_link: +@@ -5429,6 +5440,10 @@ static void ice_remove(struct pci_dev *pdev) + struct ice_pf *pf = pci_get_drvdata(pdev); + int i; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) + break; +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index 73f08d02f9c7..962c7d103257 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -22,6 +22,10 @@ + + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -274,6 +278,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + netdev_txq_bql_complete_prefetchw(txring_txq(tx_ring)); +@@ -1391,6 +1399,16 @@ static int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + u32 xdp_xmit = 0; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + xdp_prog = READ_ONCE(rx_ring->xdp_prog); + if (xdp_prog) { + xdp_ring = rx_ring->xdp_ring; diff --git a/LINUX/final-patches/vanilla--ice--61300--99999 b/LINUX/final-patches/vanilla--ice--61300--99999 new file mode 100644 index 000000000..9e2a572ed --- /dev/null +++ b/LINUX/final-patches/vanilla--ice--61300--99999 @@ -0,0 +1,134 @@ +diff --git a/ice/ice_base.c b/ice/ice_base.c +index eadb1e3d12b3..bed56a61e5ce 100644 +--- a/ice/ice_base.c ++++ b/ice/ice_base.c +@@ -7,6 +7,11 @@ + #include "ice_lib.h" + #include "ice_dcb_lib.h" + #include "ice_sriov.h" ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_BASE ++#include ++#endif ++ + + /** + * __ice_vsi_get_qs_contig - Assign a contiguous chunk of queues to VSI +@@ -575,6 +580,10 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) + rxdid = ICE_RXDID_FLEX_NIC_2; + } + ++#ifdef DEV_NETMAP ++ ice_netmap_preconfigure_rx_ring(ring, &rlan_ctx); ++#endif /* DEV_NETMAP */ ++ + /* Enable Flexible Descriptors in the queue context which + * allows this driver to select a specific receive descriptor format + * increasing context priority to pick up profile ID; default is 0x01; +@@ -745,6 +754,11 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring) + return 0; + } + ++#ifdef DEV_NETMAP ++ if (ice_netmap_configure_rx_ring(ring)) ++ return 0; ++#endif /* DEV_NETMAP */ ++ + if (ring->vsi->type == ICE_VSI_CTRL) + ice_init_ctrl_rx_descs(ring, num_bufs); + else +@@ -1127,6 +1141,11 @@ ice_vsi_cfg_txq(const struct ice_vsi *vsi, struct ice_tx_ring *ring, + goto err_cfg_tstamp; + } + } ++ ++#ifdef DEV_NETMAP ++ ice_netmap_configure_tx_ring(ring); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_cfg_tstamp: +diff --git a/ice/ice_main.c b/ice/ice_main.c +index d04605d3e61a..f2398e0119bc 100644 +--- a/ice/ice_main.c ++++ b/ice/ice_main.c +@@ -56,6 +56,11 @@ MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)"); + DEFINE_STATIC_KEY_FALSE(ice_xdp_locking_key); + EXPORT_SYMBOL(ice_xdp_locking_key); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#define NETMAP_ICE_LIB ++#include ++#endif ++ + /** + * ice_hw_to_dev - Get device pointer from the hardware structure + * @hw: pointer to the device HW structure +@@ -5066,6 +5071,12 @@ static int ice_init(struct ice_pf *pf) + /* since everything is good, start the service timer */ + mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period)); + ++ ice_devlink_register(pf); ++ ++#ifdef DEV_NETMAP ++ ice_netmap_attach(pf); ++#endif ++ + return 0; + + err_init_link: +@@ -5421,6 +5432,10 @@ static void ice_remove(struct pci_dev *pdev) + struct ice_pf *pf = pci_get_drvdata(pdev); + int i; + ++#ifdef DEV_NETMAP ++ ice_netmap_detach(pf); ++#endif /* DEV_NETMAP */ ++ + for (i = 0; i < ICE_MAX_RESET_WAIT; i++) { + if (!ice_is_reset_in_progress(pf->state)) + break; +diff --git a/ice/ice_txrx.c b/ice/ice_txrx.c +index ad76768a4232..65baa7edc636 100644 +--- a/ice/ice_txrx.c ++++ b/ice/ice_txrx.c +@@ -24,6 +24,10 @@ + + #define ICE_FDIR_CLEAN_DELAY 10 + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /** + * ice_prgm_fdir_fltr - Program a Flow Director filter + * @vsi: VSI to send dummy packet +@@ -276,6 +280,10 @@ static bool ice_clean_tx_irq(struct ice_tx_ring *tx_ring, int napi_budget) + s16 i = tx_ring->next_to_clean; + struct ice_tx_desc *tx_desc; + struct ice_tx_buf *tx_buf; ++#ifdef DEV_NETMAP ++ if (tx_ring->netdev && netmap_tx_irq(tx_ring->netdev, tx_ring->q_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ + + /* get the bql data ready */ + netdev_txq_bql_complete_prefetchw(txring_txq(tx_ring)); +@@ -953,6 +961,16 @@ static int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget) + u32 xdp_xmit = 0; + bool failure; + ++#ifdef DEV_NETMAP ++ if (rx_ring->netdev) { ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->q_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) { ++ return 1; ++ } ++ } ++#endif /* DEV_NETMAP */ ++ + libeth_xdp_init_buff(xdp, &rx_ring->xdp, &rx_ring->xdp_rxq); + + xdp_prog = READ_ONCE(rx_ring->xdp_prog); diff --git a/LINUX/final-patches/vanilla--igb--20621--20623 b/LINUX/final-patches/vanilla--igb--20621--20623 deleted file mode 100644 index 470149b91..000000000 --- a/LINUX/final-patches/vanilla--igb--20621--20623 +++ /dev/null @@ -1,82 +0,0 @@ -diff --git a/igb/igb_main.c b/igb/igb_main.c -index c881347..a2af379 100644 ---- a/igb/igb_main.c -+++ b/igb/igb_main.c -@@ -226,6 +226,10 @@ char *igb_get_hw_dev_name(struct e1000_hw *hw) - return adapter->netdev->name; - } - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - /** - * igb_get_time_str - format current NIC and system time as string - */ -@@ -1614,6 +1618,10 @@ static int __devinit igb_probe(struct pci_dev *pdev, - /* carrier off reporting is important to ethtool even BEFORE open */ - netif_carrier_off(netdev); - -+#ifdef DEV_NETMAP -+ igb_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ -+ - #ifdef CONFIG_IGB_DCA - if (dca_add_requester(&pdev->dev) == 0) { - adapter->flags |= IGB_FLAG_DCA_ENABLED; -@@ -1699,6 +1707,10 @@ static void __devexit igb_remove(struct pci_dev *pdev) - wr32(E1000_DCA_CTRL, E1000_DCA_CTRL_DCA_MODE_DISABLE); - } - #endif -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - - /* Release control of h/w to f/w. If f/w is AMT enabled, this - * would have already happened in close and is redundant. */ -@@ -2196,6 +2208,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, - - txdctl |= E1000_TXDCTL_QUEUE_ENABLE; - wr32(E1000_TXDCTL(reg_idx), txdctl); -+#ifdef DEV_NETMAP -+ igb_netmap_configure_tx_ring(adapter, reg_idx); -+#endif /* DEV_NETMAP */ - } - - /** -@@ -4905,6 +4920,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) - unsigned int i, eop, count = 0; - bool cleaned = false; - -+#ifdef DEV_NETMAP -+ if (netmap_tx_irq(netdev, tx_ring->queue_index)) -+ return 1; /* cleaned ok */ -+#endif /* DEV_NETMAP */ -+ - i = tx_ring->next_to_clean; - eop = tx_ring->buffer_info[i].next_to_watch; - eop_desc = E1000_TX_DESC_ADV(*tx_ring, eop); -@@ -5109,6 +5129,11 @@ static bool igb_clean_rx_irq_adv(struct igb_q_vector *q_vector, - u16 length; - u16 vlan_tag; - -+#ifdef DEV_NETMAP -+ if (netmap_rx_irq(netdev, rx_ring->queue_index, work_done)) -+ return 1; -+#endif /* DEV_NETMAP */ -+ - i = rx_ring->next_to_clean; - buffer_info = &rx_ring->buffer_info[i]; - rx_desc = E1000_RX_DESC_ADV(*rx_ring, i); -@@ -5236,6 +5261,10 @@ void igb_alloc_rx_buffers_adv(struct igb_ring *rx_ring, int cleaned_count) - unsigned int i; - int bufsz; - -+#ifdef DEV_NETMAP -+ if (igb_netmap_configure_rx_ring(rx_ring)) -+ return; -+#endif /* DEV_NETMAP */ - i = rx_ring->next_to_use; - buffer_info = &rx_ring->buffer_info[i]; - diff --git a/LINUX/final-patches/vanilla--igb--20623--30200 b/LINUX/final-patches/vanilla--igb--20623--30200 deleted file mode 100644 index 7708b0fff..000000000 --- a/LINUX/final-patches/vanilla--igb--20623--30200 +++ /dev/null @@ -1,82 +0,0 @@ -diff --git a/igb/igb_main.c b/igb/igb_main.c -index cea37e0..81fd28b 100644 ---- a/igb/igb_main.c -+++ b/igb/igb_main.c -@@ -201,6 +201,10 @@ MODULE_DESCRIPTION("Intel(R) Gigabit Ethernet Network Driver"); - MODULE_LICENSE("GPL"); - MODULE_VERSION(DRV_VERSION); - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - struct igb_reg_info { - u32 ofs; - char *name; -@@ -1963,6 +1967,10 @@ static int __devinit igb_probe(struct pci_dev *pdev, - /* carrier off reporting is important to ethtool even BEFORE open */ - netif_carrier_off(netdev); - -+#ifdef DEV_NETMAP -+ igb_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ -+ - #ifdef CONFIG_IGB_DCA - if (dca_add_requester(&pdev->dev) == 0) { - adapter->flags |= IGB_FLAG_DCA_ENABLED; -@@ -2072,6 +2080,10 @@ static void __devexit igb_remove(struct pci_dev *pdev) - dev_info(&pdev->dev, "IOV Disabled\n"); - } - #endif -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - - iounmap(hw->hw_addr); - if (hw->flash_address) -@@ -2545,6 +2557,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, - - txdctl |= E1000_TXDCTL_QUEUE_ENABLE; - wr32(E1000_TXDCTL(reg_idx), txdctl); -+#ifdef DEV_NETMAP -+ igb_netmap_configure_tx_ring(adapter, reg_idx); -+#endif /* DEV_NETMAP */ - } - - /** -@@ -5338,6 +5353,11 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) - unsigned int i, eop, count = 0; - bool cleaned = false; - -+#ifdef DEV_NETMAP -+ if (netmap_tx_irq(netdev, tx_ring->queue_index)) -+ return 1; /* cleaned ok */ -+#endif /* DEV_NETMAP */ -+ - i = tx_ring->next_to_clean; - eop = tx_ring->buffer_info[i].next_to_watch; - eop_desc = E1000_TX_DESC_ADV(*tx_ring, eop); -@@ -5540,6 +5560,11 @@ static bool igb_clean_rx_irq_adv(struct igb_q_vector *q_vector, - u16 length; - u16 vlan_tag; - -+#ifdef DEV_NETMAP -+ if (netmap_rx_irq(netdev, rx_ring->queue_index, work_done)) -+ return 1; -+#endif /* DEV_NETMAP */ -+ - i = rx_ring->next_to_clean; - buffer_info = &rx_ring->buffer_info[i]; - rx_desc = E1000_RX_DESC_ADV(*rx_ring, i); -@@ -5668,6 +5693,10 @@ void igb_alloc_rx_buffers_adv(struct igb_ring *rx_ring, int cleaned_count) - unsigned int i; - int bufsz; - -+#ifdef DEV_NETMAP -+ if (igb_netmap_configure_rx_ring(rx_ring)) -+ return; -+#endif /* DEV_NETMAP */ - i = rx_ring->next_to_use; - buffer_info = &rx_ring->buffer_info[i]; - diff --git a/LINUX/final-patches/vanilla--igb--30200--30800 b/LINUX/final-patches/vanilla--igb--30200--30800 deleted file mode 100644 index 4043728f5..000000000 --- a/LINUX/final-patches/vanilla--igb--30200--30800 +++ /dev/null @@ -1,103 +0,0 @@ -diff --git a/igb/igb_main.c b/igb/igb_main.c -index ced5444..43c2419 100644 ---- a/igb/igb_main.c -+++ b/igb/igb_main.c -@@ -225,6 +225,10 @@ MODULE_DESCRIPTION("Intel(R) Gigabit Ethernet Network Driver"); - MODULE_LICENSE("GPL"); - MODULE_VERSION(DRV_VERSION); - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - struct igb_reg_info { - u32 ofs; - char *name; -@@ -2073,6 +2077,10 @@ static int __devinit igb_probe(struct pci_dev *pdev, - /* carrier off reporting is important to ethtool even BEFORE open */ - netif_carrier_off(netdev); - -+#ifdef DEV_NETMAP -+ igb_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ -+ - #ifdef CONFIG_IGB_DCA - if (dca_add_requester(&pdev->dev) == 0) { - adapter->flags |= IGB_FLAG_DCA_ENABLED; -@@ -2199,6 +2207,10 @@ static void __devexit igb_remove(struct pci_dev *pdev) - dev_info(&pdev->dev, "IOV Disabled\n"); - } - #endif -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - - iounmap(hw->hw_addr); - if (hw->flash_address) -@@ -2711,6 +2723,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, - - txdctl |= E1000_TXDCTL_QUEUE_ENABLE; - wr32(E1000_TXDCTL(reg_idx), txdctl); -+#ifdef DEV_NETMAP -+ igb_netmap_configure_tx_ring(adapter, reg_idx); -+#endif /* DEV_NETMAP */ - } - - /** -@@ -3088,6 +3103,19 @@ void igb_configure_rx_ring(struct igb_adapter *adapter, - /* Only set Drop Enable if we are supporting multiple queues */ - if (adapter->vfs_allocated_count || adapter->num_rx_queues > 1) - srrctl |= E1000_SRRCTL_DROP_EN; -+#ifdef DEV_NETMAP -+ { -+ /* The driver uses split buffers, which are not -+ * supported in native netmap mode */ -+ struct ifnet *ifp = adapter->netdev; -+ struct netmap_adapter *na = NA(ifp); -+ if (nm_native_on(na)) { -+ srrctl &= ~(7 << 25); /* clear descriptor type */ -+ srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF; -+ /* XXX we should set tail here */ -+ } -+ } -+#endif - - wr32(E1000_SRRCTL(reg_idx), srrctl); - -@@ -5705,6 +5733,10 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) - - if (test_bit(__IGB_DOWN, &adapter->state)) - return true; -+#ifdef DEV_NETMAP -+ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) -+ return 1; /* cleaned ok */ -+#endif /* DEV_NETMAP */ - - tx_buffer = &tx_ring->tx_buffer_info[i]; - tx_desc = IGB_TX_DESC(tx_ring, i); -@@ -5980,6 +6012,12 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, int budget) - u16 cleaned_count = igb_desc_unused(rx_ring); - u16 i = rx_ring->next_to_clean; - -+#ifdef DEV_NETMAP -+ int dummy = 1; // select rx irq handling -+ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy)) -+ return 1; -+#endif /* DEV_NETMAP */ -+ - rx_desc = IGB_RX_DESC(rx_ring, i); - - while (igb_test_staterr(rx_desc, E1000_RXD_STAT_DD)) { -@@ -6170,6 +6208,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) - struct igb_rx_buffer *bi; - u16 i = rx_ring->next_to_use; - -+#ifdef DEV_NETMAP -+ if (igb_netmap_configure_rx_ring(rx_ring)) -+ return; -+#endif /* DEV_NETMAP */ -+ - rx_desc = IGB_RX_DESC(rx_ring, i); - bi = &rx_ring->rx_buffer_info[i]; - i -= rx_ring->count; diff --git a/LINUX/final-patches/vanilla--igb--30f00--40100 b/LINUX/final-patches/vanilla--igb--40000--40100 similarity index 78% rename from LINUX/final-patches/vanilla--igb--30f00--40100 rename to LINUX/final-patches/vanilla--igb--40000--40100 index 8f7a2004a..ef9eb8384 100644 --- a/LINUX/final-patches/vanilla--igb--30f00--40100 +++ b/LINUX/final-patches/vanilla--igb--40000--40100 @@ -1,8 +1,8 @@ diff --git a/igb/igb_main.c b/igb/igb_main.c -index 16430a8..c2c4622 100644 +index f366b3b96d03..f7ee2c4c5eaf 100644 --- a/igb/igb_main.c +++ b/igb/igb_main.c -@@ -257,6 +257,10 @@ static int debug = -1; +@@ -251,6 +251,10 @@ static int debug = -1; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); @@ -13,15 +13,15 @@ index 16430a8..c2c4622 100644 struct igb_reg_info { u32 ofs; char *name; -@@ -1798,7 +1802,6 @@ void igb_down(struct igb_adapter *adapter) - napi_disable(&(adapter->q_vector[i]->napi)); +@@ -1797,7 +1801,6 @@ void igb_down(struct igb_adapter *adapter) + } } - del_timer_sync(&adapter->watchdog_timer); del_timer_sync(&adapter->phy_info_timer); -@@ -2540,6 +2543,10 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +@@ -2546,6 +2549,10 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* carrier off reporting is important to ethtool even BEFORE open */ netif_carrier_off(netdev); @@ -32,7 +32,7 @@ index 16430a8..c2c4622 100644 #ifdef CONFIG_IGB_DCA if (dca_add_requester(&pdev->dev) == 0) { adapter->flags |= IGB_FLAG_DCA_ENABLED; -@@ -2805,6 +2812,10 @@ static void igb_remove(struct pci_dev *pdev) +@@ -2811,6 +2818,10 @@ static void igb_remove(struct pci_dev *pdev) wr32(E1000_DCA_CTRL, E1000_DCA_CTRL_DCA_MODE_DISABLE); } #endif @@ -43,7 +43,7 @@ index 16430a8..c2c4622 100644 /* Release control of h/w to f/w. If f/w is AMT enabled, this * would have already happened in close and is redundant. -@@ -3276,6 +3287,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, +@@ -3283,6 +3294,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, txdctl |= E1000_TXDCTL_QUEUE_ENABLE; wr32(E1000_TXDCTL(reg_idx), txdctl); @@ -53,7 +53,7 @@ index 16430a8..c2c4622 100644 } /** -@@ -6321,6 +6335,10 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) +@@ -6399,6 +6413,10 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) if (test_bit(__IGB_DOWN, &adapter->state)) return true; @@ -64,7 +64,7 @@ index 16430a8..c2c4622 100644 tx_buffer = &tx_ring->tx_buffer_info[i]; tx_desc = IGB_TX_DESC(tx_ring, i); -@@ -6984,6 +7002,10 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) +@@ -6950,6 +6968,10 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) unsigned int total_bytes = 0, total_packets = 0; u16 cleaned_count = igb_desc_unused(rx_ring); @@ -75,7 +75,7 @@ index 16430a8..c2c4622 100644 while (likely(total_packets < budget)) { union e1000_adv_rx_desc *rx_desc; -@@ -7101,6 +7123,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) +@@ -7067,6 +7089,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) struct igb_rx_buffer *bi; u16 i = rx_ring->next_to_use; diff --git a/LINUX/final-patches/vanilla--igb--40100--99999 b/LINUX/final-patches/vanilla--igb--40100--40400 similarity index 98% rename from LINUX/final-patches/vanilla--igb--40100--99999 rename to LINUX/final-patches/vanilla--igb--40100--40400 index dfee4bdf4..00a867770 100644 --- a/LINUX/final-patches/vanilla--igb--40100--99999 +++ b/LINUX/final-patches/vanilla--igb--40100--40400 @@ -1,5 +1,5 @@ diff --git a/igb/igb_main.c b/igb/igb_main.c -index a0a9b1f..85be1eb 100644 +index a0a9b1fcb5e8..85be1ebd02ab 100644 --- a/igb/igb_main.c +++ b/igb/igb_main.c @@ -251,6 +251,10 @@ static int debug = -1; diff --git a/LINUX/final-patches/vanilla--igb--30800--30f00 b/LINUX/final-patches/vanilla--igb--40400--50a00 similarity index 65% rename from LINUX/final-patches/vanilla--igb--30800--30f00 rename to LINUX/final-patches/vanilla--igb--40400--50a00 index 1e3643441..5d5ea3430 100644 --- a/LINUX/final-patches/vanilla--igb--30800--30f00 +++ b/LINUX/final-patches/vanilla--igb--40400--50a00 @@ -1,8 +1,8 @@ diff --git a/igb/igb_main.c b/igb/igb_main.c -index 31cfe2e..2776ed4 100644 +index ea7b09887245..ddb376efc198 100644 --- a/igb/igb_main.c +++ b/igb/igb_main.c -@@ -247,6 +247,10 @@ static int debug = -1; +@@ -253,6 +253,10 @@ static int debug = -1; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); @@ -13,7 +13,7 @@ index 31cfe2e..2776ed4 100644 struct igb_reg_info { u32 ofs; char *name; -@@ -2127,6 +2131,10 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +@@ -2540,6 +2544,10 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* carrier off reporting is important to ethtool even BEFORE open */ netif_carrier_off(netdev); @@ -24,7 +24,7 @@ index 31cfe2e..2776ed4 100644 #ifdef CONFIG_IGB_DCA if (dca_add_requester(&pdev->dev) == 0) { adapter->flags |= IGB_FLAG_DCA_ENABLED; -@@ -2233,6 +2241,10 @@ static void igb_remove(struct pci_dev *pdev) +@@ -2809,6 +2817,10 @@ static void igb_remove(struct pci_dev *pdev) wr32(E1000_DCA_CTRL, E1000_DCA_CTRL_DCA_MODE_DISABLE); } #endif @@ -34,8 +34,8 @@ index 31cfe2e..2776ed4 100644 + /* Release control of h/w to f/w. If f/w is AMT enabled, this - * would have already happened in close and is redundant. */ -@@ -2746,6 +2758,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + * would have already happened in close and is redundant. +@@ -3292,6 +3304,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, txdctl |= E1000_TXDCTL_QUEUE_ENABLE; wr32(E1000_TXDCTL(reg_idx), txdctl); @@ -45,29 +45,34 @@ index 31cfe2e..2776ed4 100644 } /** -@@ -5690,6 +5705,10 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) +@@ -6409,6 +6424,10 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) if (test_bit(__IGB_DOWN, &adapter->state)) return true; +#ifdef DEV_NETMAP -+ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) -+ return 1; /* cleaned ok */ ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; /* cleaned ok */ +#endif /* DEV_NETMAP */ tx_buffer = &tx_ring->tx_buffer_info[i]; tx_desc = IGB_TX_DESC(tx_ring, i); -@@ -6349,6 +6368,10 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) +@@ -6916,6 +6935,15 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) unsigned int total_bytes = 0, total_packets = 0; u16 cleaned_count = igb_desc_unused(rx_ring); +#ifdef DEV_NETMAP -+ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) -+ return true; ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; +#endif /* DEV_NETMAP */ - do { + while (likely(total_packets < budget)) { union e1000_adv_rx_desc *rx_desc; -@@ -6461,6 +6484,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) +@@ -7033,6 +7061,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) struct igb_rx_buffer *bi; u16 i = rx_ring->next_to_use; diff --git a/LINUX/final-patches/vanilla--igb--50a00--50c00 b/LINUX/final-patches/vanilla--igb--50a00--50c00 new file mode 100644 index 000000000..8e6c06e80 --- /dev/null +++ b/LINUX/final-patches/vanilla--igb--50a00--50c00 @@ -0,0 +1,87 @@ +diff --git a/igb/igb_main.c b/igb/igb_main.c +index 0d343d050973..ddee5da89ba3 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -241,6 +241,10 @@ static int debug = -1; + module_param(debug, int, 0); + MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + struct igb_reg_info { + u32 ofs; + char *name; +@@ -3489,6 +3493,10 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef CONFIG_IGB_DCA + if (dca_add_requester(&pdev->dev) == 0) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3798,6 +3806,10 @@ static void igb_remove(struct pci_dev *pdev) + wr32(E1000_DCA_CTRL, E1000_DCA_CTRL_DCA_MODE_DISABLE); + } + #endif ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + + /* Release control of h/w to f/w. If f/w is AMT enabled, this + * would have already happened in close and is redundant. +@@ -4302,6 +4314,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + wr32(E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -8043,6 +8058,10 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector, int napi_budget) + + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ + + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); +@@ -8682,6 +8701,16 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.rxq = &rx_ring->xdp_rxq; + + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ +@@ -8861,6 +8890,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + u16 i = rx_ring->next_to_use; + u16 bufsz; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/vanilla--igb--50c00--99999 b/LINUX/final-patches/vanilla--igb--50c00--99999 new file mode 100644 index 000000000..057b27dc6 --- /dev/null +++ b/LINUX/final-patches/vanilla--igb--50c00--99999 @@ -0,0 +1,87 @@ +diff --git a/igb/igb_main.c b/igb/igb_main.c +index a45cd2b416c8..8a7712bb2a5d 100644 +--- a/igb/igb_main.c ++++ b/igb/igb_main.c +@@ -241,6 +241,10 @@ static int debug = -1; + module_param(debug, int, 0); + MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + struct igb_reg_info { + u32 ofs; + char *name; +@@ -3489,6 +3493,10 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + ++#ifdef DEV_NETMAP ++ igb_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + #ifdef CONFIG_IGB_DCA + if (dca_add_requester(&pdev->dev) == 0) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; +@@ -3798,6 +3806,10 @@ static void igb_remove(struct pci_dev *pdev) + wr32(E1000_DCA_CTRL, E1000_DCA_CTRL_DCA_MODE_DISABLE); + } + #endif ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + + /* Release control of h/w to f/w. If f/w is AMT enabled, this + * would have already happened in close and is redundant. +@@ -4302,6 +4314,9 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, + + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; + wr32(E1000_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igb_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -8030,6 +8045,10 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector, int napi_budget) + + if (test_bit(__IGB_DOWN, &adapter->state)) + return true; ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ + + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGB_TX_DESC(tx_ring, i); +@@ -8674,6 +8693,16 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) + u32 frame_sz = 0; + int rx_buf_pgcnt; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ + #if (PAGE_SIZE < 8192) + frame_sz = igb_rx_frame_truesize(rx_ring, 0); +@@ -8852,6 +8881,11 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) + u16 i = rx_ring->next_to_use; + u16 bufsz; + ++#ifdef DEV_NETMAP ++ if (igb_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; diff --git a/LINUX/final-patches/vanilla--igc--41400--50100 b/LINUX/final-patches/vanilla--igc--41400--50100 new file mode 100644 index 000000000..ec876c0a6 --- /dev/null +++ b/LINUX/final-patches/vanilla--igc--41400--50100 @@ -0,0 +1,86 @@ +diff --git a/igc/igc_main.c b/igc/igc_main.c +index 9d85707e8a81..45868726cc24 100644 +--- a/igc/igc_main.c ++++ b/igc/igc_main.c +@@ -58,6 +58,12 @@ static void igc_irq_enable(struct igc_adapter *adapter); + static void igc_configure_msix(struct igc_adapter *adapter); + static bool igc_alloc_mapped_page(struct igc_ring *rx_ring, + struct igc_rx_buffer *bi); ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++static void igc_up(struct igc_adapter *adapter); ++static void igc_down(struct igc_adapter *adapter); ++static void igc_reset(struct igc_adapter *adapter); ++#include ++#endif + + enum latency_range { + lowest_latency = 0, +@@ -596,6 +602,9 @@ static void igc_configure_tx_ring(struct igc_adapter *adapter, + + txdctl |= IGC_TXDCTL_QUEUE_ENABLE; + wr32(IGC_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igc_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -1333,6 +1342,11 @@ static void igc_alloc_rx_buffers(struct igc_ring *rx_ring, u16 cleaned_count) + struct igc_rx_buffer *bi; + u16 bufsz; + ++#ifdef DEV_NETMAP ++ if (igc_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; +@@ -1398,6 +1412,11 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget) + struct sk_buff *skb = rx_ring->skb; + u16 cleaned_count = igc_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_packets < budget)) { + union igc_adv_rx_desc *rx_desc; + struct igc_rx_buffer *rx_buffer; +@@ -1548,6 +1567,11 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + if (test_bit(__IGC_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGC_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -3687,6 +3711,10 @@ static int igc_probe(struct pci_dev *pdev, + /* Check if Media Autosense is enabled */ + adapter->ei = *ei; + ++#ifdef DEV_NETMAP ++ igc_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* print pcie link status and MAC address */ + pcie_print_link_status(pdev); + netdev_info(netdev, "MAC: %pM\n", netdev->dev_addr); +@@ -3738,6 +3766,11 @@ static void igc_remove(struct pci_dev *pdev) + * would have already happened in close and is redundant. + */ + igc_release_hw_control(adapter); ++ ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igc_clear_interrupt_scheme(adapter); diff --git a/LINUX/final-patches/vanilla--igc--50100--50600 b/LINUX/final-patches/vanilla--igc--50100--50600 new file mode 100644 index 000000000..b5a474873 --- /dev/null +++ b/LINUX/final-patches/vanilla--igc--50100--50600 @@ -0,0 +1,83 @@ +diff --git a/igc/igc_main.c b/igc/igc_main.c +index 87a11879bf2d..8da49a957b29 100644 +--- a/igc/igc_main.c ++++ b/igc/igc_main.c +@@ -60,6 +60,9 @@ static void igc_irq_enable(struct igc_adapter *adapter); + static void igc_configure_msix(struct igc_adapter *adapter); + static bool igc_alloc_mapped_page(struct igc_ring *rx_ring, + struct igc_rx_buffer *bi); ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif + + enum latency_range { + lowest_latency = 0, +@@ -598,6 +601,9 @@ static void igc_configure_tx_ring(struct igc_adapter *adapter, + + txdctl |= IGC_TXDCTL_QUEUE_ENABLE; + wr32(IGC_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igc_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -1335,6 +1341,11 @@ static void igc_alloc_rx_buffers(struct igc_ring *rx_ring, u16 cleaned_count) + struct igc_rx_buffer *bi; + u16 bufsz; + ++#ifdef DEV_NETMAP ++ if (igc_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; +@@ -1400,6 +1411,11 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget) + struct sk_buff *skb = rx_ring->skb; + u16 cleaned_count = igc_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_packets < budget)) { + union igc_adv_rx_desc *rx_desc; + struct igc_rx_buffer *rx_buffer; +@@ -1550,6 +1566,11 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + if (test_bit(__IGC_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGC_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -3723,6 +3744,10 @@ static int igc_probe(struct pci_dev *pdev, + /* Check if Media Autosense is enabled */ + adapter->ei = *ei; + ++#ifdef DEV_NETMAP ++ igc_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* print pcie link status and MAC address */ + pcie_print_link_status(pdev); + netdev_info(netdev, "MAC: %pM\n", netdev->dev_addr); +@@ -3774,6 +3799,11 @@ static void igc_remove(struct pci_dev *pdev) + * would have already happened in close and is redundant. + */ + igc_release_hw_control(adapter); ++ ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igc_clear_interrupt_scheme(adapter); diff --git a/LINUX/final-patches/vanilla--igc--50600--61300 b/LINUX/final-patches/vanilla--igc--50600--61300 new file mode 100644 index 000000000..077201a89 --- /dev/null +++ b/LINUX/final-patches/vanilla--igc--50600--61300 @@ -0,0 +1,84 @@ +diff --git a/igc/igc_main.c b/igc/igc_main.c +index d9d5425fe8d9..7d9f5b80f28b 100644 +--- a/igc/igc_main.c ++++ b/igc/igc_main.c +@@ -52,6 +52,10 @@ static const struct pci_device_id igc_pci_tbl[] = { + + MODULE_DEVICE_TABLE(pci, igc_pci_tbl); + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + enum latency_range { + lowest_latency = 0, + low_latency = 1, +@@ -612,6 +616,9 @@ static void igc_configure_tx_ring(struct igc_adapter *adapter, + + txdctl |= IGC_TXDCTL_QUEUE_ENABLE; + wr32(IGC_TXDCTL(reg_idx), txdctl); ++#ifdef DEV_NETMAP ++ igc_netmap_configure_tx_ring(adapter, reg_idx); ++#endif /* DEV_NETMAP */ + } + + /** +@@ -1801,6 +1808,11 @@ static void igc_alloc_rx_buffers(struct igc_ring *rx_ring, u16 cleaned_count) + struct igc_rx_buffer *bi; + u16 bufsz; + ++#ifdef DEV_NETMAP ++ if (igc_netmap_configure_rx_ring(rx_ring)) ++ return; ++#endif /* DEV_NETMAP */ ++ + /* nothing to do */ + if (!cleaned_count) + return; +@@ -1866,6 +1878,11 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget) + struct sk_buff *skb = rx_ring->skb; + u16 cleaned_count = igc_desc_unused(rx_ring); + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &total_packets)) ++ return true; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_packets < budget)) { + union igc_adv_rx_desc *rx_desc; + struct igc_rx_buffer *rx_buffer; +@@ -1970,6 +1987,11 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + if (test_bit(__IGC_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(tx_ring->netdev, tx_ring->queue_index)) ++ return true; /* cleaned ok */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IGC_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -4788,6 +4810,10 @@ static int igc_probe(struct pci_dev *pdev, + /* Check if Media Autosense is enabled */ + adapter->ei = *ei; + ++#ifdef DEV_NETMAP ++ igc_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + /* print pcie link status and MAC address */ + pcie_print_link_status(pdev); + netdev_info(netdev, "MAC: %pM\n", netdev->dev_addr); +@@ -4840,6 +4866,11 @@ static void igc_remove(struct pci_dev *pdev) + * would have already happened in close and is redundant. + */ + igc_release_hw_control(adapter); ++ ++#ifdef DEV_NETMAP ++ netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + unregister_netdev(netdev); + + igc_clear_interrupt_scheme(adapter); diff --git a/LINUX/final-patches/vanilla--ixgbe--20620--20622 b/LINUX/final-patches/vanilla--ixgbe--20620--20622 deleted file mode 100644 index ceb0341fe..000000000 --- a/LINUX/final-patches/vanilla--ixgbe--20620--20622 +++ /dev/null @@ -1,119 +0,0 @@ -diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index a456578..eec58c8 100644 ---- a/ixgbe/ixgbe_main.c -+++ b/ixgbe/ixgbe_main.c -@@ -337,6 +337,16 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, - unsigned int i, eop, count = 0; - unsigned int total_bytes = 0, total_packets = 0; - -+#ifdef DEV_NETMAP -+ /* -+ * In netmap mode, all the work is done in the context -+ * of the client thread. Interrupt handlers only wake up -+ * clients, which may be sleeping on individual rings -+ * or on a global resource for all rings. -+ */ -+ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) -+ return 1; /* seems to be ignored */ -+#endif /* DEV_NETMAP */ - i = tx_ring->next_to_clean; - eop = tx_ring->tx_buffer_info[i].next_to_watch; - eop_desc = IXGBE_TX_DESC_ADV(*tx_ring, eop); -@@ -778,6 +788,18 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - int ddp_bytes = 0; - #endif /* IXGBE_FCOE */ - -+#ifdef DEV_NETMAP -+ int nm_irq; -+ /* -+ * Same as the txeof routine: only wakeup clients on intr. -+ */ -+ nm_irq = netmap_rx_irq(adapter->netdev, rx_ring->queue_index, work_done); -+ if (nm_irq != NM_IRQ_PASS) { -+ *work_done = (nm_irq == NM_IRQ_RESCHED) ? work_to_do : 1; -+ return true; -+ } -+#endif /* DEV_NETMAP */ -+ - i = rx_ring->next_to_clean; - rx_desc = IXGBE_RX_DESC_ADV(*rx_ring, i); - staterr = le32_to_cpu(rx_desc->wb.upper.status_error); -@@ -1008,6 +1030,24 @@ static void ixgbe_configure_msix(struct ixgbe_adapter *adapter) - IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIAC, mask); - } - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+/* -+ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to -+ * be a reference on how to implement netmap support in a driver. -+ * Additional comments are in ixgbe_netmap_linux.h . -+ * -+ * The code is originally developed on FreeBSD and in the interest -+ * of maintainability we try to limit differences between the two systems. -+ * -+ * contains functions for netmap support -+ * that extend the standard driver. -+ * It also defines DEV_NETMAP so further conditional sections use -+ * that instead of CONFIG_NETMAP -+ */ -+#include -+#endif -+ -+ - enum latency_range { - lowest_latency = 0, - low_latency = 1, -@@ -1044,7 +1084,6 @@ static u8 ixgbe_update_itr(struct ixgbe_adapter *adapter, - if (packets == 0) - goto update_itr_done; - -- - /* simple throttlerate management - * 0-20MB/s lowest (100000 ints/s) - * 20-100MB/s low (20000 ints/s) -@@ -2568,6 +2607,12 @@ static void ixgbe_configure(struct ixgbe_adapter *adapter) - - ixgbe_configure_tx(adapter); - ixgbe_configure_rx(adapter); -+#ifdef DEV_NETMAP -+ for (i = 0; i < adapter->num_rx_queues; i++) -+ ixgbe_netmap_configure_rx_ring(adapter, -+ adapter->rx_ring[i].reg_idx); -+ return; -+#endif /* DEV_NETMAP */ - for (i = 0; i < adapter->num_rx_queues; i++) - ixgbe_alloc_rx_buffers(adapter, &adapter->rx_ring[i], - (adapter->rx_ring[i].count - 1)); -@@ -2753,6 +2798,11 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter) - txdctl = IXGBE_READ_REG(hw, IXGBE_TXDCTL(j)); - txdctl |= IXGBE_TXDCTL_ENABLE; - IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(j), txdctl); -+ -+#ifdef DEV_NETMAP // XXX i and j are the same ? -+ ixgbe_netmap_configure_tx_ring(adapter, j); -+#endif /* DEV_NETMAP */ -+ - } - - for (i = 0; i < num_rx_rings; i++) { -@@ -4290,6 +4340,9 @@ static int ixgbe_open(struct net_device *netdev) - goto err_up; - - netif_tx_start_all_queues(netdev); -+#ifdef DEV_NETMAP -+ ixgbe_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ - - return 0; - -@@ -5893,6 +5946,10 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) - struct net_device *netdev = pci_get_drvdata(pdev); - struct ixgbe_adapter *adapter = netdev_priv(netdev); - -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - set_bit(__IXGBE_DOWN, &adapter->state); - /* clear the module not found bit to make sure the worker won't - * reschedule diff --git a/LINUX/final-patches/vanilla--ixgbe--20622--20623 b/LINUX/final-patches/vanilla--ixgbe--20622--20623 deleted file mode 100644 index 12ec0b3c4..000000000 --- a/LINUX/final-patches/vanilla--ixgbe--20622--20623 +++ /dev/null @@ -1,118 +0,0 @@ -diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index 6c00ee4..50d9f5e 100644 ---- a/ixgbe/ixgbe_main.c -+++ b/ixgbe/ixgbe_main.c -@@ -400,6 +400,16 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, - unsigned int i, eop, count = 0; - unsigned int total_bytes = 0, total_packets = 0; - -+#ifdef DEV_NETMAP -+ /* -+ * In netmap mode, all the work is done in the context -+ * of the client thread. Interrupt handlers only wake up -+ * clients, which may be sleeping on individual rings -+ * or on a global resource for all rings. -+ */ -+ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) -+ return 1; /* seems to be ignored */ -+#endif /* DEV_NETMAP */ - i = tx_ring->next_to_clean; - eop = tx_ring->tx_buffer_info[i].next_to_watch; - eop_desc = IXGBE_TX_DESC_ADV(*tx_ring, eop); -@@ -845,6 +855,18 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - int ddp_bytes = 0; - #endif /* IXGBE_FCOE */ - -+#ifdef DEV_NETMAP -+ int nm_irq; -+ /* -+ * Same as the txeof routine: only wakeup clients on intr. -+ */ -+ nm_irq = netmap_rx_irq(adapter->netdev, rx_ring->queue_index, work_done); -+ if (nm_irq != NM_IRQ_PASS) { -+ *work_done = (nm_irq == NM_IRQ_RESCHED) ? work_to_do : 1; -+ return true; -+ } -+#endif /* DEV_NETMAP */ -+ - i = rx_ring->next_to_clean; - rx_desc = IXGBE_RX_DESC_ADV(*rx_ring, i); - staterr = le32_to_cpu(rx_desc->wb.upper.status_error); -@@ -1103,6 +1125,24 @@ static void ixgbe_configure_msix(struct ixgbe_adapter *adapter) - IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIAC, mask); - } - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+/* -+ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to -+ * be a reference on how to implement netmap support in a driver. -+ * Additional comments are in ixgbe_netmap_linux.h . -+ * -+ * The code is originally developed on FreeBSD and in the interest -+ * of maintainability we try to limit differences between the two systems. -+ * -+ * contains functions for netmap support -+ * that extend the standard driver. -+ * It also defines DEV_NETMAP so further conditional sections use -+ * that instead of CONFIG_NETMAP -+ */ -+#include -+#endif -+ -+ - enum latency_range { - lowest_latency = 0, - low_latency = 1, -@@ -1139,7 +1179,6 @@ static u8 ixgbe_update_itr(struct ixgbe_adapter *adapter, - if (packets == 0) - goto update_itr_done; - -- - /* simple throttlerate management - * 0-20MB/s lowest (100000 ints/s) - * 20-100MB/s low (20000 ints/s) -@@ -2738,6 +2777,12 @@ static void ixgbe_configure(struct ixgbe_adapter *adapter) - - ixgbe_configure_tx(adapter); - ixgbe_configure_rx(adapter); -+#ifdef DEV_NETMAP -+ for (i = 0; i < adapter->num_rx_queues; i++) -+ ixgbe_netmap_configure_rx_ring(adapter, -+ adapter->rx_ring[i]->reg_idx); -+ return; -+#endif /* DEV_NETMAP */ - for (i = 0; i < adapter->num_rx_queues; i++) - ixgbe_alloc_rx_buffers(adapter, adapter->rx_ring[i], - (adapter->rx_ring[i]->count - 1)); -@@ -2955,6 +3000,10 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter) - DPRINTK(DRV, ERR, "Could not enable " - "Tx Queue %d\n", j); - } -+#ifdef DEV_NETMAP // XXX i and j are the same ? -+ ixgbe_netmap_configure_tx_ring(adapter, j); -+#endif /* DEV_NETMAP */ -+ - } - - for (i = 0; i < num_rx_rings; i++) { -@@ -4641,6 +4690,9 @@ static int ixgbe_open(struct net_device *netdev) - goto err_up; - - netif_tx_start_all_queues(netdev); -+#ifdef DEV_NETMAP -+ ixgbe_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ - - return 0; - -@@ -6402,6 +6454,10 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) - struct net_device *netdev = pci_get_drvdata(pdev); - struct ixgbe_adapter *adapter = netdev_priv(netdev); - -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - set_bit(__IXGBE_DOWN, &adapter->state); - /* clear the module not found bit to make sure the worker won't - * reschedule diff --git a/LINUX/final-patches/vanilla--ixgbe--20623--20625 b/LINUX/final-patches/vanilla--ixgbe--20623--20625 deleted file mode 100644 index cb610477b..000000000 --- a/LINUX/final-patches/vanilla--ixgbe--20623--20625 +++ /dev/null @@ -1,109 +0,0 @@ -diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index 74d9b6d..d2a6b08 100644 ---- a/ixgbe/ixgbe_main.c -+++ b/ixgbe/ixgbe_main.c -@@ -214,6 +214,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { - {} - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+/* -+ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to -+ * be a reference on how to implement netmap support in a driver. -+ * Additional comments are in ixgbe_netmap_linux.h . -+ * -+ * The code is originally developed on FreeBSD and in the interest -+ * of maintainability we try to limit differences between the two systems. -+ * -+ * contains functions for netmap support -+ * that extend the standard driver. -+ * It also defines DEV_NETMAP so further conditional sections use -+ * that instead of CONFIG_NETMAP -+ */ -+#include -+#endif - - /* - * ixgbe_regdump - register printout routine -@@ -741,6 +757,16 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, - unsigned int i, eop, count = 0; - unsigned int total_bytes = 0, total_packets = 0; - -+#ifdef DEV_NETMAP -+ /* -+ * In netmap mode, all the work is done in the context -+ * of the client thread. Interrupt handlers only wake up -+ * clients, which may be sleeping on individual rings -+ * or on a global resource for all rings. -+ */ -+ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) -+ return 1; /* seems to be ignored */ -+#endif /* DEV_NETMAP */ - i = tx_ring->next_to_clean; - eop = tx_ring->tx_buffer_info[i].next_to_watch; - eop_desc = IXGBE_TX_DESC_ADV(*tx_ring, eop); -@@ -1187,6 +1213,17 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - int ddp_bytes = 0; - #endif /* IXGBE_FCOE */ - -+#ifdef DEV_NETMAP -+ int nm_irq; -+ /* -+ * Same as the txeof routine: only wakeup clients on intr. -+ */ -+ nm_irq = netmap_rx_irq(adapter->netdev, rx_ring->queue_index, work_done); -+ if (nm_irq != NM_IRQ_PASS) { -+ *work_done = (nm_irq == NM_IRQ_RESCHED) ? work_to_do : 1; -+ return true; -+ } -+#endif /* DEV_NETMAP */ - i = rx_ring->next_to_clean; - rx_desc = IXGBE_RX_DESC_ADV(*rx_ring, i); - staterr = le32_to_cpu(rx_desc->wb.upper.status_error); -@@ -3159,6 +3196,12 @@ static void ixgbe_configure(struct ixgbe_adapter *adapter) - - ixgbe_configure_tx(adapter); - ixgbe_configure_rx(adapter); -+#ifdef DEV_NETMAP -+ for (i = 0; i < adapter->num_rx_queues; i++) -+ ixgbe_netmap_configure_rx_ring(adapter, -+ adapter->rx_ring[i]->reg_idx); -+ return; -+#endif /* DEV_NETMAP */ - for (i = 0; i < adapter->num_rx_queues; i++) - ixgbe_alloc_rx_buffers(adapter, adapter->rx_ring[i], - (adapter->rx_ring[i]->count - 1)); -@@ -3390,6 +3433,10 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter) - DPRINTK(DRV, ERR, "Could not enable " - "Tx Queue %d\n", j); - } -+#ifdef DEV_NETMAP // XXX i and j are the same ? -+ ixgbe_netmap_configure_tx_ring(adapter, j); -+#endif /* DEV_NETMAP */ -+ - } - - for (i = 0; i < num_rx_rings; i++) { -@@ -6833,6 +6880,11 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, - - dev_info(&pdev->dev, "Intel(R) 10 Gigabit Network Connection\n"); - cards_found++; -+ -+#ifdef DEV_NETMAP -+ ixgbe_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ -+ - return 0; - - err_register: -@@ -6873,6 +6925,10 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) - struct net_device *netdev = pci_get_drvdata(pdev); - struct ixgbe_adapter *adapter = netdev_priv(netdev); - -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - set_bit(__IXGBE_DOWN, &adapter->state); - /* clear the module not found bit to make sure the worker won't - * reschedule diff --git a/LINUX/final-patches/vanilla--ixgbe--20625--20626 b/LINUX/final-patches/vanilla--ixgbe--20625--20626 deleted file mode 100644 index 4842ca9e1..000000000 --- a/LINUX/final-patches/vanilla--ixgbe--20625--20626 +++ /dev/null @@ -1,106 +0,0 @@ -diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index eee0b29..a94380e 100644 ---- a/ixgbe/ixgbe_main.c -+++ b/ixgbe/ixgbe_main.c -@@ -214,6 +214,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { - {} - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+/* -+ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to -+ * be a reference on how to implement netmap support in a driver. -+ * Additional comments are in ixgbe_netmap_linux.h . -+ * -+ * The code is originally developed on FreeBSD and in the interest -+ * of maintainability we try to limit differences between the two systems. -+ * -+ * contains functions for netmap support -+ * that extend the standard driver. -+ * It also defines DEV_NETMAP so further conditional sections use -+ * that instead of CONFIG_NETMAP -+ */ -+#include -+#endif - - /* - * ixgbe_regdump - register printout routine -@@ -740,6 +756,16 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, - unsigned int i, eop, count = 0; - unsigned int total_bytes = 0, total_packets = 0; - -+#ifdef DEV_NETMAP -+ /* -+ * In netmap mode, all the work is done in the context -+ * of the client thread. Interrupt handlers only wake up -+ * clients, which may be sleeping on individual rings -+ * or on a global resource for all rings. -+ */ -+ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) -+ return 1; /* seems to be ignored */ -+#endif /* DEV_NETMAP */ - i = tx_ring->next_to_clean; - eop = tx_ring->tx_buffer_info[i].next_to_watch; - eop_desc = IXGBE_TX_DESC_ADV(tx_ring, eop); -@@ -1185,6 +1211,17 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - int ddp_bytes = 0; - #endif /* IXGBE_FCOE */ - -+#ifdef DEV_NETMAP -+ int nm_irq; -+ /* -+ * Same as the txeof routine: only wakeup clients on intr. -+ */ -+ nm_irq = netmap_rx_irq(adapter->netdev, rx_ring->queue_index, work_done); -+ if (nm_irq != NM_IRQ_PASS) { -+ *work_done = (nm_irq == NM_IRQ_RESCHED) ? work_to_do : 1; -+ return true; -+ } -+#endif /* DEV_NETMAP */ - i = rx_ring->next_to_clean; - rx_desc = IXGBE_RX_DESC_ADV(rx_ring, i); - staterr = le32_to_cpu(rx_desc->wb.upper.status_error); -@@ -2519,6 +2556,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); -+#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); -+#endif /* DEV_NETMAP */ - } - - static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -2833,6 +2873,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, - IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); - - ixgbe_rx_desc_queue_enable(adapter, ring); -+#ifdef DEV_NETMAP -+ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) -+ return; -+#endif /* DEV_NETMAP */ - ixgbe_alloc_rx_buffers(adapter, ring, IXGBE_DESC_UNUSED(ring)); - } - -@@ -7048,6 +7092,11 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, - - e_dev_info("Intel(R) 10 Gigabit Network Connection\n"); - cards_found++; -+ -+#ifdef DEV_NETMAP -+ ixgbe_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ -+ - return 0; - - err_register: -@@ -7088,6 +7137,10 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) - struct net_device *netdev = pci_get_drvdata(pdev); - struct ixgbe_adapter *adapter = netdev_priv(netdev); - -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - set_bit(__IXGBE_DOWN, &adapter->state); - /* clear the module not found bit to make sure the worker won't - * reschedule diff --git a/LINUX/final-patches/vanilla--ixgbe--20626--30100 b/LINUX/final-patches/vanilla--ixgbe--20626--30100 deleted file mode 100644 index cb603e0a6..000000000 --- a/LINUX/final-patches/vanilla--ixgbe--20626--30100 +++ /dev/null @@ -1,106 +0,0 @@ -diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index 30f9ccf..e570fce 100644 ---- a/ixgbe/ixgbe_main.c -+++ b/ixgbe/ixgbe_main.c -@@ -221,6 +221,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { - {} - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+/* -+ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to -+ * be a reference on how to implement netmap support in a driver. -+ * Additional comments are in ixgbe_netmap_linux.h . -+ * -+ * The code is originally developed on FreeBSD and in the interest -+ * of maintainability we try to limit differences between the two systems. -+ * -+ * contains functions for netmap support -+ * that extend the standard driver. -+ * It also defines DEV_NETMAP so further conditional sections use -+ * that instead of CONFIG_NETMAP -+ */ -+#include -+#endif - - /* - * ixgbe_regdump - register printout routine -@@ -826,6 +842,16 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, - unsigned int total_bytes = 0, total_packets = 0; - u16 i, eop, count = 0; - -+#ifdef DEV_NETMAP -+ /* -+ * In netmap mode, all the work is done in the context -+ * of the client thread. Interrupt handlers only wake up -+ * clients, which may be sleeping on individual rings -+ * or on a global resource for all rings. -+ */ -+ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) -+ return 1; /* seems to be ignored */ -+#endif /* DEV_NETMAP */ - i = tx_ring->next_to_clean; - eop = tx_ring->tx_buffer_info[i].next_to_watch; - eop_desc = IXGBE_TX_DESC_ADV(tx_ring, eop); -@@ -1308,6 +1334,17 @@ static void ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - u16 cleaned_count = 0; - bool pkt_is_rsc = false; - -+#ifdef DEV_NETMAP -+ int nm_irq; -+ /* -+ * Same as the txeof routine: only wakeup clients on intr. -+ */ -+ nm_irq = netmap_rx_irq(adapter->netdev, rx_ring->queue_index, work_done); -+ if (nm_irq != NM_IRQ_PASS) { -+ *work_done = (nm_irq == NM_IRQ_RESCHED) ? work_to_do : 1; -+ return; -+ } -+#endif /* DEV_NETMAP */ - i = rx_ring->next_to_clean; - rx_desc = IXGBE_RX_DESC_ADV(rx_ring, i); - staterr = le32_to_cpu(rx_desc->wb.upper.status_error); -@@ -2730,6 +2767,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); -+#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); -+#endif /* DEV_NETMAP */ - } - - static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -3094,6 +3134,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, - IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); - - ixgbe_rx_desc_queue_enable(adapter, ring); -+#ifdef DEV_NETMAP -+ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) -+ return; -+#endif /* DEV_NETMAP */ - ixgbe_alloc_rx_buffers(ring, IXGBE_DESC_UNUSED(ring)); - } - -@@ -7450,6 +7494,11 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, - - e_dev_info("Intel(R) 10 Gigabit Network Connection\n"); - cards_found++; -+ -+#ifdef DEV_NETMAP -+ ixgbe_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ -+ - return 0; - - err_register: -@@ -7490,6 +7539,10 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) - struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); - struct net_device *netdev = adapter->netdev; - -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - set_bit(__IXGBE_DOWN, &adapter->state); - - /* diff --git a/LINUX/final-patches/vanilla--ixgbe--30100--30200 b/LINUX/final-patches/vanilla--ixgbe--30100--30200 deleted file mode 100644 index bcefb65ac..000000000 --- a/LINUX/final-patches/vanilla--ixgbe--30100--30200 +++ /dev/null @@ -1,107 +0,0 @@ -diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index e1fcc95..262e92d 100644 ---- a/ixgbe/ixgbe_main.c -+++ b/ixgbe/ixgbe_main.c -@@ -249,6 +249,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { - {} - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+/* -+ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to -+ * be a reference on how to implement netmap support in a driver. -+ * Additional comments are in ixgbe_netmap_linux.h . -+ * -+ * The code is originally developed on FreeBSD and in the interest -+ * of maintainability we try to limit differences between the two systems. -+ * -+ * contains functions for netmap support -+ * that extend the standard driver. -+ * It also defines DEV_NETMAP so further conditional sections use -+ * that instead of CONFIG_NETMAP -+ */ -+#include -+#endif - - /* - * ixgbe_regdump - register printout routine -@@ -801,6 +817,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, - unsigned int total_bytes = 0, total_packets = 0; - u16 i, eop, count = 0; - -+#ifdef DEV_NETMAP -+ /* -+ * In netmap mode, all the work is done in the context -+ * of the client thread. Interrupt handlers only wake up -+ * clients, which may be sleeping on individual rings -+ * or on a global resource for all rings. -+ */ -+ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) -+ return 1; /* seems to be ignored */ -+#endif /* DEV_NETMAP */ -+ - i = tx_ring->next_to_clean; - eop = tx_ring->tx_buffer_info[i].next_to_watch; - eop_desc = IXGBE_TX_DESC_ADV(tx_ring, eop); -@@ -1303,6 +1330,17 @@ static void ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - u16 cleaned_count = 0; - bool pkt_is_rsc = false; - -+#ifdef DEV_NETMAP -+ int nm_irq; -+ /* -+ * Same as the txeof routine: only wakeup clients on intr. -+ */ -+ nm_irq = netmap_rx_irq(adapter->netdev, rx_ring->queue_index, work_done); -+ if (nm_irq != NM_IRQ_PASS) { -+ *work_done = (nm_irq == NM_IRQ_RESCHED) ? work_to_do : 1; -+ return; -+ } -+#endif /* DEV_NETMAP */ - i = rx_ring->next_to_clean; - rx_desc = IXGBE_RX_DESC_ADV(rx_ring, i); - staterr = le32_to_cpu(rx_desc->wb.upper.status_error); -@@ -2676,6 +2714,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); -+#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); -+#endif /* DEV_NETMAP */ - } - - static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -3039,6 +3080,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, - IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); - - ixgbe_rx_desc_queue_enable(adapter, ring); -+#ifdef DEV_NETMAP -+ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) -+ return; -+#endif /* DEV_NETMAP */ - ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); - } - -@@ -7696,6 +7741,11 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, - - e_dev_info("Intel(R) 10 Gigabit Network Connection\n"); - cards_found++; -+ -+#ifdef DEV_NETMAP -+ ixgbe_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ -+ - return 0; - - err_register: -@@ -7732,6 +7782,10 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) - struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); - struct net_device *netdev = adapter->netdev; - -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - set_bit(__IXGBE_DOWN, &adapter->state); - cancel_work_sync(&adapter->service_task); - diff --git a/LINUX/final-patches/vanilla--ixgbe--30200--30400 b/LINUX/final-patches/vanilla--ixgbe--30200--30400 deleted file mode 100644 index ed39437cb..000000000 --- a/LINUX/final-patches/vanilla--ixgbe--30200--30400 +++ /dev/null @@ -1,105 +0,0 @@ -diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index 8ef92d1..ce93cea 100644 ---- a/ixgbe/ixgbe_main.c -+++ b/ixgbe/ixgbe_main.c -@@ -188,6 +188,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { - {} - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+/* -+ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to -+ * be a reference on how to implement netmap support in a driver. -+ * Additional comments are in ixgbe_netmap_linux.h . -+ * -+ * The code is originally developed on FreeBSD and in the interest -+ * of maintainability we try to limit differences between the two systems. -+ * -+ * contains functions for netmap support -+ * that extend the standard driver. -+ * It also defines DEV_NETMAP so further conditional sections use -+ * that instead of CONFIG_NETMAP -+ */ -+#include -+#endif - - /* - * ixgbe_regdump - register printout routine -@@ -745,6 +761,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, - unsigned int budget = q_vector->tx.work_limit; - u16 i = tx_ring->next_to_clean; - -+#ifdef DEV_NETMAP -+ /* -+ * In netmap mode, all the work is done in the context -+ * of the client thread. Interrupt handlers only wake up -+ * clients, which may be sleeping on individual rings -+ * or on a global resource for all rings. -+ */ -+ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) -+ return 1; /* seems to be ignored */ -+#endif /* DEV_NETMAP */ -+ - tx_buffer = &tx_ring->tx_buffer_info[i]; - tx_desc = IXGBE_TX_DESC_ADV(tx_ring, i); - -@@ -1253,6 +1280,15 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - u16 cleaned_count = 0; - bool pkt_is_rsc = false; - -+#ifdef DEV_NETMAP -+ /* -+ * Same as the txeof routine: only wakeup clients on intr. -+ */ -+ int dummy, nm_irq; -+ nm_irq = netmap_rx_irq(adapter->netdev, rx_ring->queue_index, &dummy); -+ if (nm_irq != NM_IRQ_PASS) -+ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; -+#endif /* DEV_NETMAP */ - i = rx_ring->next_to_clean; - rx_desc = IXGBE_RX_DESC_ADV(rx_ring, i); - staterr = le32_to_cpu(rx_desc->wb.upper.status_error); -@@ -2420,6 +2456,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); -+#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); -+#endif /* DEV_NETMAP */ - } - - static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -2783,6 +2822,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, - IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); - - ixgbe_rx_desc_queue_enable(adapter, ring); -+#ifdef DEV_NETMAP -+ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) -+ return; -+#endif /* DEV_NETMAP */ - ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); - } - -@@ -7710,6 +7753,11 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, - - e_dev_info("Intel(R) 10 Gigabit Network Connection\n"); - cards_found++; -+ -+#ifdef DEV_NETMAP -+ ixgbe_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ -+ - return 0; - - err_register: -@@ -7746,6 +7794,10 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) - struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); - struct net_device *netdev = adapter->netdev; - -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - set_bit(__IXGBE_DOWN, &adapter->state); - cancel_work_sync(&adapter->service_task); - diff --git a/LINUX/final-patches/vanilla--ixgbe--30500--30700 b/LINUX/final-patches/vanilla--ixgbe--30500--30700 deleted file mode 100644 index b2070707c..000000000 --- a/LINUX/final-patches/vanilla--ixgbe--30500--30700 +++ /dev/null @@ -1,113 +0,0 @@ -diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index e242104..5a995de 100644 ---- a/ixgbe/ixgbe_main.c -+++ b/ixgbe/ixgbe_main.c -@@ -204,6 +204,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { - {} - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+/* -+ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to -+ * be a reference on how to implement netmap support in a driver. -+ * Additional comments are in ixgbe_netmap_linux.h . -+ * -+ * The code is originally developed on FreeBSD and in the interest -+ * of maintainability we try to limit differences between the two systems. -+ * -+ * contains functions for netmap support -+ * that extend the standard driver. -+ * It also defines DEV_NETMAP so further conditional sections use -+ * that instead of CONFIG_NETMAP -+ */ -+#include -+#endif - - /* - * ixgbe_regdump - register printout routine -@@ -764,6 +780,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, - if (test_bit(__IXGBE_DOWN, &adapter->state)) - return true; - -+#ifdef DEV_NETMAP -+ /* -+ * In netmap mode, all the work is done in the context -+ * of the client thread. Interrupt handlers only wake up -+ * clients, which may be sleeping on individual rings -+ * or on a global resource for all rings. -+ */ -+ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) -+ return 1; /* seems to be ignored */ -+#endif /* DEV_NETMAP */ -+ - tx_buffer = &tx_ring->tx_buffer_info[i]; - tx_desc = IXGBE_TX_DESC(tx_ring, i); - i -= tx_ring->count; -@@ -1665,6 +1692,16 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - #endif /* IXGBE_FCOE */ - u16 cleaned_count = ixgbe_desc_unused(rx_ring); - -+#ifdef DEV_NETMAP -+ /* -+ * Same as the txeof routine: only wakeup clients on intr. -+ */ -+ int dummy, nm_irq; -+ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); -+ if (nm_irq != NM_IRQ_PASS) -+ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; -+#endif /* DEV_NETMAP */ -+ - do { - struct ixgbe_rx_buffer *rx_buffer; - union ixgbe_adv_rx_desc *rx_desc; -@@ -2725,6 +2762,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); -+#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); -+#endif /* DEV_NETMAP */ - } - - static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -3102,6 +3142,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, - IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); - - ixgbe_rx_desc_queue_enable(adapter, ring); -+#ifdef DEV_NETMAP -+ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) -+ return; -+#endif /* DEV_NETMAP */ - ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); - } - -@@ -4827,6 +4871,7 @@ static int ixgbe_open(struct net_device *netdev) - - ixgbe_up_complete(adapter); - -+ - return 0; - - err_req_irq: -@@ -7358,6 +7403,10 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, - e_err(probe, "failed to allocate sysfs resources\n"); - #endif /* CONFIG_IXGBE_HWMON */ - -+#ifdef DEV_NETMAP -+ ixgbe_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ -+ - return 0; - - err_register: -@@ -7393,6 +7442,10 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) - struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); - struct net_device *netdev = adapter->netdev; - -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - set_bit(__IXGBE_DOWN, &adapter->state); - cancel_work_sync(&adapter->service_task); - diff --git a/LINUX/final-patches/vanilla--ixgbe--30700--30a00 b/LINUX/final-patches/vanilla--ixgbe--30700--30a00 deleted file mode 100644 index 1f2104684..000000000 --- a/LINUX/final-patches/vanilla--ixgbe--30700--30a00 +++ /dev/null @@ -1,113 +0,0 @@ -diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index fa3d552..12785e3 100644 ---- a/ixgbe/ixgbe_main.c -+++ b/ixgbe/ixgbe_main.c -@@ -205,6 +205,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { - {} - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+/* -+ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to -+ * be a reference on how to implement netmap support in a driver. -+ * Additional comments are in ixgbe_netmap_linux.h . -+ * -+ * The code is originally developed on FreeBSD and in the interest -+ * of maintainability we try to limit differences between the two systems. -+ * -+ * contains functions for netmap support -+ * that extend the standard driver. -+ * It also defines DEV_NETMAP so further conditional sections use -+ * that instead of CONFIG_NETMAP -+ */ -+#include -+#endif - - /* - * ixgbe_regdump - register printout routine -@@ -766,6 +782,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, - if (test_bit(__IXGBE_DOWN, &adapter->state)) - return true; - -+#ifdef DEV_NETMAP -+ /* -+ * In netmap mode, all the work is done in the context -+ * of the client thread. Interrupt handlers only wake up -+ * clients, which may be sleeping on individual rings -+ * or on a global resource for all rings. -+ */ -+ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) -+ return 1; /* seems to be ignored */ -+#endif /* DEV_NETMAP */ -+ - tx_buffer = &tx_ring->tx_buffer_info[i]; - tx_desc = IXGBE_TX_DESC(tx_ring, i); - i -= tx_ring->count; -@@ -1791,6 +1818,16 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - #endif /* IXGBE_FCOE */ - u16 cleaned_count = ixgbe_desc_unused(rx_ring); - -+#ifdef DEV_NETMAP -+ /* -+ * Same as the txeof routine: only wakeup clients on intr. -+ */ -+ int dummy, nm_irq; -+ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); -+ if (nm_irq != NM_IRQ_PASS) -+ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; -+#endif /* DEV_NETMAP */ -+ - do { - union ixgbe_adv_rx_desc *rx_desc; - struct sk_buff *skb; -@@ -2788,6 +2825,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); -+#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); -+#endif /* DEV_NETMAP */ - } - - static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -3157,6 +3197,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, - IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); - - ixgbe_rx_desc_queue_enable(adapter, ring); -+#ifdef DEV_NETMAP -+ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) -+ return; -+#endif /* DEV_NETMAP */ - ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); - } - -@@ -4903,6 +4947,7 @@ static int ixgbe_open(struct net_device *netdev) - - ixgbe_up_complete(adapter); - -+ - return 0; - - err_set_queues: -@@ -7464,6 +7509,10 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, - ixgbe_dbg_adapter_init(adapter); - #endif /* CONFIG_DEBUG_FS */ - -+#ifdef DEV_NETMAP -+ ixgbe_netmap_attach(adapter); -+#endif /* DEV_NETMAP */ -+ - return 0; - - err_register: -@@ -7498,6 +7547,10 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) - struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); - struct net_device *netdev = adapter->netdev; - -+#ifdef DEV_NETMAP -+ netmap_detach(netdev); -+#endif /* DEV_NETMAP */ -+ - #ifdef CONFIG_DEBUG_FS - ixgbe_dbg_adapter_exit(adapter); - #endif /*CONFIG_DEBUG_FS */ diff --git a/LINUX/final-patches/vanilla--ixgbe--31300--40900 b/LINUX/final-patches/vanilla--ixgbe--40000--40900 similarity index 76% rename from LINUX/final-patches/vanilla--ixgbe--31300--40900 rename to LINUX/final-patches/vanilla--ixgbe--40000--40900 index 004f1a9ce..3b406348f 100644 --- a/LINUX/final-patches/vanilla--ixgbe--31300--40900 +++ b/LINUX/final-patches/vanilla--ixgbe--40000--40900 @@ -1,8 +1,8 @@ diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index 67b02bd..2160fcb 100644 +index 70cc4c5c0a01..5ad76a0d3494 100644 --- a/ixgbe/ixgbe_main.c +++ b/ixgbe/ixgbe_main.c -@@ -458,6 +458,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { +@@ -459,6 +459,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { { .name = NULL } }; @@ -25,7 +25,7 @@ index 67b02bd..2160fcb 100644 /* * ixgbe_regdump - register printout routine -@@ -1087,6 +1103,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, +@@ -1088,6 +1104,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, if (test_bit(__IXGBE_DOWN, &adapter->state)) return true; @@ -43,7 +43,7 @@ index 67b02bd..2160fcb 100644 tx_buffer = &tx_ring->tx_buffer_info[i]; tx_desc = IXGBE_TX_DESC(tx_ring, i); i -= tx_ring->count; -@@ -1997,6 +2024,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, +@@ -2018,6 +2045,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, #endif /* IXGBE_FCOE */ u16 cleaned_count = ixgbe_desc_unused(rx_ring); @@ -60,17 +60,18 @@ index 67b02bd..2160fcb 100644 while (likely(total_rx_packets < budget)) { union ixgbe_adv_rx_desc *rx_desc; struct sk_buff *skb; -@@ -3049,6 +3086,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); +@@ -3055,6 +3092,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + + clear_bit(__IXGBE_HANG_CHECK_ARMED, &ring->state); + +#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); +#endif /* DEV_NETMAP */ - } ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); - static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -3483,6 +3523,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, +@@ -3504,6 +3545,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); ixgbe_rx_desc_queue_enable(adapter, ring); @@ -81,7 +82,7 @@ index 67b02bd..2160fcb 100644 ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); } -@@ -4805,6 +4849,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) +@@ -4840,6 +4885,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) e_crit(drv, "Fan has stopped, replace the adapter\n"); } @@ -91,15 +92,15 @@ index 67b02bd..2160fcb 100644 /* bring the link up in the watchdog, this could race with our first * link up interrupt but shouldn't be a problem */ adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE; -@@ -5627,6 +5674,7 @@ static int ixgbe_open(struct net_device *netdev) +@@ -5714,6 +5762,7 @@ static int ixgbe_close(struct net_device *netdev) - ixgbe_up_complete(adapter); + ixgbe_release_hw_control(adapter); + return 0; + } - err_set_queues: -@@ -8521,6 +8569,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +@@ -8629,6 +8678,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL, true); @@ -110,13 +111,13 @@ index 67b02bd..2160fcb 100644 return 0; err_register: -@@ -8564,6 +8616,11 @@ static void ixgbe_remove(struct pci_dev *pdev) +@@ -8672,6 +8725,11 @@ static void ixgbe_remove(struct pci_dev *pdev) return; netdev = adapter->netdev; + +#ifdef DEV_NETMAP -+ netmap_detach(netdev); ++ ixgbe_netmap_detach(adapter); +#endif /* DEV_NETMAP */ + ixgbe_dbg_adapter_exit(adapter); diff --git a/LINUX/final-patches/vanilla--ixgbe--40900--99999 b/LINUX/final-patches/vanilla--ixgbe--40900--41000 similarity index 79% rename from LINUX/final-patches/vanilla--ixgbe--40900--99999 rename to LINUX/final-patches/vanilla--ixgbe--40900--41000 index 11cf6853b..b3ca99fc5 100644 --- a/LINUX/final-patches/vanilla--ixgbe--40900--99999 +++ b/LINUX/final-patches/vanilla--ixgbe--40900--41000 @@ -1,5 +1,5 @@ diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index fee1f2918..2a71eac 100644 +index fee1f2918ead..7b86ce44333a 100644 --- a/ixgbe/ixgbe_main.c +++ b/ixgbe/ixgbe_main.c @@ -497,6 +497,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { @@ -60,17 +60,27 @@ index fee1f2918..2a71eac 100644 while (likely(total_rx_packets < budget)) { union ixgbe_adv_rx_desc *rx_desc; struct sk_buff *skb; -@@ -3225,6 +3262,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - hw_dbg(hw, "Could not enable Tx Queue %d\n", reg_idx); +@@ -3210,6 +3247,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + + clear_bit(__IXGBE_HANG_CHECK_ARMED, &ring->state); + +#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); +#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3224,7 +3265,7 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + txdctl = IXGBE_READ_REG(hw, IXGBE_TXDCTL(reg_idx)); + } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); + if (!wait_loop) +- hw_dbg(hw, "Could not enable Tx Queue %d\n", reg_idx); ++ e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); } static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -3720,6 +3760,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, +@@ -3720,6 +3761,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); ixgbe_rx_desc_queue_enable(adapter, ring); @@ -81,7 +91,7 @@ index fee1f2918..2a71eac 100644 ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); } -@@ -5271,6 +5315,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) +@@ -5271,6 +5316,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) e_crit(drv, "Fan has stopped, replace the adapter\n"); } @@ -91,7 +101,7 @@ index fee1f2918..2a71eac 100644 /* bring the link up in the watchdog, this could race with our first * link up interrupt but shouldn't be a problem */ adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE; -@@ -9799,6 +9846,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +@@ -9799,6 +9847,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL, true); @@ -102,13 +112,13 @@ index fee1f2918..2a71eac 100644 return 0; err_register: -@@ -9843,6 +9894,11 @@ static void ixgbe_remove(struct pci_dev *pdev) +@@ -9843,6 +9895,11 @@ static void ixgbe_remove(struct pci_dev *pdev) return; netdev = adapter->netdev; + +#ifdef DEV_NETMAP -+ netmap_detach(netdev); ++ ixgbe_netmap_detach(adapter); +#endif /* DEV_NETMAP */ + ixgbe_dbg_adapter_exit(adapter); diff --git a/LINUX/final-patches/vanilla--ixgbe--30f00--31300 b/LINUX/final-patches/vanilla--ixgbe--41000--41400 similarity index 69% rename from LINUX/final-patches/vanilla--ixgbe--30f00--31300 rename to LINUX/final-patches/vanilla--ixgbe--41000--41400 index 72ffaa672..55599b36e 100644 --- a/LINUX/final-patches/vanilla--ixgbe--30f00--31300 +++ b/LINUX/final-patches/vanilla--ixgbe--41000--41400 @@ -1,9 +1,9 @@ diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index d62e7a2..dfd338d 100644 +index 9fc063af233c..85ceb30fd6c7 100644 --- a/ixgbe/ixgbe_main.c +++ b/ixgbe/ixgbe_main.c -@@ -417,6 +417,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { - {} +@@ -516,6 +516,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { + { .name = NULL } }; +#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) @@ -25,7 +25,7 @@ index d62e7a2..dfd338d 100644 /* * ixgbe_regdump - register printout routine -@@ -1048,6 +1064,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, +@@ -1178,6 +1194,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, if (test_bit(__IXGBE_DOWN, &adapter->state)) return true; @@ -43,9 +43,9 @@ index d62e7a2..dfd338d 100644 tx_buffer = &tx_ring->tx_buffer_info[i]; tx_desc = IXGBE_TX_DESC(tx_ring, i); i -= tx_ring->count; -@@ -2087,6 +2114,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - #endif /* IXGBE_FCOE */ - u16 cleaned_count = ixgbe_desc_unused(rx_ring); +@@ -2341,6 +2368,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; +#ifdef DEV_NETMAP + /* @@ -57,20 +57,30 @@ index d62e7a2..dfd338d 100644 + return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; +#endif /* DEV_NETMAP */ + + xdp.rxq = &rx_ring->xdp_rxq; + while (likely(total_rx_packets < budget)) { - union ixgbe_adv_rx_desc *rx_desc; - struct sk_buff *skb; -@@ -3131,6 +3168,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); +@@ -3582,6 +3619,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + +#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); +#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3596,7 +3637,7 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + txdctl = IXGBE_READ_REG(hw, IXGBE_TXDCTL(reg_idx)); + } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); + if (!wait_loop) +- hw_dbg(hw, "Could not enable Tx Queue %d\n", reg_idx); ++ e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); } static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -3496,6 +3536,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, +@@ -4170,6 +4211,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); ixgbe_rx_desc_queue_enable(adapter, ring); @@ -81,7 +91,7 @@ index d62e7a2..dfd338d 100644 ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); } -@@ -4698,6 +4742,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) +@@ -5667,6 +5712,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) e_crit(drv, "Fan has stopped, replace the adapter\n"); } @@ -91,15 +101,7 @@ index d62e7a2..dfd338d 100644 /* bring the link up in the watchdog, this could race with our first * link up interrupt but shouldn't be a problem */ adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE; -@@ -5503,6 +5550,7 @@ static int ixgbe_open(struct net_device *netdev) - - ixgbe_up_complete(adapter); - -+ - return 0; - - err_set_queues: -@@ -8310,6 +8358,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +@@ -10665,6 +10713,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL, true); @@ -110,12 +112,13 @@ index d62e7a2..dfd338d 100644 return 0; err_register: -@@ -8345,6 +8397,10 @@ static void ixgbe_remove(struct pci_dev *pdev) - struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); - struct net_device *netdev = adapter->netdev; +@@ -10710,6 +10762,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + netdev = adapter->netdev; ++ +#ifdef DEV_NETMAP -+ netmap_detach(netdev); ++ ixgbe_netmap_detach(adapter); +#endif /* DEV_NETMAP */ + ixgbe_dbg_adapter_exit(adapter); diff --git a/LINUX/final-patches/vanilla--ixgbe--30d00--30f00 b/LINUX/final-patches/vanilla--ixgbe--41400--50800 similarity index 61% rename from LINUX/final-patches/vanilla--ixgbe--30d00--30f00 rename to LINUX/final-patches/vanilla--ixgbe--41400--50800 index 7083572e5..502721dbb 100644 --- a/LINUX/final-patches/vanilla--ixgbe--30d00--30f00 +++ b/LINUX/final-patches/vanilla--ixgbe--41400--50800 @@ -1,9 +1,9 @@ diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index 5bcc870..c4e71a9 100644 +index 113b38e0defb..15079a88fbb2 100644 --- a/ixgbe/ixgbe_main.c +++ b/ixgbe/ixgbe_main.c -@@ -328,6 +328,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { - {} +@@ -457,6 +457,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { + { .name = NULL } }; +#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) @@ -25,7 +25,7 @@ index 5bcc870..c4e71a9 100644 /* * ixgbe_regdump - register printout routine -@@ -959,6 +975,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, +@@ -1119,6 +1135,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, if (test_bit(__IXGBE_DOWN, &adapter->state)) return true; @@ -43,9 +43,9 @@ index 5bcc870..c4e71a9 100644 tx_buffer = &tx_ring->tx_buffer_info[i]; tx_desc = IXGBE_TX_DESC(tx_ring, i); i -= tx_ring->count; -@@ -1995,6 +2022,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - #endif /* IXGBE_FCOE */ - u16 cleaned_count = ixgbe_desc_unused(rx_ring); +@@ -2283,6 +2310,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; +#ifdef DEV_NETMAP + /* @@ -57,20 +57,30 @@ index 5bcc870..c4e71a9 100644 + return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; +#endif /* DEV_NETMAP */ + - do { - union ixgbe_adv_rx_desc *rx_desc; - struct sk_buff *skb; -@@ -3033,6 +3070,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); + xdp.rxq = &rx_ring->xdp_rxq; + + while (likely(total_rx_packets < budget)) { +@@ -3540,6 +3577,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + +#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); +#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -3554,7 +3595,7 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + txdctl = IXGBE_READ_REG(hw, IXGBE_TXDCTL(reg_idx)); + } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); + if (!wait_loop) +- hw_dbg(hw, "Could not enable Tx Queue %d\n", reg_idx); ++ e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); } static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -3394,6 +3434,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, +@@ -4147,6 +4188,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); ixgbe_rx_desc_queue_enable(adapter, ring); @@ -78,35 +88,20 @@ index 5bcc870..c4e71a9 100644 + if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) + return; +#endif /* DEV_NETMAP */ - ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); - } + if (ring->xsk_umem) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); + else +@@ -5631,6 +5676,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) + e_crit(drv, "Fan has stopped, replace the adapter\n"); + } -@@ -4600,16 +4644,6 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) - /* enable transmits */ - netif_tx_start_all_queues(adapter->netdev); - -- /* enable any upper devices */ -- netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) { -- if (netif_is_macvlan(upper)) { -- struct macvlan_dev *vlan = netdev_priv(upper); -- -- if (vlan->fwd_priv) -- netif_tx_start_all_queues(upper); -- } -- } -- ++ /* enable transmits */ ++ netif_tx_start_all_queues(adapter->netdev); ++ /* bring the link up in the watchdog, this could race with our first * link up interrupt but shouldn't be a problem */ adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE; -@@ -5412,6 +5446,7 @@ static int ixgbe_open(struct net_device *netdev) - - ixgbe_up_complete(adapter); - -+ - return 0; - - err_set_queues: -@@ -8174,6 +8209,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +@@ -11119,6 +11167,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL, true); @@ -117,14 +112,15 @@ index 5bcc870..c4e71a9 100644 return 0; err_register: -@@ -8208,6 +8247,10 @@ static void ixgbe_remove(struct pci_dev *pdev) - struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); - struct net_device *netdev = adapter->netdev; +@@ -11164,6 +11216,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + netdev = adapter->netdev; ++ +#ifdef DEV_NETMAP -+ netmap_detach(netdev); ++ ixgbe_netmap_detach(adapter); +#endif /* DEV_NETMAP */ + ixgbe_dbg_adapter_exit(adapter); - set_bit(__IXGBE_DOWN, &adapter->state); + set_bit(__IXGBE_REMOVING, &adapter->state); diff --git a/LINUX/final-patches/vanilla--ixgbe--30a00--30d00 b/LINUX/final-patches/vanilla--ixgbe--50800--50a00 similarity index 57% rename from LINUX/final-patches/vanilla--ixgbe--30a00--30d00 rename to LINUX/final-patches/vanilla--ixgbe--50800--50a00 index d5186f1e7..cae395415 100644 --- a/LINUX/final-patches/vanilla--ixgbe--30a00--30d00 +++ b/LINUX/final-patches/vanilla--ixgbe--50800--50a00 @@ -1,9 +1,9 @@ diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index d30fbdd..0326ffd 100644 +index 97a423ecf808..a40ffcf4a6ee 100644 --- a/ixgbe/ixgbe_main.c +++ b/ixgbe/ixgbe_main.c -@@ -248,6 +248,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { - {} +@@ -460,6 +460,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { + { .name = NULL } }; +#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) @@ -25,7 +25,7 @@ index d30fbdd..0326ffd 100644 /* * ixgbe_regdump - register printout routine -@@ -872,6 +888,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, +@@ -1122,6 +1138,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, if (test_bit(__IXGBE_DOWN, &adapter->state)) return true; @@ -43,9 +43,9 @@ index d30fbdd..0326ffd 100644 tx_buffer = &tx_ring->tx_buffer_info[i]; tx_desc = IXGBE_TX_DESC(tx_ring, i); i -= tx_ring->count; -@@ -1906,6 +1933,16 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - #endif /* IXGBE_FCOE */ - u16 cleaned_count = ixgbe_desc_unused(rx_ring); +@@ -2299,6 +2326,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; +#ifdef DEV_NETMAP + /* @@ -57,20 +57,21 @@ index d30fbdd..0326ffd 100644 + return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; +#endif /* DEV_NETMAP */ + - do { - union ixgbe_adv_rx_desc *rx_desc; - struct sk_buff *skb; -@@ -2905,6 +2942,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); + xdp.rxq = &rx_ring->xdp_rxq; + + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ +@@ -3537,6 +3574,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + +#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); +#endif /* DEV_NETMAP */ - } ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); - static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -3266,6 +3306,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, +@@ -4144,6 +4185,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); ixgbe_rx_desc_queue_enable(adapter, ring); @@ -78,20 +79,22 @@ index d30fbdd..0326ffd 100644 + if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) + return; +#endif /* DEV_NETMAP */ - ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); - } - -@@ -5037,6 +5081,7 @@ static int ixgbe_open(struct net_device *netdev) - - ixgbe_up_complete(adapter); + if (ring->xsk_umem) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); + else +@@ -5644,6 +5689,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) + e_crit(drv, "Fan has stopped, replace the adapter\n"); + } ++ /* enable transmits */ ++ netif_tx_start_all_queues(adapter->netdev); + - return 0; + /* bring the link up in the watchdog, this could race with our first + * link up interrupt but shouldn't be a problem */ + adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE; +@@ -11177,6 +11225,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) - err_set_queues: -@@ -7658,6 +7703,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) - IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL, - true); + ixgbe_mii_bus_init(hw); +#ifdef DEV_NETMAP + ixgbe_netmap_attach(adapter); @@ -100,14 +103,15 @@ index d30fbdd..0326ffd 100644 return 0; err_register: -@@ -7692,6 +7741,10 @@ static void ixgbe_remove(struct pci_dev *pdev) - struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); - struct net_device *netdev = adapter->netdev; +@@ -11223,6 +11275,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + netdev = adapter->netdev; ++ +#ifdef DEV_NETMAP -+ netmap_detach(netdev); ++ ixgbe_netmap_detach(adapter); +#endif /* DEV_NETMAP */ + ixgbe_dbg_adapter_exit(adapter); - set_bit(__IXGBE_DOWN, &adapter->state); + set_bit(__IXGBE_REMOVING, &adapter->state); diff --git a/LINUX/final-patches/vanilla--ixgbe--50a00--50c00 b/LINUX/final-patches/vanilla--ixgbe--50a00--50c00 new file mode 100644 index 000000000..60def20af --- /dev/null +++ b/LINUX/final-patches/vanilla--ixgbe--50a00--50c00 @@ -0,0 +1,117 @@ +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index f3f449f53920..27545e40ab26 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -458,6 +458,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { + { .name = NULL } + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif + + /* + * ixgbe_regdump - register printout routine +@@ -1120,6 +1136,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return 1; /* seems to be ignored */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2301,6 +2328,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.rxq = &rx_ring->xdp_rxq; + + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ +@@ -3540,6 +3577,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4147,6 +4188,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); + else +@@ -5673,6 +5718,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) + e_crit(drv, "Fan has stopped, replace the adapter\n"); + } + ++ /* enable transmits */ ++ netif_tx_start_all_queues(adapter->netdev); ++ + /* bring the link up in the watchdog, this could race with our first + * link up interrupt but shouldn't be a problem */ + adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE; +@@ -11055,6 +11103,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_netdev; + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_netdev: +@@ -11103,6 +11155,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + ixgbe_dbg_adapter_exit(adapter); + + set_bit(__IXGBE_REMOVING, &adapter->state); diff --git a/LINUX/final-patches/vanilla--ixgbe--50c00--61000 b/LINUX/final-patches/vanilla--ixgbe--50c00--61000 new file mode 100644 index 000000000..2781969db --- /dev/null +++ b/LINUX/final-patches/vanilla--ixgbe--50c00--61000 @@ -0,0 +1,117 @@ +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index cffb95f8f632..e7273c414ebd 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -458,6 +458,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { + { .name = NULL } + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif + + /* + * ixgbe_regdump - register printout routine +@@ -1120,6 +1136,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return 1; /* seems to be ignored */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2297,6 +2324,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + unsigned int xdp_xmit = 0; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ + #if (PAGE_SIZE < 8192) + frame_sz = ixgbe_rx_frame_truesize(rx_ring, 0); +@@ -3534,6 +3571,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4143,6 +4184,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); + else +@@ -5669,6 +5714,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) + e_crit(drv, "Fan has stopped, replace the adapter\n"); + } + ++ /* enable transmits */ ++ netif_tx_start_all_queues(adapter->netdev); ++ + /* bring the link up in the watchdog, this could race with our first + * link up interrupt but shouldn't be a problem */ + adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE; +@@ -11051,6 +11099,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (err) + goto err_netdev; + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_netdev: +@@ -11099,6 +11151,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + ixgbe_dbg_adapter_exit(adapter); + + set_bit(__IXGBE_REMOVING, &adapter->state); diff --git a/LINUX/final-patches/vanilla--ixgbe--61000--61200 b/LINUX/final-patches/vanilla--ixgbe--61000--61200 new file mode 100644 index 000000000..6a0791e04 --- /dev/null +++ b/LINUX/final-patches/vanilla--ixgbe--61000--61200 @@ -0,0 +1,118 @@ +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index cba860f0e1f1..3d5eefdb7ad5 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -475,6 +475,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { + { .name = NULL } + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif + + /* + * ixgbe_regdump - register printout routine +@@ -1178,6 +1194,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return 1; /* seems to be ignored */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2343,6 +2370,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + struct xdp_buff xdp; + int xdp_res = 0; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ + #if (PAGE_SIZE < 8192) + frame_sz = ixgbe_rx_frame_truesize(rx_ring, 0); +@@ -3803,6 +3840,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4412,6 +4453,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); + else +@@ -6077,6 +6122,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) + e_crit(drv, "Fan has stopped, replace the adapter\n"); + } + ++ /* enable transmits */ ++ netif_tx_start_all_queues(adapter->netdev); ++ + /* bring the link up in the watchdog, this could race with our first + * link up interrupt but shouldn't be a problem */ + adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE; +@@ -11829,6 +11877,11 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + ixgbe_devlink_init_regions(adapter); + devl_register(adapter->devlink); + devl_unlock(adapter->devlink); ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_netdev: +@@ -11883,6 +11936,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + devl_lock(adapter->devlink); + devl_unregister(adapter->devlink); + ixgbe_devlink_destroy_regions(adapter); diff --git a/LINUX/final-patches/vanilla--ixgbe--61200--99999 b/LINUX/final-patches/vanilla--ixgbe--61200--99999 new file mode 100644 index 000000000..b3b68768e --- /dev/null +++ b/LINUX/final-patches/vanilla--ixgbe--61200--99999 @@ -0,0 +1,117 @@ +diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c +index 3190ce7e44c7..e5d7fe1e1b86 100644 +--- a/ixgbe/ixgbe_main.c ++++ b/ixgbe/ixgbe_main.c +@@ -477,6 +477,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { + { .name = NULL } + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#include ++#endif + + /* + * ixgbe_regdump - register printout routine +@@ -1360,6 +1376,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return 1; /* seems to be ignored */ ++#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBE_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -2509,6 +2536,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, + struct xdp_buff xdp; + int xdp_res = 0; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ + #if (PAGE_SIZE < 8192) + frame_sz = ixgbe_rx_frame_truesize(rx_ring, 0); +@@ -3973,6 +4010,10 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, + memset(ring->tx_buffer_info, 0, + sizeof(struct ixgbe_tx_buffer) * ring->count); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + /* enable queue */ + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), txdctl); + +@@ -4589,6 +4630,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + + ixgbe_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + if (ring->xsk_pool) + ixgbe_alloc_rx_buffers_zc(ring, ixgbe_desc_unused(ring)); + else +@@ -6254,6 +6299,9 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) + e_crit(drv, "Fan has stopped, replace the adapter\n"); + } + ++ /* enable transmits */ ++ netif_tx_start_all_queues(adapter->netdev); ++ + /* bring the link up in the watchdog, this could race with our first + * link up interrupt but shouldn't be a problem */ + adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE; +@@ -12008,6 +12056,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + if (ixgbe_fwlog_init(hw)) + e_dev_info("Firmware logging not supported\n"); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_netdev: +@@ -12062,6 +12114,11 @@ static void ixgbe_remove(struct pci_dev *pdev) + return; + + netdev = adapter->netdev; ++ ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + devl_lock(adapter->devlink); + devl_unregister(adapter->devlink); + ixgbe_devlink_destroy_regions(adapter); diff --git a/LINUX/final-patches/vanilla--ixgbevf--40000--40700 b/LINUX/final-patches/vanilla--ixgbevf--40000--40700 new file mode 100644 index 000000000..13df445f2 --- /dev/null +++ b/LINUX/final-patches/vanilla--ixgbevf--40000--40700 @@ -0,0 +1,109 @@ +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 4186981e562d..f37d7cc10e24 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -283,6 +283,24 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif ++ + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: board private structure +@@ -301,6 +319,18 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -912,6 +942,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + +@@ -1585,6 +1625,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1763,6 +1807,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4064,6 +4112,10 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + break; + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -4101,6 +4153,10 @@ static void ixgbevf_remove(struct pci_dev *pdev) + if (!netdev) + return; + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + adapter = netdev_priv(netdev); + + set_bit(__IXGBEVF_REMOVING, &adapter->state); diff --git a/LINUX/final-patches/vanilla--ixgbe--30400--30500 b/LINUX/final-patches/vanilla--ixgbevf--40700--40800 similarity index 50% rename from LINUX/final-patches/vanilla--ixgbe--30400--30500 rename to LINUX/final-patches/vanilla--ixgbevf--40700--40800 index 16903edfe..ef0acae1c 100644 --- a/LINUX/final-patches/vanilla--ixgbe--30400--30500 +++ b/LINUX/final-patches/vanilla--ixgbevf--40700--40800 @@ -1,10 +1,10 @@ -diff --git a/ixgbe/ixgbe_main.c b/ixgbe/ixgbe_main.c -index 467948e9..ff180b6 100644 ---- a/ixgbe/ixgbe_main.c -+++ b/ixgbe/ixgbe_main.c -@@ -204,6 +204,22 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { - {} - }; +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index acc24010cfe0..3d0b47eb123b 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -292,6 +292,24 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } +#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) +/* @@ -20,13 +20,15 @@ index 467948e9..ff180b6 100644 + * It also defines DEV_NETMAP so further conditional sections use + * that instead of CONFIG_NETMAP + */ ++#define NM_IXGBEVF +#include +#endif - - /* - * ixgbe_regdump - register printout routine -@@ -749,6 +765,17 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, - if (test_bit(__IXGBE_DOWN, &adapter->state)) ++ + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: board private structure +@@ -311,6 +329,18 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) return true; +#ifdef DEV_NETMAP @@ -37,15 +39,16 @@ index 467948e9..ff180b6 100644 + * or on a global resource for all rings. + */ + if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) -+ return 1; /* seems to be ignored */ ++ return true; +#endif /* DEV_NETMAP */ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; - tx_desc = IXGBE_TX_DESC(tx_ring, i); + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); i -= tx_ring->count; -@@ -1629,6 +1656,16 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, - #endif /* IXGBE_FCOE */ - u16 cleaned_count = ixgbe_desc_unused(rx_ring); +@@ -927,6 +957,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; +#ifdef DEV_NETMAP + /* @@ -57,43 +60,35 @@ index 467948e9..ff180b6 100644 + return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; +#endif /* DEV_NETMAP */ + - do { - struct ixgbe_rx_buffer *rx_buffer; + while (likely(total_rx_packets < budget)) { union ixgbe_adv_rx_desc *rx_desc; -@@ -2683,6 +2720,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, - } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); - if (!wait_loop) - e_err(drv, "Could not enable Tx Queue %d\n", reg_idx); + +@@ -1602,6 +1642,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + +#ifdef DEV_NETMAP -+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); +#endif /* DEV_NETMAP */ - } ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); - static void ixgbe_setup_mtqc(struct ixgbe_adapter *adapter) -@@ -3032,6 +3072,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, - IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl); + /* poll to verify queue is enabled */ +@@ -1789,6 +1833,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); - ixgbe_rx_desc_queue_enable(adapter, ring); + ixgbevf_rx_desc_queue_enable(adapter, ring); +#ifdef DEV_NETMAP + if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) + return; +#endif /* DEV_NETMAP */ - ixgbe_alloc_rx_buffers(ring, ixgbe_desc_unused(ring)); + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); } -@@ -4764,6 +4808,7 @@ static int ixgbe_open(struct net_device *netdev) - - ixgbe_up_complete(adapter); - -+ - return 0; - - err_req_irq: -@@ -7152,6 +7197,11 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, +@@ -4131,6 +4179,10 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + break; + } - e_dev_info("%s\n", ixgbe_default_device_descr); - cards_found++; -+ +#ifdef DEV_NETMAP + ixgbe_netmap_attach(adapter); +#endif /* DEV_NETMAP */ @@ -101,14 +96,14 @@ index 467948e9..ff180b6 100644 return 0; err_register: -@@ -7187,6 +7237,10 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) - struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); - struct net_device *netdev = adapter->netdev; +@@ -4170,6 +4222,10 @@ static void ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); +#ifdef DEV_NETMAP -+ netmap_detach(netdev); ++ ixgbe_netmap_detach(adapter); +#endif /* DEV_NETMAP */ + - set_bit(__IXGBE_DOWN, &adapter->state); + set_bit(__IXGBEVF_REMOVING, &adapter->state); cancel_work_sync(&adapter->service_task); diff --git a/LINUX/final-patches/vanilla--ixgbevf--40800--40900 b/LINUX/final-patches/vanilla--ixgbevf--40800--40900 new file mode 100644 index 000000000..35cdd90a7 --- /dev/null +++ b/LINUX/final-patches/vanilla--ixgbevf--40800--40900 @@ -0,0 +1,109 @@ +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index d9d6616f02a4..501804fe4ae7 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -294,6 +294,24 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif ++ + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: board private structure +@@ -313,6 +331,18 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -929,6 +959,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + +@@ -1604,6 +1644,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1791,6 +1835,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4143,6 +4191,10 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + break; + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -4180,6 +4232,10 @@ static void ixgbevf_remove(struct pci_dev *pdev) + if (!netdev) + return; + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + adapter = netdev_priv(netdev); + + set_bit(__IXGBEVF_REMOVING, &adapter->state); diff --git a/LINUX/final-patches/vanilla--ixgbevf--40900--41100 b/LINUX/final-patches/vanilla--ixgbevf--40900--41100 new file mode 100644 index 000000000..5d1f05e9a --- /dev/null +++ b/LINUX/final-patches/vanilla--ixgbevf--40900--41100 @@ -0,0 +1,118 @@ +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index cbf70fe4028a..724ea090a557 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -294,6 +294,24 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif ++ + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: board private structure +@@ -313,6 +331,18 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -929,6 +959,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + u16 cleaned_count = ixgbevf_desc_unused(rx_ring); + struct sk_buff *skb = rx_ring->skb; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + while (likely(total_rx_packets < budget)) { + union ixgbe_adv_rx_desc *rx_desc; + +@@ -1604,6 +1644,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1612,7 +1656,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + txdctl = IXGBE_READ_REG(hw, IXGBE_VFTXDCTL(reg_idx)); + } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); + if (!wait_loop) +- hw_dbg(hw, "Could not enable Tx Queue %d\n", reg_idx); ++ pr_err("Could not enable Tx Queue %d\n", reg_idx); + } + + /** +@@ -1791,6 +1835,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4152,6 +4200,10 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + break; + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -4191,6 +4243,10 @@ static void ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + diff --git a/LINUX/final-patches/vanilla--ixgbevf--41100--50800 b/LINUX/final-patches/vanilla--ixgbevf--41100--50800 new file mode 100644 index 000000000..81d73c84e --- /dev/null +++ b/LINUX/final-patches/vanilla--ixgbevf--41100--50800 @@ -0,0 +1,118 @@ +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 850f8af95e49..14168d3a1ed7 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -279,6 +279,24 @@ static void ixgbevf_tx_timeout(struct net_device *netdev) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif ++ + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: board private structure +@@ -298,6 +316,18 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1117,6 +1147,16 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ ++ + xdp.rxq = &rx_ring->xdp_rxq; + + while (likely(total_rx_packets < budget)) { +@@ -1712,6 +1752,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1720,7 +1764,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + txdctl = IXGBE_READ_REG(hw, IXGBE_VFTXDCTL(reg_idx)); + } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); + if (!wait_loop) +- hw_dbg(hw, "Could not enable Tx Queue %d\n", reg_idx); ++ pr_err("Could not enable Tx Queue %d\n", reg_idx); + } + + /** +@@ -1946,6 +1990,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4688,6 +4736,10 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + break; + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -4728,6 +4780,10 @@ static void ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + diff --git a/LINUX/final-patches/vanilla--ixgbevf--50800--50c00 b/LINUX/final-patches/vanilla--ixgbevf--50800--50c00 new file mode 100644 index 000000000..57e847f7a --- /dev/null +++ b/LINUX/final-patches/vanilla--ixgbevf--50800--50c00 @@ -0,0 +1,127 @@ +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index a39e2cb384dd..e75f700647c7 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -257,6 +257,24 @@ static void ixgbevf_tx_timeout(struct net_device *netdev, unsigned int txqueue) + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif ++ + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: board private structure +@@ -276,6 +294,18 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1135,13 +1165,24 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + +- xdp.rxq = &rx_ring->xdp_rxq; ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; + + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ ++#endif /* DEV_NETMAP */ ++ + #if (PAGE_SIZE < 8192) + xdp.frame_sz = ixgbevf_rx_frame_truesize(rx_ring, 0); + #endif + ++ xdp.rxq = &rx_ring->xdp_rxq; ++ + while (likely(total_rx_packets < budget)) { + struct ixgbevf_rx_buffer *rx_buffer; + union ixgbe_adv_rx_desc *rx_desc; +@@ -1746,6 +1787,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1754,7 +1799,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + txdctl = IXGBE_READ_REG(hw, IXGBE_VFTXDCTL(reg_idx)); + } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); + if (!wait_loop) +- hw_dbg(hw, "Could not enable Tx Queue %d\n", reg_idx); ++ pr_err("Could not enable Tx Queue %d\n", reg_idx); + } + + /** +@@ -1980,6 +2025,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4737,6 +4786,10 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + break; + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -4777,6 +4830,10 @@ static void ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + diff --git a/LINUX/final-patches/vanilla--ixgbevf--50c00--99999 b/LINUX/final-patches/vanilla--ixgbevf--50c00--99999 new file mode 100644 index 000000000..73ed1647d --- /dev/null +++ b/LINUX/final-patches/vanilla--ixgbevf--50c00--99999 @@ -0,0 +1,126 @@ +diff --git a/ixgbevf/ixgbevf_main.c b/ixgbevf/ixgbevf_main.c +index 449d7d5b280d..d5d04e1f6429 100644 +--- a/ixgbevf/ixgbevf_main.c ++++ b/ixgbevf/ixgbevf_main.c +@@ -255,6 +255,24 @@ static void ixgbevf_tx_timeout(struct net_device *netdev, unsigned int __always_ + ixgbevf_tx_timeout_reset(adapter); + } + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++/* ++ * The #ifdef DEV_NETMAP / #endif blocks in this file are meant to ++ * be a reference on how to implement netmap support in a driver. ++ * Additional comments are in ixgbe_netmap_linux.h . ++ * ++ * The code is originally developed on FreeBSD and in the interest ++ * of maintainability we try to limit differences between the two systems. ++ * ++ * contains functions for netmap support ++ * that extend the standard driver. ++ * It also defines DEV_NETMAP so further conditional sections use ++ * that instead of CONFIG_NETMAP ++ */ ++#define NM_IXGBEVF ++#include ++#endif ++ + /** + * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: board private structure +@@ -274,6 +292,18 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return true; + ++#ifdef DEV_NETMAP ++ /* ++ * In netmap mode, all the work is done in the context ++ * of the client thread. Interrupt handlers only wake up ++ * clients, which may be sleeping on individual rings ++ * or on a global resource for all rings. ++ */ ++ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) ++ return true; ++#endif /* DEV_NETMAP */ ++ ++ + tx_buffer = &tx_ring->tx_buffer_info[i]; + tx_desc = IXGBEVF_TX_DESC(tx_ring, i); + i -= tx_ring->count; +@@ -1123,12 +1153,24 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + bool xdp_xmit = false; + struct xdp_buff xdp; + ++#ifdef DEV_NETMAP ++ /* ++ * Same as the txeof routine: only wakeup clients on intr. ++ */ ++ int dummy, nm_irq; ++ nm_irq = netmap_rx_irq(rx_ring->netdev, rx_ring->queue_index, &dummy); ++ if (nm_irq != NM_IRQ_PASS) ++ return (nm_irq == NM_IRQ_RESCHED) ? budget : 1; ++#endif /* DEV_NETMAP */ + /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */ ++ + #if (PAGE_SIZE < 8192) + frame_sz = ixgbevf_rx_frame_truesize(rx_ring, 0); + #endif + xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq); + ++ xdp.rxq = &rx_ring->xdp_rxq; ++ + while (likely(total_rx_packets < budget)) { + struct ixgbevf_rx_buffer *rx_buffer; + union ixgbe_adv_rx_desc *rx_desc; +@@ -1733,6 +1775,10 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + clear_bit(__IXGBEVF_TX_XDP_RING_PRIMED, &ring->state); + ++#ifdef DEV_NETMAP ++ txdctl = ixgbe_netmap_configure_tx_ring(adapter, reg_idx, txdctl); ++#endif /* DEV_NETMAP */ ++ + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); + + /* poll to verify queue is enabled */ +@@ -1741,7 +1787,7 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, + txdctl = IXGBE_READ_REG(hw, IXGBE_VFTXDCTL(reg_idx)); + } while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE)); + if (!wait_loop) +- hw_dbg(hw, "Could not enable Tx Queue %d\n", reg_idx); ++ pr_err("Could not enable Tx Queue %d\n", reg_idx); + } + + /** +@@ -1967,6 +2013,10 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); + + ixgbevf_rx_desc_queue_enable(adapter, ring); ++#ifdef DEV_NETMAP ++ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) ++ return; ++#endif /* DEV_NETMAP */ + ixgbevf_alloc_rx_buffers(ring, ixgbevf_desc_unused(ring)); + } + +@@ -4679,6 +4729,10 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) + break; + } + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + return 0; + + err_register: +@@ -4719,6 +4773,10 @@ static void ixgbevf_remove(struct pci_dev *pdev) + + adapter = netdev_priv(netdev); + ++#ifdef DEV_NETMAP ++ ixgbe_netmap_detach(adapter); ++#endif /* DEV_NETMAP */ ++ + set_bit(__IXGBEVF_REMOVING, &adapter->state); + cancel_work_sync(&adapter->service_task); + diff --git a/LINUX/final-patches/vanilla--r8169.c--20620--20625 b/LINUX/final-patches/vanilla--r8169.c--20620--20625 index d4a8f3781..20b7a6018 100644 --- a/LINUX/final-patches/vanilla--r8169.c--20620--20625 +++ b/LINUX/final-patches/vanilla--r8169.c--20620--20625 @@ -1,5 +1,5 @@ diff --git a/r8169.c b/r8169.c -index 0fe2fc9..5d363e5 100644 +index 0fe2fc90f207..5d363e589803 100644 --- a/r8169.c +++ b/r8169.c @@ -537,6 +537,10 @@ static int rtl8169_poll(struct napi_struct *napi, int budget); diff --git a/LINUX/final-patches/vanilla--r8169.c--20625--20626 b/LINUX/final-patches/vanilla--r8169.c--20625--20626 index fe419354f..e5cf077e7 100644 --- a/LINUX/final-patches/vanilla--r8169.c--20625--20626 +++ b/LINUX/final-patches/vanilla--r8169.c--20625--20626 @@ -1,5 +1,5 @@ diff --git a/r8169.c b/r8169.c -index 53b13de..ced4849 100644 +index 53b13deade95..ced4849577f0 100644 --- a/r8169.c +++ b/r8169.c @@ -535,6 +535,10 @@ static int rtl8169_poll(struct napi_struct *napi, int budget); diff --git a/LINUX/final-patches/vanilla--r8169.c--20626--30400 b/LINUX/final-patches/vanilla--r8169.c--20626--30400 index 5e6d6473a..a1b9eb6f6 100644 --- a/LINUX/final-patches/vanilla--r8169.c--20626--30400 +++ b/LINUX/final-patches/vanilla--r8169.c--20626--30400 @@ -1,5 +1,5 @@ diff --git a/r8169.c b/r8169.c -index 7ffdb80..fc92723 100644 +index 7ffdb80adf40..fc9272305d02 100644 --- a/r8169.c +++ b/r8169.c @@ -590,6 +590,10 @@ static int rtl8169_poll(struct napi_struct *napi, int budget); diff --git a/LINUX/final-patches/vanilla--stmmac--40900--40a00 b/LINUX/final-patches/vanilla--stmmac--40900--40a00 new file mode 100644 index 000000000..4be27cd03 --- /dev/null +++ b/LINUX/final-patches/vanilla--stmmac--40900--40a00 @@ -0,0 +1,132 @@ +diff --git a/stmmac/stmmac_main.c b/stmmac/stmmac_main.c +index caf069a465f2..a880c2c5c0e0 100644 +--- a/stmmac/stmmac_main.c ++++ b/stmmac/stmmac_main.c +@@ -121,6 +121,9 @@ static void stmmac_exit_fs(struct net_device *dev); + + #define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x)) + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif + /** + * stmmac_verify_args - verify the driver parameters. + * Description: it checks the driver parameters and set a default in case of +@@ -472,7 +475,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) + /* PTP v1, UDP, any kind of event packet */ + config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; + /* take time stamp for all event messages */ +- snap_type_sel = PTP_TCR_SNAPTYPSEL_1; ++ snap_type_sel = 0xFFFFFFFF & PTP_TCR_SNAPTYPSEL_1; + + ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +@@ -504,7 +507,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; + ptp_v2 = PTP_TCR_TSVER2ENA; + /* take time stamp for all event messages */ +- snap_type_sel = PTP_TCR_SNAPTYPSEL_1; ++ snap_type_sel = 0xFFFFFFFF & PTP_TCR_SNAPTYPSEL_1; + + ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +@@ -538,7 +541,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + ptp_v2 = PTP_TCR_TSVER2ENA; + /* take time stamp for all event messages */ +- snap_type_sel = PTP_TCR_SNAPTYPSEL_1; ++ snap_type_sel = 0xFFFFFFFF & PTP_TCR_SNAPTYPSEL_1; + + ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +@@ -1038,6 +1041,23 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) + /* RX INITIALIZATION */ + pr_debug("\tSKB addresses:\nskb\t\tskb data\tdma data\n"); + } ++ ++#ifdef DEV_NETMAP ++ if (stmmac_netmap_rx_init(priv)) { ++ priv->cur_rx = 0; ++ priv->dirty_rx = 1; ++ buf_sz = bfsize; ++ ++ if (stmmac_netmap_tx_init(priv)) { ++ priv->dirty_tx = 0; ++ priv->cur_tx = 0; ++ netdev_reset_queue(priv->dev); ++ return 0; ++ } ++ } ++ ++#endif /* DEV_NETMAP */ ++ + for (i = 0; i < DMA_RX_SIZE; i++) { + struct dma_desc *p; + if (priv->extend_desc) +@@ -1307,6 +1327,11 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) + unsigned int bytes_compl = 0, pkts_compl = 0; + unsigned int entry = priv->dirty_tx; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(priv->dev, 0)) ++ return; ++#endif /* DEV_NETMAP */ ++ + spin_lock(&priv->tx_lock); + + priv->xstats.tx_clean++; +@@ -1745,7 +1770,7 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) + } + + if (priv->hw->pcs && priv->hw->mac->pcs_ctrl_ane) +- priv->hw->mac->pcs_ctrl_ane(priv->hw, 1, priv->hw->ps, 0); ++ priv->hw->mac->pcs_ctrl_ane((void __iomem *)priv->hw, 1, priv->hw->ps, 0); + + /* set TX ring length */ + if (priv->hw->dma->set_tx_ring_len) +@@ -2481,6 +2506,11 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) + unsigned int count = 0; + int coe = priv->hw->rx_csum; + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(priv->dev, 0, &count)) ++ return count; ++#endif /* DEV_NETMAP */ ++ + if (netif_msg_rx_status(priv)) { + void *rx_head; + +@@ -3380,6 +3410,10 @@ int stmmac_dvr_probe(struct device *device, + } + } + ++#ifdef DEV_NETMAP ++ stmmac_netmap_attach(priv); ++#endif /* DEV_NETMAP */ ++ + return 0; + + error_mdio_register: +@@ -3424,6 +3458,11 @@ int stmmac_dvr_remove(struct device *dev) + priv->hw->pcs != STMMAC_PCS_TBI && + priv->hw->pcs != STMMAC_PCS_RTBI) + stmmac_mdio_unregister(ndev); ++ ++#ifdef DEV_NETMAP ++ netmap_detach(ndev); ++#endif /* DEV_NETMAP */ ++ + free_netdev(ndev); + + return 0; +@@ -3621,8 +3660,8 @@ static void __exit stmmac_exit(void) + #endif + } + +-module_init(stmmac_init) +-module_exit(stmmac_exit) ++module_init(stmmac_init); ++module_exit(stmmac_exit); + + MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet device driver"); + MODULE_AUTHOR("Giuseppe Cavallaro "); diff --git a/LINUX/final-patches/vanilla--stmmac--40a00--40c00 b/LINUX/final-patches/vanilla--stmmac--40a00--40c00 new file mode 100644 index 000000000..176cb2f86 --- /dev/null +++ b/LINUX/final-patches/vanilla--stmmac--40a00--40c00 @@ -0,0 +1,131 @@ +diff --git a/stmmac/stmmac_main.c b/stmmac/stmmac_main.c +index e3f6389e1b01..303d9f03a985 100644 +--- a/stmmac/stmmac_main.c ++++ b/stmmac/stmmac_main.c +@@ -121,6 +121,9 @@ static void stmmac_exit_fs(struct net_device *dev); + + #define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x)) + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif + /** + * stmmac_verify_args - verify the driver parameters. + * Description: it checks the driver parameters and set a default in case of +@@ -474,7 +477,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) + /* PTP v1, UDP, any kind of event packet */ + config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; + /* take time stamp for all event messages */ +- snap_type_sel = PTP_TCR_SNAPTYPSEL_1; ++ snap_type_sel = 0xFFFFFFFF & PTP_TCR_SNAPTYPSEL_1; + + ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +@@ -506,7 +509,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; + ptp_v2 = PTP_TCR_TSVER2ENA; + /* take time stamp for all event messages */ +- snap_type_sel = PTP_TCR_SNAPTYPSEL_1; ++ snap_type_sel = 0xFFFFFFFF & PTP_TCR_SNAPTYPSEL_1; + + ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +@@ -540,7 +543,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + ptp_v2 = PTP_TCR_TSVER2ENA; + /* take time stamp for all event messages */ +- snap_type_sel = PTP_TCR_SNAPTYPSEL_1; ++ snap_type_sel = 0xFFFFFFFF & PTP_TCR_SNAPTYPSEL_1; + + ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +@@ -1040,6 +1043,22 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) + netif_dbg(priv, probe, priv->dev, + "SKB addresses:\nskb\t\tskb data\tdma data\n"); + ++#ifdef DEV_NETMAP ++ if (stmmac_netmap_rx_init(priv)) { ++ priv->cur_rx = 0; ++ priv->dirty_rx = 1; ++ buf_sz = bfsize; ++ ++ if (stmmac_netmap_tx_init(priv)) { ++ priv->dirty_tx = 0; ++ priv->cur_tx = 0; ++ netdev_reset_queue(priv->dev); ++ return 0; ++ } ++ } ++ ++#endif /* DEV_NETMAP */ ++ + for (i = 0; i < DMA_RX_SIZE; i++) { + struct dma_desc *p; + if (priv->extend_desc) +@@ -1308,6 +1327,11 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) + unsigned int bytes_compl = 0, pkts_compl = 0; + unsigned int entry = priv->dirty_tx; + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(priv->dev, 0)) ++ return; ++#endif /* DEV_NETMAP */ ++ + netif_tx_lock(priv->dev); + + priv->xstats.tx_clean++; +@@ -1739,7 +1763,7 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) + } + + if (priv->hw->pcs && priv->hw->mac->pcs_ctrl_ane) +- priv->hw->mac->pcs_ctrl_ane(priv->hw, 1, priv->hw->ps, 0); ++ priv->hw->mac->pcs_ctrl_ane((void __iomem *)priv->hw, 1, priv->hw->ps, 0); + + /* set TX ring length */ + if (priv->hw->dma->set_tx_ring_len) +@@ -2471,6 +2495,11 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) + unsigned int count = 0; + int coe = priv->hw->rx_csum; + ++#ifdef DEV_NETMAP ++ if (netmap_rx_irq(priv->dev, 0, &count)) ++ return count; ++#endif /* DEV_NETMAP */ ++ + if (netif_msg_rx_status(priv)) { + void *rx_head; + +@@ -3381,6 +3410,10 @@ int stmmac_dvr_probe(struct device *device, + goto error_netdev_register; + } + ++#ifdef DEV_NETMAP ++ stmmac_netmap_attach(priv); ++#endif /* DEV_NETMAP */ ++ + return ret; + + error_netdev_register: +@@ -3428,6 +3461,11 @@ int stmmac_dvr_remove(struct device *dev) + priv->hw->pcs != STMMAC_PCS_TBI && + priv->hw->pcs != STMMAC_PCS_RTBI) + stmmac_mdio_unregister(ndev); ++ ++#ifdef DEV_NETMAP ++ netmap_detach(ndev); ++#endif /* DEV_NETMAP */ ++ + free_netdev(ndev); + + return 0; +@@ -3625,8 +3663,8 @@ static void __exit stmmac_exit(void) + #endif + } + +-module_init(stmmac_init) +-module_exit(stmmac_exit) ++module_init(stmmac_init); ++module_exit(stmmac_exit); + + MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet device driver"); + MODULE_AUTHOR("Giuseppe Cavallaro "); diff --git a/LINUX/final-patches/vanilla--veth.c--20620--30900 b/LINUX/final-patches/vanilla--veth.c--20620--30900 deleted file mode 100644 index fffae7d65..000000000 --- a/LINUX/final-patches/vanilla--veth.c--20620--30900 +++ /dev/null @@ -1,35 +0,0 @@ -diff --git a/veth.c b/veth.c -index 52af501..a416e43 100644 ---- a/veth.c -+++ b/veth.c -@@ -38,6 +38,10 @@ struct veth_priv { - unsigned ip_summed; - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - /* - * ethtool interface - */ -@@ -284,6 +288,9 @@ static int veth_dev_init(struct net_device *dev) - - priv = netdev_priv(dev); - priv->stats = stats; -+#ifdef DEV_NETMAP -+ veth_netmap_attach(dev); -+#endif /* DEV_NETMAP */ - return 0; - } - -@@ -291,6 +298,9 @@ static void veth_dev_free(struct net_device *dev) - { - struct veth_priv *priv; - -+#ifdef DEV_NETMAP -+ netmap_detach(dev); -+#endif /* DEV_NETMAP */ - priv = netdev_priv(dev); - free_percpu(priv->stats); - free_netdev(dev); diff --git a/LINUX/final-patches/vanilla--veth.c--30f00--99999 b/LINUX/final-patches/vanilla--veth.c--40000--41300 similarity index 94% rename from LINUX/final-patches/vanilla--veth.c--30f00--99999 rename to LINUX/final-patches/vanilla--veth.c--40000--41300 index 0e17d9ca0..bdd96ac12 100644 --- a/LINUX/final-patches/vanilla--veth.c--30f00--99999 +++ b/LINUX/final-patches/vanilla--veth.c--40000--41300 @@ -1,5 +1,5 @@ diff --git a/veth.c b/veth.c -index b4a10bc..52b7c37 100644 +index 4cca36ebc4fb..54502397b5ac 100644 --- a/veth.c +++ b/veth.c @@ -37,6 +37,10 @@ struct veth_priv { diff --git a/LINUX/final-patches/vanilla--veth.c--30900--30f00 b/LINUX/final-patches/vanilla--veth.c--41300--41400 similarity index 54% rename from LINUX/final-patches/vanilla--veth.c--30900--30f00 rename to LINUX/final-patches/vanilla--veth.c--41300--41400 index e2ca5b885..6d1e573bf 100644 --- a/LINUX/final-patches/vanilla--veth.c--30900--30f00 +++ b/LINUX/final-patches/vanilla--veth.c--41300--41400 @@ -1,9 +1,9 @@ diff --git a/veth.c b/veth.c -index 07a4af0..672375e 100644 +index 41a00cd76955..ce884d392a01 100644 --- a/veth.c +++ b/veth.c -@@ -36,6 +36,10 @@ struct veth_priv { - atomic64_t dropped; +@@ -60,6 +60,10 @@ struct veth_priv { + unsigned int requested_headroom; }; +#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) @@ -13,9 +13,17 @@ index 07a4af0..672375e 100644 /* * ethtool interface */ -@@ -234,11 +238,17 @@ static int veth_dev_init(struct net_device *dev) - if (!dev->vstats) - return -ENOMEM; +@@ -765,7 +769,6 @@ static int veth_open(struct net_device *dev) + netif_carrier_on(dev); + netif_carrier_on(peer); + } +- + return 0; + } + +@@ -825,12 +828,18 @@ static int veth_dev_init(struct net_device *dev) + return err; + } +#ifdef DEV_NETMAP + veth_netmap_attach(dev); @@ -25,9 +33,10 @@ index 07a4af0..672375e 100644 static void veth_dev_free(struct net_device *dev) { + veth_free_queues(dev); +#ifdef DEV_NETMAP + netmap_detach(dev); +#endif /* DEV_NETMAP */ free_percpu(dev->vstats); - free_netdev(dev); } + diff --git a/LINUX/final-patches/vanilla--veth.c--41400--60700 b/LINUX/final-patches/vanilla--veth.c--41400--60700 new file mode 100644 index 000000000..7b3442eba --- /dev/null +++ b/LINUX/final-patches/vanilla--veth.c--41400--60700 @@ -0,0 +1,42 @@ +diff --git a/veth.c b/veth.c +index 890fa5b905e2..ad8f96f1a6a8 100644 +--- a/veth.c ++++ b/veth.c +@@ -63,6 +63,10 @@ struct veth_priv { + unsigned int requested_headroom; + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /* + * ethtool interface + */ +@@ -891,7 +895,6 @@ static int veth_open(struct net_device *dev) + netif_carrier_on(dev); + netif_carrier_on(peer); + } +- + return 0; + } + +@@ -953,12 +956,18 @@ static int veth_dev_init(struct net_device *dev) + return err; + } + ++#ifdef DEV_NETMAP ++ veth_netmap_attach(dev); ++#endif /* DEV_NETMAP */ + return 0; + } + + static void veth_dev_free(struct net_device *dev) + { + veth_free_queues(dev); ++#ifdef DEV_NETMAP ++ netmap_detach(dev); ++#endif /* DEV_NETMAP */ + free_percpu(dev->lstats); + } + diff --git a/LINUX/final-patches/vanilla--veth.c--60700--99999 b/LINUX/final-patches/vanilla--veth.c--60700--99999 new file mode 100644 index 000000000..1324a96dc --- /dev/null +++ b/LINUX/final-patches/vanilla--veth.c--60700--99999 @@ -0,0 +1,37 @@ +diff --git a/veth.c b/veth.c +index 977861c46b1f..22ec126909ea 100644 +--- a/veth.c ++++ b/veth.c +@@ -82,6 +82,10 @@ struct veth_xdp_tx_bq { + unsigned int count; + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + /* + * ethtool interface + */ +@@ -1487,6 +1491,10 @@ static int veth_alloc_queues(struct net_device *dev) + u64_stats_init(&priv->rq[i].stats.syncp); + } + ++#ifdef DEV_NETMAP ++ veth_netmap_attach(dev); ++#endif /* DEV_NETMAP */ ++ + return 0; + } + +@@ -1505,6 +1513,10 @@ static int veth_dev_init(struct net_device *dev) + static void veth_dev_free(struct net_device *dev) + { + veth_free_queues(dev); ++ ++#ifdef DEV_NETMAP ++ netmap_detach(dev); ++#endif /* DEV_NETMAP */ + } + + #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/LINUX/final-patches/vanilla--virtio_net.c--20622--20625 b/LINUX/final-patches/vanilla--virtio_net.c--20622--20625 deleted file mode 100644 index 775493f38..000000000 --- a/LINUX/final-patches/vanilla--virtio_net.c--20622--20625 +++ /dev/null @@ -1,80 +0,0 @@ -diff --git a/virtio_net.c b/virtio_net.c -index b0577dd..0c873c4 100644 ---- a/virtio_net.c -+++ b/virtio_net.c -@@ -64,6 +64,10 @@ struct virtnet_info - struct page *pages; - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - struct skb_vnet_hdr { - union { - struct virtio_net_hdr hdr; -@@ -121,6 +125,10 @@ static void skb_xmit_done(struct virtqueue *svq) - /* Suppress further interrupts. */ - svq->vq_ops->disable_cb(svq); - -+#ifdef DEV_NETMAP -+ if (netmap_tx_irq(vi->dev, 0)) -+ return; -+#endif - /* We were probably waiting for more output buffers. */ - netif_wake_queue(vi->dev); - } -@@ -470,7 +478,17 @@ static int virtnet_poll(struct napi_struct *napi, int budget) - struct virtnet_info *vi = container_of(napi, struct virtnet_info, napi); - void *buf; - unsigned int len, received = 0; -+#ifdef DEV_NETMAP -+ int work_done = 0; -+ int nm_irq = netmap_rx_irq(vi->dev, 0, &work_done); - -+ if (nm_irq == NM_IRQ_COMPLETED) { -+ napi_complete(napi); -+ return 1; -+ } else if (nm_irq == NM_IRQ_RESCHED) { -+ return budget; -+ } -+#endif - again: - while (received < budget && - (buf = vi->rvq->vq_ops->get_buf(vi->rvq, &len)) != NULL) { -@@ -638,6 +656,9 @@ static int virtnet_open(struct net_device *dev) - { - struct virtnet_info *vi = netdev_priv(dev); - -+#ifdef DEV_NETMAP -+ virtio_netmap_init_buffers(vi); -+#endif - napi_enable(&vi->napi); - - /* If all buffers were filled by other side before we napi_enabled, we -@@ -985,6 +1006,10 @@ static int virtnet_probe(struct virtio_device *vdev) - goto unregister; - } - -+#ifdef DEV_NETMAP -+ virtio_netmap_attach(vi); -+#endif -+ - vi->status = VIRTIO_NET_S_LINK_UP; - virtnet_update_status(vi); - netif_carrier_on(dev); -@@ -1027,7 +1052,14 @@ static void free_unused_bufs(struct virtnet_info *vi) - static void __devexit virtnet_remove(struct virtio_device *vdev) - { - struct virtnet_info *vi = vdev->priv; -+#ifdef DEV_NETMAP -+ /* Save the pointer, will go away after netmap_detach(). */ -+ struct netmap_adapter *token = NA(vi->dev); - -+ netmap_detach(vi->dev); -+ virtio_netmap_clean_used_rings(vi, token); -+ virtio_netmap_reclaim_unused(vi); -+#endif - /* Stop all the virtqueues. */ - vdev->config->reset(vdev); - diff --git a/LINUX/final-patches/vanilla--virtio_net.c--20625--20626 b/LINUX/final-patches/vanilla--virtio_net.c--20625--20626 deleted file mode 100644 index 2e013c912..000000000 --- a/LINUX/final-patches/vanilla--virtio_net.c--20625--20626 +++ /dev/null @@ -1,80 +0,0 @@ -diff --git a/virtio_net.c b/virtio_net.c -index b6d4028..60bb2b2 100644 ---- a/virtio_net.c -+++ b/virtio_net.c -@@ -67,6 +67,10 @@ struct virtnet_info { - struct scatterlist tx_sg[MAX_SKB_FRAGS + 2]; - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - struct skb_vnet_hdr { - union { - struct virtio_net_hdr hdr; -@@ -124,6 +128,10 @@ static void skb_xmit_done(struct virtqueue *svq) - /* Suppress further interrupts. */ - virtqueue_disable_cb(svq); - -+#ifdef DEV_NETMAP -+ if (netmap_tx_irq(vi->dev, 0)) -+ return; -+#endif - /* We were probably waiting for more output buffers. */ - netif_wake_queue(vi->dev); - } -@@ -467,7 +475,17 @@ static int virtnet_poll(struct napi_struct *napi, int budget) - struct virtnet_info *vi = container_of(napi, struct virtnet_info, napi); - void *buf; - unsigned int len, received = 0; -+#ifdef DEV_NETMAP -+ int work_done = 0; -+ int nm_irq = netmap_rx_irq(vi->dev, 0, &work_done); - -+ if (nm_irq == NM_IRQ_COMPLETED) { -+ napi_complete(napi); -+ return 1; -+ } else if (nm_irq == NM_IRQ_RESCHED) { -+ return budget; -+ } -+#endif - again: - while (received < budget && - (buf = virtqueue_get_buf(vi->rvq, &len)) != NULL) { -@@ -638,6 +656,9 @@ static int virtnet_open(struct net_device *dev) - { - struct virtnet_info *vi = netdev_priv(dev); - -+#ifdef DEV_NETMAP -+ virtio_netmap_init_buffers(vi); -+#endif - napi_enable(&vi->napi); - - /* If all buffers were filled by other side before we napi_enabled, we -@@ -986,6 +1007,10 @@ static int virtnet_probe(struct virtio_device *vdev) - goto unregister; - } - -+#ifdef DEV_NETMAP -+ virtio_netmap_attach(vi); -+#endif -+ - /* Assume link up if device can't report link status, - otherwise get link status from config. */ - if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { -@@ -1034,7 +1059,14 @@ static void free_unused_bufs(struct virtnet_info *vi) - static void __devexit virtnet_remove(struct virtio_device *vdev) - { - struct virtnet_info *vi = vdev->priv; -+#ifdef DEV_NETMAP -+ /* Save the pointer, will go away after netmap_detach(). */ -+ struct netmap_adapter *token = NA(vi->dev); - -+ netmap_detach(vi->dev); -+ virtio_netmap_clean_used_rings(vi, token); -+ virtio_netmap_reclaim_unused(vi); -+#endif - /* Stop all the virtqueues. */ - vdev->config->reset(vdev); - diff --git a/LINUX/final-patches/vanilla--virtio_net.c--20626--30300 b/LINUX/final-patches/vanilla--virtio_net.c--20626--30300 deleted file mode 100644 index 03ad70378..000000000 --- a/LINUX/final-patches/vanilla--virtio_net.c--20626--30300 +++ /dev/null @@ -1,80 +0,0 @@ -diff --git a/virtio_net.c b/virtio_net.c -index 82dba5a..06324f8 100644 ---- a/virtio_net.c -+++ b/virtio_net.c -@@ -67,6 +67,10 @@ struct virtnet_info { - struct scatterlist tx_sg[MAX_SKB_FRAGS + 2]; - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - struct skb_vnet_hdr { - union { - struct virtio_net_hdr hdr; -@@ -124,6 +128,10 @@ static void skb_xmit_done(struct virtqueue *svq) - /* Suppress further interrupts. */ - virtqueue_disable_cb(svq); - -+#ifdef DEV_NETMAP -+ if (netmap_tx_irq(vi->dev, 0)) -+ return; -+#endif - /* We were probably waiting for more output buffers. */ - netif_wake_queue(vi->dev); - } -@@ -481,7 +489,17 @@ static int virtnet_poll(struct napi_struct *napi, int budget) - struct virtnet_info *vi = container_of(napi, struct virtnet_info, napi); - void *buf; - unsigned int len, received = 0; -+#ifdef DEV_NETMAP -+ int work_done = 0; -+ int nm_irq = netmap_rx_irq(vi->dev, 0, &work_done); - -+ if (nm_irq == NM_IRQ_COMPLETED) { -+ napi_complete(napi); -+ return 1; -+ } else if (nm_irq == NM_IRQ_RESCHED) { -+ return budget; -+ } -+#endif - again: - while (received < budget && - (buf = virtqueue_get_buf(vi->rvq, &len)) != NULL) { -@@ -652,6 +670,9 @@ static int virtnet_open(struct net_device *dev) - { - struct virtnet_info *vi = netdev_priv(dev); - -+#ifdef DEV_NETMAP -+ virtio_netmap_init_buffers(vi); -+#endif - virtnet_napi_enable(vi); - return 0; - } -@@ -991,6 +1012,10 @@ static int virtnet_probe(struct virtio_device *vdev) - goto unregister; - } - -+#ifdef DEV_NETMAP -+ virtio_netmap_attach(vi); -+#endif -+ - /* Assume link up if device can't report link status, - otherwise get link status from config. */ - if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { -@@ -1039,7 +1064,14 @@ static void free_unused_bufs(struct virtnet_info *vi) - static void __devexit virtnet_remove(struct virtio_device *vdev) - { - struct virtnet_info *vi = vdev->priv; -+#ifdef DEV_NETMAP -+ /* Save the pointer, will go away after netmap_detach(). */ -+ struct netmap_adapter *token = NA(vi->dev); - -+ netmap_detach(vi->dev); -+ virtio_netmap_clean_used_rings(vi, token); -+ virtio_netmap_reclaim_unused(vi); -+#endif - /* Stop all the virtqueues. */ - vdev->config->reset(vdev); - diff --git a/LINUX/final-patches/vanilla--virtio_net.c--30300--30500 b/LINUX/final-patches/vanilla--virtio_net.c--30300--30500 deleted file mode 100644 index deba61c7e..000000000 --- a/LINUX/final-patches/vanilla--virtio_net.c--30300--30500 +++ /dev/null @@ -1,85 +0,0 @@ -diff --git a/virtio_net.c b/virtio_net.c -index 4880aa8..64e3625 100644 ---- a/virtio_net.c -+++ b/virtio_net.c -@@ -80,6 +80,10 @@ struct virtnet_info { - struct scatterlist tx_sg[MAX_SKB_FRAGS + 2]; - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - struct skb_vnet_hdr { - union { - struct virtio_net_hdr hdr; -@@ -137,6 +141,10 @@ static void skb_xmit_done(struct virtqueue *svq) - /* Suppress further interrupts. */ - virtqueue_disable_cb(svq); - -+#ifdef DEV_NETMAP -+ if (netmap_tx_irq(vi->dev, 0)) -+ return; -+#endif - /* We were probably waiting for more output buffers. */ - netif_wake_queue(vi->dev); - } -@@ -517,7 +525,17 @@ static int virtnet_poll(struct napi_struct *napi, int budget) - struct virtnet_info *vi = container_of(napi, struct virtnet_info, napi); - void *buf; - unsigned int len, received = 0; -+#ifdef DEV_NETMAP -+ int work_done = 0; -+ int nm_irq = netmap_rx_irq(vi->dev, 0, &work_done); - -+ if (nm_irq == NM_IRQ_COMPLETED) { -+ napi_complete(napi); -+ return 1; -+ } else if (nm_irq == NM_IRQ_RESCHED) { -+ return budget; -+ } -+#endif - again: - while (received < budget && - (buf = virtqueue_get_buf(vi->rvq, &len)) != NULL) { -@@ -727,7 +745,14 @@ static void virtnet_netpoll(struct net_device *dev) - static int virtnet_open(struct net_device *dev) - { - struct virtnet_info *vi = netdev_priv(dev); -+#ifdef DEV_NETMAP -+ int ok = virtio_netmap_init_buffers(vi); - -+ if (ok) { -+ virtnet_napi_enable(vi); -+ return 0; -+ } -+#endif - /* Make sure we have some buffers: if oom use wq. */ - if (!try_fill_recv(vi, GFP_KERNEL)) - queue_delayed_work(system_nrt_wq, &vi->refill, 0); -@@ -1107,6 +1132,10 @@ static int virtnet_probe(struct virtio_device *vdev) - goto unregister; - } - -+#ifdef DEV_NETMAP -+ virtio_netmap_attach(vi); -+#endif -+ - /* Assume link up if device can't report link status, - otherwise get link status from config. */ - if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { -@@ -1169,7 +1198,14 @@ static void remove_vq_common(struct virtnet_info *vi) - static void __devexit virtnet_remove(struct virtio_device *vdev) - { - struct virtnet_info *vi = vdev->priv; -+#ifdef DEV_NETMAP -+ /* Save the pointer, will go away after netmap_detach(). */ -+ struct netmap_adapter *token = NA(vi->dev); - -+ netmap_detach(vi->dev); -+ virtio_netmap_clean_used_rings(vi, token); -+ virtio_netmap_reclaim_unused(vi); -+#endif - unregister_netdev(vi->dev); - - remove_vq_common(vi); diff --git a/LINUX/final-patches/vanilla--virtio_net.c--30500--30800 b/LINUX/final-patches/vanilla--virtio_net.c--30500--30800 deleted file mode 100644 index 91c23fba3..000000000 --- a/LINUX/final-patches/vanilla--virtio_net.c--30500--30800 +++ /dev/null @@ -1,85 +0,0 @@ -diff --git a/virtio_net.c b/virtio_net.c -index f18149a..cc935cf 100644 ---- a/virtio_net.c -+++ b/virtio_net.c -@@ -90,6 +90,10 @@ struct virtnet_info { - struct scatterlist tx_sg[MAX_SKB_FRAGS + 2]; - }; - -+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) -+#include -+#endif -+ - struct skb_vnet_hdr { - union { - struct virtio_net_hdr hdr; -@@ -147,6 +151,10 @@ static void skb_xmit_done(struct virtqueue *svq) - /* Suppress further interrupts. */ - virtqueue_disable_cb(svq); - -+#ifdef DEV_NETMAP -+ if (netmap_tx_irq(vi->dev, 0)) -+ return; -+#endif - /* We were probably waiting for more output buffers. */ - netif_wake_queue(vi->dev); - } -@@ -529,7 +537,17 @@ static int virtnet_poll(struct napi_struct *napi, int budget) - struct virtnet_info *vi = container_of(napi, struct virtnet_info, napi); - void *buf; - unsigned int len, received = 0; -+#ifdef DEV_NETMAP -+ int work_done = 0; -+ int nm_irq = netmap_rx_irq(vi->dev, 0, &work_done); - -+ if (nm_irq == NM_IRQ_COMPLETED) { -+ napi_complete(napi); -+ return 1; -+ } else if (nm_irq == NM_IRQ_RESCHED) { -+ return budget; -+ } -+#endif - again: - while (received < budget && - (buf = virtqueue_get_buf(vi->rvq, &len)) != NULL) { -@@ -742,6 +760,14 @@ static void virtnet_netpoll(struct net_device *dev) - static int virtnet_open(struct net_device *dev) - { - struct virtnet_info *vi = netdev_priv(dev); -+#ifdef DEV_NETMAP -+ int ok = virtio_netmap_init_buffers(vi); -+ -+ if (ok) { -+ virtnet_napi_enable(vi); -+ return 0; -+ } -+#endif - - /* Make sure we have some buffers: if oom use wq. */ - if (!try_fill_recv(vi, GFP_KERNEL)) -@@ -1148,6 +1174,10 @@ static int virtnet_probe(struct virtio_device *vdev) - goto unregister; - } - -+#ifdef DEV_NETMAP -+ virtio_netmap_attach(vi); -+#endif -+ - /* Assume link up if device can't report link status, - otherwise get link status from config. */ - if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { -@@ -1210,7 +1240,14 @@ static void remove_vq_common(struct virtnet_info *vi) - static void __devexit virtnet_remove(struct virtio_device *vdev) - { - struct virtnet_info *vi = vdev->priv; -+#ifdef DEV_NETMAP -+ /* Save the pointer, will go away after netmap_detach(). */ -+ struct netmap_adapter *token = NA(vi->dev); - -+ netmap_detach(vi->dev); -+ virtio_netmap_clean_used_rings(vi, token); -+ virtio_netmap_reclaim_unused(vi); -+#endif - /* Prevent config work handler from accessing the device. */ - mutex_lock(&vi->config_lock); - vi->config_enable = false; diff --git a/LINUX/final-patches/vanilla--virtio_net.c--31300--40100 b/LINUX/final-patches/vanilla--virtio_net.c--40000--40100 similarity index 85% rename from LINUX/final-patches/vanilla--virtio_net.c--31300--40100 rename to LINUX/final-patches/vanilla--virtio_net.c--40000--40100 index a703e7170..70dd5ce36 100644 --- a/LINUX/final-patches/vanilla--virtio_net.c--31300--40100 +++ b/LINUX/final-patches/vanilla--virtio_net.c--40000--40100 @@ -1,5 +1,5 @@ diff --git a/virtio_net.c b/virtio_net.c -index 059fdf1..d79cd6a 100644 +index 59b0e9754ae3..96597c5617e5 100644 --- a/virtio_net.c +++ b/virtio_net.c @@ -142,6 +142,10 @@ struct virtnet_info { @@ -44,23 +44,22 @@ index 059fdf1..d79cd6a 100644 received += virtnet_receive(rq, budget - received); /* Out of packets? */ -@@ -808,6 +829,15 @@ static int virtnet_open(struct net_device *dev) +@@ -808,8 +829,14 @@ static int virtnet_open(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); int i; +#ifdef DEV_NETMAP + int ok = virtio_netmap_init_buffers(vi); -+ -+ if (ok) { -+ for (i = 0; i < vi->max_queue_pairs; i++) -+ virtnet_napi_enable(&vi->rq[i]); -+ return 0; -+ } +#endif for (i = 0; i < vi->max_queue_pairs; i++) { ++#ifdef DEV_NETMAP ++ if (!ok) ++#endif if (i < vi->curr_queue_pairs) -@@ -1859,6 +1889,10 @@ static int virtnet_probe(struct virtio_device *vdev) + /* Make sure we have some buffers: if oom use wq. */ + if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) +@@ -1873,6 +1900,10 @@ static int virtnet_probe(struct virtio_device *vdev) goto free_recv_bufs; } @@ -71,7 +70,7 @@ index 059fdf1..d79cd6a 100644 /* Assume link up if device can't report link status, otherwise get link status from config. */ if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { -@@ -1907,7 +1941,14 @@ static void remove_vq_common(struct virtnet_info *vi) +@@ -1921,7 +1952,14 @@ static void remove_vq_common(struct virtnet_info *vi) static void virtnet_remove(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; diff --git a/LINUX/final-patches/vanilla--virtio_net.c--40100--40900 b/LINUX/final-patches/vanilla--virtio_net.c--40100--40900 index e83d330fa..d4755f270 100644 --- a/LINUX/final-patches/vanilla--virtio_net.c--40100--40900 +++ b/LINUX/final-patches/vanilla--virtio_net.c--40100--40900 @@ -1,5 +1,5 @@ diff --git a/virtio_net.c b/virtio_net.c -index 63c7810..f5fc43c 100644 +index 63c7810e1545..0115f62b92fe 100644 --- a/virtio_net.c +++ b/virtio_net.c @@ -142,6 +142,10 @@ struct virtnet_info { @@ -44,23 +44,22 @@ index 63c7810..f5fc43c 100644 received = virtnet_receive(rq, budget); /* Out of packets? */ -@@ -808,6 +829,15 @@ static int virtnet_open(struct net_device *dev) +@@ -808,8 +829,14 @@ static int virtnet_open(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); int i; +#ifdef DEV_NETMAP + int ok = virtio_netmap_init_buffers(vi); -+ -+ if (ok) { -+ for (i = 0; i < vi->max_queue_pairs; i++) -+ virtnet_napi_enable(&vi->rq[i]); -+ return 0; -+ } +#endif for (i = 0; i < vi->max_queue_pairs; i++) { ++#ifdef DEV_NETMAP ++ if (!ok) ++#endif if (i < vi->curr_queue_pairs) -@@ -1881,6 +1911,10 @@ static int virtnet_probe(struct virtio_device *vdev) + /* Make sure we have some buffers: if oom use wq. */ + if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) +@@ -1881,6 +1908,10 @@ static int virtnet_probe(struct virtio_device *vdev) goto free_recv_bufs; } @@ -71,7 +70,7 @@ index 63c7810..f5fc43c 100644 /* Assume link up if device can't report link status, otherwise get link status from config. */ if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { -@@ -1929,7 +1963,14 @@ static void remove_vq_common(struct virtnet_info *vi) +@@ -1929,7 +1960,14 @@ static void remove_vq_common(struct virtnet_info *vi) static void virtnet_remove(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; diff --git a/LINUX/final-patches/vanilla--virtio_net.c--40900--99999 b/LINUX/final-patches/vanilla--virtio_net.c--40900--40c00 similarity index 83% rename from LINUX/final-patches/vanilla--virtio_net.c--40900--99999 rename to LINUX/final-patches/vanilla--virtio_net.c--40900--40c00 index 437566ac0..06d2d6579 100644 --- a/LINUX/final-patches/vanilla--virtio_net.c--40900--99999 +++ b/LINUX/final-patches/vanilla--virtio_net.c--40900--40c00 @@ -1,5 +1,5 @@ diff --git a/virtio_net.c b/virtio_net.c -index cbf1c61..be4daab 100644 +index cbf1c613c67a..4b9be7350412 100644 --- a/virtio_net.c +++ b/virtio_net.c @@ -155,6 +155,10 @@ struct virtnet_info { @@ -44,23 +44,22 @@ index cbf1c61..be4daab 100644 received = virtnet_receive(rq, budget); /* Out of packets? */ -@@ -787,6 +808,15 @@ static int virtnet_open(struct net_device *dev) +@@ -787,8 +808,14 @@ static int virtnet_open(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); int i; +#ifdef DEV_NETMAP + int ok = virtio_netmap_init_buffers(vi); -+ -+ if (ok) { -+ for (i = 0; i < vi->max_queue_pairs; i++) -+ virtnet_napi_enable(&vi->rq[i]); -+ return 0; -+ } +#endif for (i = 0; i < vi->max_queue_pairs; i++) { ++#ifdef DEV_NETMAP ++ if (!ok) ++#endif if (i < vi->curr_queue_pairs) -@@ -1928,6 +1958,10 @@ static int virtnet_probe(struct virtio_device *vdev) + /* Make sure we have some buffers: if oom use wq. */ + if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) +@@ -1928,6 +1955,10 @@ static int virtnet_probe(struct virtio_device *vdev) goto free_unregister_netdev; } @@ -71,7 +70,7 @@ index cbf1c61..be4daab 100644 /* Assume link up if device can't report link status, otherwise get link status from config. */ if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { -@@ -1975,7 +2009,14 @@ static void remove_vq_common(struct virtnet_info *vi) +@@ -1975,7 +2006,14 @@ static void remove_vq_common(struct virtnet_info *vi) static void virtnet_remove(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; @@ -86,7 +85,7 @@ index cbf1c61..be4daab 100644 virtnet_cpu_notif_remove(vi); /* Make sure no work handler is accessing the device. */ -@@ -2072,6 +2113,9 @@ static unsigned int features_legacy[] = { +@@ -2072,6 +2110,9 @@ static unsigned int features_legacy[] = { VIRTNET_FEATURES, VIRTIO_NET_F_GSO, VIRTIO_F_ANY_LAYOUT, diff --git a/LINUX/final-patches/vanilla--virtio_net.c--31100--31300 b/LINUX/final-patches/vanilla--virtio_net.c--40c00--40f00 similarity index 55% rename from LINUX/final-patches/vanilla--virtio_net.c--31100--31300 rename to LINUX/final-patches/vanilla--virtio_net.c--40c00--40f00 index 9f1b59fc6..38c23f055 100644 --- a/LINUX/final-patches/vanilla--virtio_net.c--31100--31300 +++ b/LINUX/final-patches/vanilla--virtio_net.c--40c00--40f00 @@ -1,19 +1,19 @@ diff --git a/virtio_net.c b/virtio_net.c -index 59caa06..b64cb15 100644 +index 143d8a95a60d..bd58e50c4597 100644 --- a/virtio_net.c +++ b/virtio_net.c -@@ -145,6 +145,10 @@ struct virtnet_info { - struct notifier_block nb; +@@ -170,6 +170,10 @@ struct virtnet_info { + u32 speed; }; +#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) +#include +#endif + - struct skb_vnet_hdr { - union { - struct virtio_net_hdr hdr; -@@ -224,6 +228,10 @@ static void skb_xmit_done(struct virtqueue *vq) + struct padded_vnet_hdr { + struct virtio_net_hdr_mrg_rxbuf hdr; + /* +@@ -263,6 +267,11 @@ static void skb_xmit_done(struct virtqueue *vq) /* Suppress further interrupts. */ virtqueue_disable_cb(vq); @@ -21,12 +21,13 @@ index 59caa06..b64cb15 100644 + if (netmap_tx_irq(vi->dev, vq2txq(vq))) + return; +#endif - /* We were probably waiting for more output buffers. */ - netif_wake_subqueue(vi->dev, vq2txq(vq)); - } -@@ -754,6 +762,19 @@ static int virtnet_poll(struct napi_struct *napi, int budget) ++ + if (napi->weight) + virtqueue_napi_schedule(napi, vq); + else +@@ -1099,8 +1108,22 @@ static int virtnet_poll(struct napi_struct *napi, int budget) container_of(napi, struct receive_queue, napi); - unsigned int r, received = 0; + unsigned int received; +#ifdef DEV_NETMAP + int work_done = 0; @@ -41,28 +42,30 @@ index 59caa06..b64cb15 100644 + } +#endif + - again: - received += virtnet_receive(rq, budget - received); + virtnet_poll_cleantx(rq); + ++ + received = virtnet_receive(rq, budget); -@@ -813,6 +834,15 @@ static int virtnet_open(struct net_device *dev) + /* Out of packets? */ +@@ -1114,8 +1137,14 @@ static int virtnet_open(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); int i; +#ifdef DEV_NETMAP + int ok = virtio_netmap_init_buffers(vi); -+ -+ if (ok) { -+ for (i = 0; i < vi->max_queue_pairs; i++) -+ virtnet_napi_enable(&vi->rq[i]); -+ return 0; -+ } +#endif for (i = 0; i < vi->max_queue_pairs; i++) { ++#ifdef DEV_NETMAP ++ if (!ok) ++#endif if (i < vi->curr_queue_pairs) -@@ -1826,6 +1856,10 @@ static int virtnet_probe(struct virtio_device *vdev) - goto free_recv_bufs; - } + /* Make sure we have some buffers: if oom use wq. */ + if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) +@@ -2559,6 +2588,10 @@ static int virtnet_probe(struct virtio_device *vdev) + + virtnet_set_queues(vi, vi->curr_queue_pairs); +#ifdef DEV_NETMAP + virtio_netmap_attach(vi); @@ -71,7 +74,7 @@ index 59caa06..b64cb15 100644 /* Assume link up if device can't report link status, otherwise get link status from config. */ if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { -@@ -1872,7 +1906,14 @@ static void remove_vq_common(struct virtnet_info *vi) +@@ -2615,7 +2648,14 @@ static void remove_vq_common(struct virtnet_info *vi) static void virtnet_remove(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; @@ -83,6 +86,16 @@ index 59caa06..b64cb15 100644 + virtio_netmap_clean_used_rings(vi, token); + virtio_netmap_reclaim_unused(vi); +#endif - unregister_hotcpu_notifier(&vi->nb); + virtnet_cpu_notif_remove(vi); + + /* Make sure no work handler is accessing the device. */ +@@ -2684,6 +2724,9 @@ static unsigned int features_legacy[] = { + VIRTNET_FEATURES, + VIRTIO_NET_F_GSO, + VIRTIO_F_ANY_LAYOUT, ++#ifdef VIRTIO_NET_F_PTNETMAP ++ VIRTIO_NET_F_PTNETMAP, ++#endif + }; - /* Prevent config work handler from accessing the device. */ + static struct virtio_driver virtio_net_driver = { diff --git a/LINUX/final-patches/vanilla--virtio_net.c--40f00--41000 b/LINUX/final-patches/vanilla--virtio_net.c--40f00--41000 new file mode 100644 index 000000000..36b7a5ba0 --- /dev/null +++ b/LINUX/final-patches/vanilla--virtio_net.c--40f00--41000 @@ -0,0 +1,98 @@ +diff --git a/virtio_net.c b/virtio_net.c +index 559b215c0169..58c22e47b387 100644 +--- a/virtio_net.c ++++ b/virtio_net.c +@@ -181,6 +181,10 @@ struct virtnet_info { + unsigned long guest_offloads; + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + struct padded_vnet_hdr { + struct virtio_net_hdr_mrg_rxbuf hdr; + /* +@@ -274,6 +278,11 @@ static void skb_xmit_done(struct virtqueue *vq) + /* Suppress further interrupts. */ + virtqueue_disable_cb(vq); + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(vi->dev, vq2txq(vq))) ++ return; ++#endif ++ + if (napi->weight) + virtqueue_napi_schedule(napi, vq); + else +@@ -1205,6 +1214,19 @@ static int virtnet_poll(struct napi_struct *napi, int budget) + unsigned int received; + bool xdp_xmit = false; + ++#ifdef DEV_NETMAP ++ int work_done = 0; ++ struct virtnet_info *vi = rq->vq->vdev->priv; ++ int nm_irq = netmap_rx_irq(vi->dev, vq2rxq(rq->vq), &work_done); ++ ++ if (nm_irq == NM_IRQ_COMPLETED) { ++ napi_complete(napi); ++ return 1; ++ } else if (nm_irq == NM_IRQ_RESCHED) { ++ return budget; ++ } ++#endif ++ + virtnet_poll_cleantx(rq); + + received = virtnet_receive(rq, budget, &xdp_xmit); +@@ -1223,8 +1245,14 @@ static int virtnet_open(struct net_device *dev) + { + struct virtnet_info *vi = netdev_priv(dev); + int i; ++#ifdef DEV_NETMAP ++ int ok = virtio_netmap_init_buffers(vi); ++#endif + + for (i = 0; i < vi->max_queue_pairs; i++) { ++#ifdef DEV_NETMAP ++ if (!ok) ++#endif + if (i < vi->curr_queue_pairs) + /* Make sure we have some buffers: if oom use wq. */ + if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) +@@ -2685,6 +2713,10 @@ static int virtnet_probe(struct virtio_device *vdev) + + virtnet_set_queues(vi, vi->curr_queue_pairs); + ++#ifdef DEV_NETMAP ++ virtio_netmap_attach(vi); ++#endif ++ + /* Assume link up if device can't report link status, + otherwise get link status from config. */ + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { +@@ -2736,7 +2768,14 @@ static void remove_vq_common(struct virtnet_info *vi) + static void virtnet_remove(struct virtio_device *vdev) + { + struct virtnet_info *vi = vdev->priv; ++#ifdef DEV_NETMAP ++ /* Save the pointer, will go away after netmap_detach(). */ ++ struct netmap_adapter *token = NA(vi->dev); + ++ netmap_detach(vi->dev); ++ virtio_netmap_clean_used_rings(vi, token); ++ virtio_netmap_reclaim_unused(vi); ++#endif + virtnet_cpu_notif_remove(vi); + + /* Make sure no work handler is accessing the device. */ +@@ -2803,6 +2842,9 @@ static unsigned int features_legacy[] = { + VIRTNET_FEATURES, + VIRTIO_NET_F_GSO, + VIRTIO_F_ANY_LAYOUT, ++#ifdef VIRTIO_NET_F_PTNETMAP ++ VIRTIO_NET_F_PTNETMAP, ++#endif + }; + + static struct virtio_driver virtio_net_driver = { diff --git a/LINUX/final-patches/vanilla--virtio_net.c--41000--41100 b/LINUX/final-patches/vanilla--virtio_net.c--41000--41100 new file mode 100644 index 000000000..0020198a1 --- /dev/null +++ b/LINUX/final-patches/vanilla--virtio_net.c--41000--41100 @@ -0,0 +1,98 @@ +diff --git a/virtio_net.c b/virtio_net.c +index 23374603e4d9..7fee058adcb6 100644 +--- a/virtio_net.c ++++ b/virtio_net.c +@@ -208,6 +208,10 @@ struct virtnet_info { + unsigned long guest_offloads; + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + struct padded_vnet_hdr { + struct virtio_net_hdr_mrg_rxbuf hdr; + /* +@@ -304,6 +308,11 @@ static void skb_xmit_done(struct virtqueue *vq) + /* Suppress further interrupts. */ + virtqueue_disable_cb(vq); + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(vi->dev, vq2txq(vq))) ++ return; ++#endif ++ + if (napi->weight) + virtqueue_napi_schedule(napi, vq); + else +@@ -1272,6 +1281,19 @@ static int virtnet_poll(struct napi_struct *napi, int budget) + unsigned int received; + bool xdp_xmit = false; + ++#ifdef DEV_NETMAP ++ int work_done = 0; ++ struct virtnet_info *vi = rq->vq->vdev->priv; ++ int nm_irq = netmap_rx_irq(vi->dev, vq2rxq(rq->vq), &work_done); ++ ++ if (nm_irq == NM_IRQ_COMPLETED) { ++ napi_complete(napi); ++ return 1; ++ } else if (nm_irq == NM_IRQ_RESCHED) { ++ return budget; ++ } ++#endif ++ + virtnet_poll_cleantx(rq); + + received = virtnet_receive(rq, budget, &xdp_xmit); +@@ -1290,8 +1312,14 @@ static int virtnet_open(struct net_device *dev) + { + struct virtnet_info *vi = netdev_priv(dev); + int i, err; ++#ifdef DEV_NETMAP ++ int ok = virtio_netmap_init_buffers(vi); ++#endif + + for (i = 0; i < vi->max_queue_pairs; i++) { ++#ifdef DEV_NETMAP ++ if (!ok) ++#endif + if (i < vi->curr_queue_pairs) + /* Make sure we have some buffers: if oom use wq. */ + if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) +@@ -2855,6 +2883,10 @@ static int virtnet_probe(struct virtio_device *vdev) + + virtnet_set_queues(vi, vi->curr_queue_pairs); + ++#ifdef DEV_NETMAP ++ virtio_netmap_attach(vi); ++#endif ++ + /* Assume link up if device can't report link status, + otherwise get link status from config. */ + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { +@@ -2905,7 +2937,14 @@ static void remove_vq_common(struct virtnet_info *vi) + static void virtnet_remove(struct virtio_device *vdev) + { + struct virtnet_info *vi = vdev->priv; ++#ifdef DEV_NETMAP ++ /* Save the pointer, will go away after netmap_detach(). */ ++ struct netmap_adapter *token = NA(vi->dev); + ++ netmap_detach(vi->dev); ++ virtio_netmap_clean_used_rings(vi, token); ++ virtio_netmap_reclaim_unused(vi); ++#endif + virtnet_cpu_notif_remove(vi); + + /* Make sure no work handler is accessing the device. */ +@@ -2972,6 +3011,9 @@ static unsigned int features_legacy[] = { + VIRTNET_FEATURES, + VIRTIO_NET_F_GSO, + VIRTIO_F_ANY_LAYOUT, ++#ifdef VIRTIO_NET_F_PTNETMAP ++ VIRTIO_NET_F_PTNETMAP, ++#endif + }; + + static struct virtio_driver virtio_net_driver = { diff --git a/LINUX/final-patches/vanilla--virtio_net.c--41100--50b00 b/LINUX/final-patches/vanilla--virtio_net.c--41100--50b00 new file mode 100644 index 000000000..edc261a9a --- /dev/null +++ b/LINUX/final-patches/vanilla--virtio_net.c--41100--50b00 @@ -0,0 +1,97 @@ +diff --git a/virtio_net.c b/virtio_net.c +index 032e1ac10a30..7e92e484d0ab 100644 +--- a/virtio_net.c ++++ b/virtio_net.c +@@ -212,6 +212,10 @@ struct virtnet_info { + unsigned long guest_offloads; + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + struct padded_vnet_hdr { + struct virtio_net_hdr_mrg_rxbuf hdr; + /* +@@ -308,6 +312,11 @@ static void skb_xmit_done(struct virtqueue *vq) + /* Suppress further interrupts. */ + virtqueue_disable_cb(vq); + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(vi->dev, vq2txq(vq))) ++ return; ++#endif ++ + if (napi->weight) + virtqueue_napi_schedule(napi, vq); + else +@@ -1277,6 +1286,18 @@ static int virtnet_poll(struct napi_struct *napi, int budget) + unsigned int received, qp; + bool xdp_xmit = false; + ++#ifdef DEV_NETMAP ++ int work_done = 0; ++ int nm_irq = netmap_rx_irq(vi->dev, vq2rxq(rq->vq), &work_done); ++ ++ if (nm_irq == NM_IRQ_COMPLETED) { ++ napi_complete(napi); ++ return 1; ++ } else if (nm_irq == NM_IRQ_RESCHED) { ++ return budget; ++ } ++#endif ++ + virtnet_poll_cleantx(rq); + + received = virtnet_receive(rq, budget, &xdp_xmit); +@@ -1300,8 +1321,14 @@ static int virtnet_open(struct net_device *dev) + { + struct virtnet_info *vi = netdev_priv(dev); + int i, err; ++#ifdef DEV_NETMAP ++ int ok = virtio_netmap_init_buffers(vi); ++#endif + + for (i = 0; i < vi->max_queue_pairs; i++) { ++#ifdef DEV_NETMAP ++ if (!ok) ++#endif + if (i < vi->curr_queue_pairs) + /* Make sure we have some buffers: if oom use wq. */ + if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) +@@ -2871,6 +2898,10 @@ static int virtnet_probe(struct virtio_device *vdev) + + virtnet_set_queues(vi, vi->curr_queue_pairs); + ++#ifdef DEV_NETMAP ++ virtio_netmap_attach(vi); ++#endif ++ + /* Assume link up if device can't report link status, + otherwise get link status from config. */ + netif_carrier_off(dev); +@@ -2921,7 +2952,14 @@ static void remove_vq_common(struct virtnet_info *vi) + static void virtnet_remove(struct virtio_device *vdev) + { + struct virtnet_info *vi = vdev->priv; ++#ifdef DEV_NETMAP ++ /* Save the pointer, will go away after netmap_detach(). */ ++ struct netmap_adapter *token = NA(vi->dev); + ++ netmap_detach(vi->dev); ++ virtio_netmap_clean_used_rings(vi, token); ++ virtio_netmap_reclaim_unused(vi); ++#endif + virtnet_cpu_notif_remove(vi); + + /* Make sure no work handler is accessing the device. */ +@@ -2988,6 +3026,9 @@ static unsigned int features_legacy[] = { + VIRTNET_FEATURES, + VIRTIO_NET_F_GSO, + VIRTIO_F_ANY_LAYOUT, ++#ifdef VIRTIO_NET_F_PTNETMAP ++ VIRTIO_NET_F_PTNETMAP, ++#endif + }; + + static struct virtio_driver virtio_net_driver = { diff --git a/LINUX/final-patches/vanilla--virtio_net.c--30b00--31100 b/LINUX/final-patches/vanilla--virtio_net.c--50b00--60500 similarity index 52% rename from LINUX/final-patches/vanilla--virtio_net.c--30b00--31100 rename to LINUX/final-patches/vanilla--virtio_net.c--50b00--60500 index 06d6ee96a..9234ec436 100644 --- a/LINUX/final-patches/vanilla--virtio_net.c--30b00--31100 +++ b/LINUX/final-patches/vanilla--virtio_net.c--50b00--60500 @@ -1,19 +1,19 @@ diff --git a/virtio_net.c b/virtio_net.c -index 3d2a90a..2365434 100644 +index 508408fbe78f..ebcb50ab46e1 100644 --- a/virtio_net.c +++ b/virtio_net.c -@@ -131,6 +131,10 @@ struct virtnet_info { - struct notifier_block nb; +@@ -236,6 +236,10 @@ struct virtnet_info { + struct failover *failover; }; +#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) +#include +#endif + - struct skb_vnet_hdr { - union { - struct virtio_net_hdr hdr; -@@ -210,6 +214,10 @@ static void skb_xmit_done(struct virtqueue *vq) + struct padded_vnet_hdr { + struct virtio_net_hdr_mrg_rxbuf hdr; + /* +@@ -347,6 +351,11 @@ static void skb_xmit_done(struct virtqueue *vq) /* Suppress further interrupts. */ virtqueue_disable_cb(vq); @@ -21,46 +21,48 @@ index 3d2a90a..2365434 100644 + if (netmap_tx_irq(vi->dev, vq2txq(vq))) + return; +#endif - /* We were probably waiting for more output buffers. */ - netif_wake_subqueue(vi->dev, vq2txq(vq)); - } -@@ -603,7 +611,17 @@ static int virtnet_poll(struct napi_struct *napi, int budget) - struct virtnet_info *vi = rq->vq->vdev->priv; - void *buf; - unsigned int r, len, received = 0; ++ + if (napi->weight) + virtqueue_napi_schedule(napi, vq); + else +@@ -1451,6 +1460,18 @@ static int virtnet_poll(struct napi_struct *napi, int budget) + unsigned int received; + unsigned int xdp_xmit = 0; + +#ifdef DEV_NETMAP + int work_done = 0; + int nm_irq = netmap_rx_irq(vi->dev, vq2rxq(rq->vq), &work_done); - ++ + if (nm_irq == NM_IRQ_COMPLETED) { + napi_complete(napi); + return 1; -+ } else if (nm_irq == NM_IRQ_RESCHED) { ++ } else if (nm_irq == NM_IRQ_RESCHED) { + return budget; -+ } ++ } +#endif - again: - while (received < budget && - (buf = virtqueue_get_buf(rq->vq, &len)) != NULL) { -@@ -636,6 +654,15 @@ static int virtnet_open(struct net_device *dev) ++ + virtnet_poll_cleantx(rq); + + received = virtnet_receive(rq, budget, &xdp_xmit); +@@ -1478,6 +1499,15 @@ static int virtnet_open(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); - int i; + int i, err; +#ifdef DEV_NETMAP + int ok = virtio_netmap_init_buffers(vi); + + if (ok) { + for (i = 0; i < vi->max_queue_pairs; i++) -+ virtnet_napi_enable(&vi->rq[i]); ++ virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); + return 0; + } +#endif for (i = 0; i < vi->max_queue_pairs; i++) { if (i < vi->curr_queue_pairs) -@@ -1592,6 +1619,10 @@ static int virtnet_probe(struct virtio_device *vdev) - goto free_recv_bufs; - } +@@ -3134,6 +3164,10 @@ static int virtnet_probe(struct virtio_device *vdev) + + virtnet_set_queues(vi, vi->curr_queue_pairs); +#ifdef DEV_NETMAP + virtio_netmap_attach(vi); @@ -68,8 +70,8 @@ index 3d2a90a..2365434 100644 + /* Assume link up if device can't report link status, otherwise get link status from config. */ - if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { -@@ -1637,7 +1668,14 @@ static void remove_vq_common(struct virtnet_info *vi) + netif_carrier_off(dev); +@@ -3187,7 +3221,14 @@ static void remove_vq_common(struct virtnet_info *vi) static void virtnet_remove(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; @@ -81,6 +83,16 @@ index 3d2a90a..2365434 100644 + virtio_netmap_clean_used_rings(vi, token); + virtio_netmap_reclaim_unused(vi); +#endif - unregister_hotcpu_notifier(&vi->nb); + virtnet_cpu_notif_remove(vi); + + /* Make sure no work handler is accessing the device. */ +@@ -3256,6 +3297,9 @@ static unsigned int features_legacy[] = { + VIRTNET_FEATURES, + VIRTIO_NET_F_GSO, + VIRTIO_F_ANY_LAYOUT, ++#ifdef VIRTIO_NET_F_PTNETMAP ++ VIRTIO_NET_F_PTNETMAP, ++#endif + }; - /* Prevent config work handler from accessing the device. */ + static struct virtio_driver virtio_net_driver = { diff --git a/LINUX/final-patches/vanilla--virtio_net.c--30800--30b00 b/LINUX/final-patches/vanilla--virtio_net.c--60500--60800 similarity index 50% rename from LINUX/final-patches/vanilla--virtio_net.c--30800--30b00 rename to LINUX/final-patches/vanilla--virtio_net.c--60500--60800 index 4bec12874..4cdce8046 100644 --- a/LINUX/final-patches/vanilla--virtio_net.c--30800--30b00 +++ b/LINUX/final-patches/vanilla--virtio_net.c--60500--60800 @@ -1,19 +1,19 @@ diff --git a/virtio_net.c b/virtio_net.c -index 35c00c5..bfbb178 100644 +index 8e9f4cfe941f..a4eae7a07304 100644 --- a/virtio_net.c +++ b/virtio_net.c -@@ -132,6 +132,10 @@ struct virtnet_info { - struct notifier_block nb; +@@ -293,6 +293,10 @@ struct virtnet_info { + struct failover *failover; }; +#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) +#include +#endif + - struct skb_vnet_hdr { - union { - struct virtio_net_hdr hdr; -@@ -211,6 +215,10 @@ static void skb_xmit_done(struct virtqueue *vq) + struct padded_vnet_hdr { + struct virtio_net_hdr_v1_hash hdr; + /* +@@ -421,6 +425,11 @@ static void skb_xmit_done(struct virtqueue *vq) /* Suppress further interrupts. */ virtqueue_disable_cb(vq); @@ -21,55 +21,59 @@ index 35c00c5..bfbb178 100644 + if (netmap_tx_irq(vi->dev, vq2txq(vq))) + return; +#endif - /* We were probably waiting for more output buffers. */ - netif_wake_subqueue(vi->dev, vq2txq(vq)); - } -@@ -603,7 +611,17 @@ static int virtnet_poll(struct napi_struct *napi, int budget) - struct virtnet_info *vi = rq->vq->vdev->priv; - void *buf; - unsigned int len, received = 0; ++ + if (napi->weight) + virtqueue_napi_schedule(napi, vq); + else +@@ -1955,6 +1964,18 @@ static int virtnet_poll(struct napi_struct *napi, int budget) + unsigned int received; + unsigned int xdp_xmit = 0; + +#ifdef DEV_NETMAP + int work_done = 0; + int nm_irq = netmap_rx_irq(vi->dev, vq2rxq(rq->vq), &work_done); - ++ + if (nm_irq == NM_IRQ_COMPLETED) { + napi_complete(napi); + return 1; -+ } else if (nm_irq == NM_IRQ_RESCHED) { ++ } else if (nm_irq == NM_IRQ_RESCHED) { + return budget; -+ } ++ } +#endif - again: - while (received < budget && - (buf = virtqueue_get_buf(rq->vq, &len)) != NULL) { -@@ -635,6 +653,15 @@ static int virtnet_open(struct net_device *dev) ++ + virtnet_poll_cleantx(rq); + + received = virtnet_receive(rq, budget, &xdp_xmit); +@@ -2015,6 +2036,15 @@ static int virtnet_open(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); - int i; + int i, err; +#ifdef DEV_NETMAP + int ok = virtio_netmap_init_buffers(vi); + + if (ok) { + for (i = 0; i < vi->max_queue_pairs; i++) -+ virtnet_napi_enable(&vi->rq[i]); ++ virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); + return 0; + } +#endif - for (i = 0; i < vi->max_queue_pairs; i++) { - /* Make sure we have some buffers: if oom use wq. */ -@@ -1572,6 +1599,10 @@ static int virtnet_probe(struct virtio_device *vdev) - goto free_recv_bufs; + enable_delayed_refill(vi); + +@@ -4259,6 +4289,12 @@ static int virtnet_probe(struct virtio_device *vdev) + goto free_unregister_netdev; } ++ virtnet_set_queues(vi, vi->curr_queue_pairs); ++ +#ifdef DEV_NETMAP + virtio_netmap_attach(vi); +#endif + /* Assume link up if device can't report link status, otherwise get link status from config. */ - if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { -@@ -1617,7 +1648,14 @@ static void remove_vq_common(struct virtnet_info *vi) + netif_carrier_off(dev); +@@ -4311,7 +4347,14 @@ static void remove_vq_common(struct virtnet_info *vi) static void virtnet_remove(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; @@ -81,6 +85,16 @@ index 35c00c5..bfbb178 100644 + virtio_netmap_clean_used_rings(vi, token); + virtio_netmap_reclaim_unused(vi); +#endif - unregister_hotcpu_notifier(&vi->nb); + virtnet_cpu_notif_remove(vi); + + /* Make sure no work handler is accessing the device. */ +@@ -4386,6 +4429,9 @@ static unsigned int features_legacy[] = { + VIRTNET_FEATURES, + VIRTIO_NET_F_GSO, + VIRTIO_F_ANY_LAYOUT, ++#ifdef VIRTIO_NET_F_PTNETMAP ++ VIRTIO_NET_F_PTNETMAP, ++#endif + }; - /* Prevent config work handler from accessing the device. */ + static struct virtio_driver virtio_net_driver = { diff --git a/LINUX/final-patches/vanilla--virtio_net.c--60800--60b00 b/LINUX/final-patches/vanilla--virtio_net.c--60800--60b00 new file mode 100644 index 000000000..3596812da --- /dev/null +++ b/LINUX/final-patches/vanilla--virtio_net.c--60800--60b00 @@ -0,0 +1,100 @@ +diff --git a/virtio_net.c b/virtio_net.c +index d7ce4a1011ea..d4234a5eeea2 100644 +--- a/virtio_net.c ++++ b/virtio_net.c +@@ -331,6 +331,10 @@ struct virtnet_info { + struct failover *failover; + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + struct padded_vnet_hdr { + struct virtio_net_hdr_v1_hash hdr; + /* +@@ -482,6 +486,11 @@ static void skb_xmit_done(struct virtqueue *vq) + /* Suppress further interrupts. */ + virtqueue_disable_cb(vq); + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(vi->dev, vq2txq(vq))) ++ return; ++#endif ++ + if (napi->weight) + virtqueue_napi_schedule(napi, vq); + else +@@ -2198,6 +2207,18 @@ static int virtnet_poll(struct napi_struct *napi, int budget) + unsigned int xdp_xmit = 0; + bool napi_complete; + ++#ifdef DEV_NETMAP ++ int work_done = 0; ++ int nm_irq = netmap_rx_irq(vi->dev, vq2rxq(rq->vq), &work_done); ++ ++ if (nm_irq == NM_IRQ_COMPLETED) { ++ nm_napi_complete(napi); ++ return 1; ++ } else if (nm_irq == NM_IRQ_RESCHED) { ++ return budget; ++ } ++#endif ++ + virtnet_poll_cleantx(rq); + + received = virtnet_receive(rq, budget, &xdp_xmit); +@@ -2262,6 +2283,15 @@ static int virtnet_open(struct net_device *dev) + { + struct virtnet_info *vi = netdev_priv(dev); + int i, err; ++#ifdef DEV_NETMAP ++ int ok = virtio_netmap_init_buffers(vi); ++ ++ if (ok) { ++ for (i = 0; i < vi->max_queue_pairs; i++) ++ virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); ++ return 0; ++ } ++#endif + + enable_delayed_refill(vi); + +@@ -4838,6 +4868,12 @@ static int virtnet_probe(struct virtio_device *vdev) + goto free_unregister_netdev; + } + ++ virtnet_set_queues(vi, vi->curr_queue_pairs); ++ ++#ifdef DEV_NETMAP ++ virtio_netmap_attach(vi); ++#endif ++ + /* Assume link up if device can't report link status, + otherwise get link status from config. */ + netif_carrier_off(dev); +@@ -4890,7 +4926,14 @@ static void remove_vq_common(struct virtnet_info *vi) + static void virtnet_remove(struct virtio_device *vdev) + { + struct virtnet_info *vi = vdev->priv; ++#ifdef DEV_NETMAP ++ /* Save the pointer, will go away after netmap_detach(). */ ++ struct netmap_adapter *token = NA(vi->dev); + ++ netmap_detach(vi->dev); ++ virtio_netmap_clean_used_rings(vi, token); ++ virtio_netmap_reclaim_unused(vi); ++#endif + virtnet_cpu_notif_remove(vi); + + /* Make sure no work handler is accessing the device. */ +@@ -4966,6 +5009,9 @@ static unsigned int features_legacy[] = { + VIRTNET_FEATURES, + VIRTIO_NET_F_GSO, + VIRTIO_F_ANY_LAYOUT, ++#ifdef VIRTIO_NET_F_PTNETMAP ++ VIRTIO_NET_F_PTNETMAP, ++#endif + }; + + static struct virtio_driver virtio_net_driver = { diff --git a/LINUX/final-patches/vanilla--virtio_net.c--60b00--60c00 b/LINUX/final-patches/vanilla--virtio_net.c--60b00--60c00 new file mode 100644 index 000000000..a5d2e8303 --- /dev/null +++ b/LINUX/final-patches/vanilla--virtio_net.c--60b00--60c00 @@ -0,0 +1,100 @@ +diff --git a/virtio_net.c b/virtio_net.c +index 5a1c1ec5a64b..30ac7195521b 100644 +--- a/virtio_net.c ++++ b/virtio_net.c +@@ -482,6 +482,10 @@ struct virtnet_info { + u64 device_stats_cap; + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + struct padded_vnet_hdr { + struct virtio_net_hdr_v1_hash hdr; + /* +@@ -714,6 +718,11 @@ static void skb_xmit_done(struct virtqueue *vq) + /* Suppress further interrupts. */ + virtqueue_disable_cb(vq); + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(vi->dev, vq2txq(vq))) ++ return; ++#endif ++ + if (napi->weight) + virtqueue_napi_schedule(napi, vq); + else +@@ -2812,6 +2821,18 @@ static int virtnet_poll(struct napi_struct *napi, int budget) + unsigned int xdp_xmit = 0; + bool napi_complete; + ++#ifdef DEV_NETMAP ++ int work_done = 0; ++ int nm_irq = netmap_rx_irq(vi->dev, vq2rxq(rq->vq), &work_done); ++ ++ if (nm_irq == NM_IRQ_COMPLETED) { ++ nm_napi_complete(napi); ++ return 1; ++ } else if (nm_irq == NM_IRQ_RESCHED) { ++ return budget; ++ } ++#endif ++ + virtnet_poll_cleantx(rq, budget); + + received = virtnet_receive(rq, budget, &xdp_xmit); +@@ -2888,6 +2909,15 @@ static int virtnet_open(struct net_device *dev) + { + struct virtnet_info *vi = netdev_priv(dev); + int i, err; ++#ifdef DEV_NETMAP ++ int ok = virtio_netmap_init_buffers(vi); ++ ++ if (ok) { ++ for (i = 0; i < vi->max_queue_pairs; i++) ++ virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); ++ return 0; ++ } ++#endif + + enable_delayed_refill(vi); + +@@ -6578,6 +6608,12 @@ static int virtnet_probe(struct virtio_device *vdev) + goto free_unregister_netdev; + } + ++ virtnet_set_queues(vi, vi->curr_queue_pairs); ++ ++#ifdef DEV_NETMAP ++ virtio_netmap_attach(vi); ++#endif ++ + /* Assume link up if device can't report link status, + otherwise get link status from config. */ + netif_carrier_off(dev); +@@ -6630,7 +6666,14 @@ static void remove_vq_common(struct virtnet_info *vi) + static void virtnet_remove(struct virtio_device *vdev) + { + struct virtnet_info *vi = vdev->priv; ++#ifdef DEV_NETMAP ++ /* Save the pointer, will go away after netmap_detach(). */ ++ struct netmap_adapter *token = NA(vi->dev); + ++ netmap_detach(vi->dev); ++ virtio_netmap_clean_used_rings(vi, token); ++ virtio_netmap_reclaim_unused(vi); ++#endif + virtnet_cpu_notif_remove(vi); + + /* Make sure no work handler is accessing the device. */ +@@ -6710,6 +6753,9 @@ static unsigned int features_legacy[] = { + VIRTNET_FEATURES, + VIRTIO_NET_F_GSO, + VIRTIO_F_ANY_LAYOUT, ++#ifdef VIRTIO_NET_F_PTNETMAP ++ VIRTIO_NET_F_PTNETMAP, ++#endif + }; + + static struct virtio_driver virtio_net_driver = { diff --git a/LINUX/final-patches/vanilla--virtio_net.c--60c00--60f00 b/LINUX/final-patches/vanilla--virtio_net.c--60c00--60f00 new file mode 100644 index 000000000..7c50e2949 --- /dev/null +++ b/LINUX/final-patches/vanilla--virtio_net.c--60c00--60f00 @@ -0,0 +1,97 @@ +diff --git a/virtio_net.c b/virtio_net.c +index 53a038fcbe99..37fd3e5fa584 100644 +--- a/virtio_net.c ++++ b/virtio_net.c +@@ -483,6 +483,10 @@ struct virtnet_info { + u64 device_stats_cap; + }; + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) ++#include ++#endif ++ + struct padded_vnet_hdr { + struct virtio_net_hdr_v1_hash hdr; + /* +@@ -734,6 +738,11 @@ static void skb_xmit_done(struct virtqueue *vq) + /* Suppress further interrupts. */ + virtqueue_disable_cb(vq); + ++#ifdef DEV_NETMAP ++ if (netmap_tx_irq(vi->dev, vq2txq(vq))) ++ return; ++#endif ++ + if (napi->weight) + virtqueue_napi_schedule(napi, vq); + else +@@ -2838,6 +2847,18 @@ static int virtnet_poll(struct napi_struct *napi, int budget) + unsigned int xdp_xmit = 0; + bool napi_complete; + ++#ifdef DEV_NETMAP ++ int work_done = 0; ++ int nm_irq = netmap_rx_irq(vi->dev, vq2rxq(rq->vq), &work_done); ++ ++ if (nm_irq == NM_IRQ_COMPLETED) { ++ nm_napi_complete(napi); ++ return 1; ++ } else if (nm_irq == NM_IRQ_RESCHED) { ++ return budget; ++ } ++#endif ++ + virtnet_poll_cleantx(rq, budget); + + received = virtnet_receive(rq, budget, &xdp_xmit); +@@ -2933,6 +2954,15 @@ static int virtnet_open(struct net_device *dev) + { + struct virtnet_info *vi = netdev_priv(dev); + int i, err; ++#ifdef DEV_NETMAP ++ int ok = virtio_netmap_init_buffers(vi); ++ ++ if (ok) { ++ for (i = 0; i < vi->max_queue_pairs; i++) ++ virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); ++ return 0; ++ } ++#endif + + enable_delayed_refill(vi); + +@@ -6677,6 +6707,9 @@ static int virtnet_probe(struct virtio_device *vdev) + vi->device_stats_cap = le64_to_cpu(v); + } + ++#ifdef DEV_NETMAP ++ virtio_netmap_attach(vi); ++#endif + /* Assume link up if device can't report link status, + otherwise get link status from config. */ + netif_carrier_off(dev); +@@ -6737,7 +6770,14 @@ static void remove_vq_common(struct virtnet_info *vi) + static void virtnet_remove(struct virtio_device *vdev) + { + struct virtnet_info *vi = vdev->priv; ++#ifdef DEV_NETMAP ++ /* Save the pointer, will go away after netmap_detach(). */ ++ struct netmap_adapter *token = NA(vi->dev); + ++ netmap_detach(vi->dev); ++ virtio_netmap_clean_used_rings(vi, token); ++ virtio_netmap_reclaim_unused(vi); ++#endif + virtnet_cpu_notif_remove(vi); + + /* Make sure no work handler is accessing the device. */ +@@ -6819,6 +6859,9 @@ static unsigned int features_legacy[] = { + VIRTNET_FEATURES, + VIRTIO_NET_F_GSO, + VIRTIO_F_ANY_LAYOUT, ++#ifdef VIRTIO_NET_F_PTNETMAP ++ VIRTIO_NET_F_PTNETMAP, ++#endif + }; + + static struct virtio_driver virtio_net_driver = { diff --git a/LINUX/final-patches/vanilla--vmxnet3--40000--40d00 b/LINUX/final-patches/vanilla--vmxnet3--40000--40d00 new file mode 100644 index 000000000..a087ef13c --- /dev/null +++ b/LINUX/final-patches/vanilla--vmxnet3--40000--40d00 @@ -0,0 +1,83 @@ +diff --git a/vmxnet3/vmxnet3_drv.c b/vmxnet3/vmxnet3_drv.c +old mode 100644 +new mode 100755 +index 294214c15292..c6d334b8d096 +--- a/vmxnet3/vmxnet3_drv.c ++++ b/vmxnet3/vmxnet3_drv.c +@@ -308,6 +308,11 @@ static u32 get_bitfield32(const __le32 *bitfield, u32 pos, u32 size) + #endif /* __BIG_ENDIAN_BITFIELD */ + + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) || defined(DEV_NETMAP) ++#include "if_vmxnet3_netmap.h" ++#endif ++ ++ + static void + vmxnet3_unmap_tx_buf(struct vmxnet3_tx_buf_info *tbi, + struct pci_dev *pdev) +@@ -367,6 +372,14 @@ vmxnet3_tq_tx_complete(struct vmxnet3_tx_queue *tq, + int completed = 0; + union Vmxnet3_GenericDesc *gdesc; + ++#ifdef DEV_NETMAP ++ struct net_device *netdev = adapter->netdev; ++ ++ if (netmap_tx_irq(netdev, 0) != NM_IRQ_PASS) ++ return 0; ++#endif ++ ++ + gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; + while (VMXNET3_TCD_GET_GEN(&gdesc->tcd) == tq->comp_ring.gen) { + completed += vmxnet3_unmap_pkt(VMXNET3_TCD_GET_TXIDX( +@@ -1164,6 +1177,15 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, + struct Vmxnet3_RxDesc rxCmdDesc; + struct Vmxnet3_RxCompDesc rxComp; + #endif ++ ++#ifdef DEV_NETMAP ++ u_int total_packets = 0; ++ struct net_device *netdev = adapter->netdev; ++ ++ if (netmap_rx_irq(netdev, 0, &total_packets) != NM_IRQ_PASS) ++ return 1; ++#endif /* DEV_NETMAP */ ++ + vmxnet3_getRxComp(rcd, &rq->comp_ring.base[rq->comp_ring.next2proc].rcd, + &rxComp); + while (rcd->gen == rq->comp_ring.gen) { +@@ -2256,6 +2278,10 @@ vmxnet3_activate_dev(struct vmxnet3_adapter *adapter) + adapter->rx_queue[0].rx_ring[0].size, + adapter->rx_queue[0].rx_ring[1].size); + ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_init_buffers(adapter); ++#endif /* DEV_NETMAP */ ++ + vmxnet3_tq_init_all(adapter); + err = vmxnet3_rq_init_all(adapter); + if (err) { +@@ -3101,6 +3127,11 @@ vmxnet3_probe_device(struct pci_dev *pdev, + goto err_register; + } + ++ ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + vmxnet3_check_link(adapter, false); + return 0; + +@@ -3152,6 +3183,10 @@ vmxnet3_remove_device(struct pci_dev *pdev) + + unregister_netdev(netdev); + ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + vmxnet3_free_intr_resources(adapter); + vmxnet3_free_pci_resources(adapter); + #ifdef VMXNET3_RSS diff --git a/LINUX/final-patches/vanilla--vmxnet3--40d00--60600 b/LINUX/final-patches/vanilla--vmxnet3--40d00--60600 new file mode 100644 index 000000000..d0763e7aa --- /dev/null +++ b/LINUX/final-patches/vanilla--vmxnet3--40d00--60600 @@ -0,0 +1,152 @@ +diff --git a/vmxnet3/vmxnet3_drv.c b/vmxnet3/vmxnet3_drv.c +old mode 100644 +new mode 100755 +index d1c7029ded7c..e827e6fe8fa3 +--- a/vmxnet3/vmxnet3_drv.c ++++ b/vmxnet3/vmxnet3_drv.c +@@ -308,6 +308,11 @@ static u32 get_bitfield32(const __le32 *bitfield, u32 pos, u32 size) + #endif /* __BIG_ENDIAN_BITFIELD */ + + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) || defined(DEV_NETMAP) ++#include "if_vmxnet3_netmap_v2.h" ++#endif ++ ++ + static void + vmxnet3_unmap_tx_buf(struct vmxnet3_tx_buf_info *tbi, + struct pci_dev *pdev) +@@ -367,6 +372,13 @@ vmxnet3_tq_tx_complete(struct vmxnet3_tx_queue *tq, + int completed = 0; + union Vmxnet3_GenericDesc *gdesc; + ++#ifdef DEV_NETMAP ++ struct net_device *netdev = adapter->netdev; ++ ++ if (netmap_tx_irq(netdev, tq - adapter->tx_queue) != NM_IRQ_PASS) ++ return 0; ++#endif ++ + gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; + while (VMXNET3_TCD_GET_GEN(&gdesc->tcd) == tq->comp_ring.gen) { + completed += vmxnet3_unmap_pkt(VMXNET3_TCD_GET_TXIDX( +@@ -492,6 +504,10 @@ vmxnet3_tq_init(struct vmxnet3_tx_queue *tq, + for (i = 0; i < tq->tx_ring.size; i++) + tq->buf_info[i].map_type = VMXNET3_MAP_NONE; + ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_tq_config_tx_buf(tq, adapter); ++#endif /* DEV_NETMAP */ ++ + /* stats are not reset */ + } + +@@ -1269,6 +1285,15 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, + struct Vmxnet3_RxDesc rxCmdDesc; + struct Vmxnet3_RxCompDesc rxComp; + #endif ++ ++#ifdef DEV_NETMAP ++ u_int total_packets = 0; ++ struct net_device *netdev = adapter->netdev; ++ ++ if (netmap_rx_irq(netdev, rq - adapter->rx_queue, &total_packets) != NM_IRQ_PASS) ++ return 1; ++#endif /* DEV_NETMAP */ ++ + vmxnet3_getRxComp(rcd, &rq->comp_ring.base[rq->comp_ring.next2proc].rcd, + &rxComp); + while (rcd->gen == rq->comp_ring.gen) { +@@ -1692,12 +1717,18 @@ vmxnet3_rq_init(struct vmxnet3_rx_queue *rq, + sizeof(struct Vmxnet3_RxDesc)); + rq->rx_ring[i].gen = VMXNET3_INIT_GEN; + } +- if (vmxnet3_rq_alloc_rx_buf(rq, 0, rq->rx_ring[0].size - 1, +- adapter) == 0) { +- /* at least has 1 rx buffer for the 1st ring */ +- return -ENOMEM; ++#ifdef DEV_NETMAP ++ if (!vmxnet3_netmap_rq_config_rx_buf(rq, adapter)) { ++#endif /* DEV_NETMAP */ ++ if (vmxnet3_rq_alloc_rx_buf(rq, 0, rq->rx_ring[0].size - 1, ++ adapter) == 0) { ++ /* at least has 1 rx buffer for the 1st ring */ ++ return -ENOMEM; ++ } ++ vmxnet3_rq_alloc_rx_buf(rq, 1, rq->rx_ring[1].size - 1, adapter); ++#ifdef DEV_NETMAP + } +- vmxnet3_rq_alloc_rx_buf(rq, 1, rq->rx_ring[1].size - 1, adapter); ++#endif /* DEV_NETMAP */ + + /* reset the comp ring */ + rq->comp_ring.next2proc = 0; +@@ -1801,7 +1832,11 @@ vmxnet3_rq_create_all(struct vmxnet3_adapter *adapter) + { + int i, err = 0; + ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_set_rxdataring_enabled(adapter); ++#else + adapter->rxdataring_enabled = VMXNET3_VERSION_GE_3(adapter); ++#endif /* DEV_NETMAP */ + + for (i = 0; i < adapter->num_rx_queues; i++) { + err = vmxnet3_rq_create(&adapter->rx_queue[i], adapter); +@@ -2537,7 +2572,10 @@ vmxnet3_activate_dev(struct vmxnet3_adapter *adapter) + adapter->rx_queue[0].rx_ring[0].size, + adapter->rx_queue[0].rx_ring[1].size); + +- vmxnet3_tq_init_all(adapter); ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_init_buffers(adapter); ++#endif /* DEV_NETMAP */ ++ + err = vmxnet3_rq_init_all(adapter); + if (err) { + netdev_err(adapter->netdev, +@@ -2545,6 +2583,8 @@ vmxnet3_activate_dev(struct vmxnet3_adapter *adapter) + goto rq_err; + } + ++ vmxnet3_tq_init_all(adapter); ++ + err = vmxnet3_request_irqs(adapter); + if (err) { + netdev_err(adapter->netdev, +@@ -2832,7 +2872,12 @@ vmxnet3_create_queues(struct vmxnet3_adapter *adapter, u32 tx_ring_size, + adapter->rx_queue[0].rx_ring[1].size = rx_ring2_size; + vmxnet3_adjust_rx_ring_size(adapter); + ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_set_rxdataring_enabled(adapter); ++#else + adapter->rxdataring_enabled = VMXNET3_VERSION_GE_3(adapter); ++#endif /* DEV_NETMAP */ ++ + for (i = 0; i < adapter->num_rx_queues; i++) { + struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i]; + /* qid and qid2 for rx queues will be assigned later when num +@@ -3463,6 +3508,11 @@ vmxnet3_probe_device(struct pci_dev *pdev, + goto err_register; + } + ++ ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + vmxnet3_check_link(adapter, false); + return 0; + +@@ -3520,6 +3570,10 @@ vmxnet3_remove_device(struct pci_dev *pdev) + + unregister_netdev(netdev); + ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + vmxnet3_free_intr_resources(adapter); + vmxnet3_free_pci_resources(adapter); + if (VMXNET3_VERSION_GE_3(adapter)) { diff --git a/LINUX/final-patches/vanilla--vmxnet3--60600--99999 b/LINUX/final-patches/vanilla--vmxnet3--60600--99999 new file mode 100644 index 000000000..54fd98667 --- /dev/null +++ b/LINUX/final-patches/vanilla--vmxnet3--60600--99999 @@ -0,0 +1,147 @@ +diff --git a/vmxnet3/vmxnet3_drv.c b/vmxnet3/vmxnet3_drv.c +old mode 100644 +new mode 100755 +index 0578864792b6..be17a491faff +--- a/vmxnet3/vmxnet3_drv.c ++++ b/vmxnet3/vmxnet3_drv.c +@@ -335,6 +335,11 @@ static u32 get_bitfield32(const __le32 *bitfield, u32 pos, u32 size) + #endif /* __BIG_ENDIAN_BITFIELD */ + + ++#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE) || defined(DEV_NETMAP) ++#include "if_vmxnet3_netmap_v2.h" ++#endif ++ ++ + static void + vmxnet3_unmap_tx_buf(struct vmxnet3_tx_buf_info *tbi, + struct pci_dev *pdev) +@@ -408,6 +413,13 @@ vmxnet3_tq_tx_complete(struct vmxnet3_tx_queue *tq, + xdp_frame_bulk_init(&bq); + rcu_read_lock(); + ++#ifdef DEV_NETMAP ++ struct net_device *netdev = adapter->netdev; ++ ++ if (netmap_tx_irq(netdev, tq - adapter->tx_queue) != NM_IRQ_PASS) ++ return 0; ++#endif ++ + gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; + while (VMXNET3_TCD_GET_GEN(&gdesc->tcd) == tq->comp_ring.gen) { + /* Prevent any &gdesc->tcd field from being (speculatively) +@@ -546,6 +558,10 @@ vmxnet3_tq_init(struct vmxnet3_tx_queue *tq, + for (i = 0; i < tq->tx_ring.size; i++) + tq->buf_info[i].map_type = VMXNET3_MAP_NONE; + ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_tq_config_tx_buf(tq, adapter); ++#endif /* DEV_NETMAP */ ++ + /* stats are not reset */ + } + +@@ -1519,6 +1535,14 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, + #endif + bool need_flush = false; + ++#ifdef DEV_NETMAP ++ u_int total_packets = 0; ++ struct net_device *netdev = adapter->netdev; ++ ++ if (netmap_rx_irq(netdev, rq - adapter->rx_queue, &total_packets) != NM_IRQ_PASS) ++ return 1; ++#endif /* DEV_NETMAP */ ++ + vmxnet3_getRxComp(rcd, &rq->comp_ring.base[rq->comp_ring.next2proc].rcd, + &rxComp); + while (rcd->gen == rq->comp_ring.gen) { +@@ -2079,6 +2103,9 @@ vmxnet3_rq_init(struct vmxnet3_rx_queue *rq, + if (err) + return err; + ++#ifdef DEV_NETMAP ++ if (!vmxnet3_netmap_rq_config_rx_buf(rq, adapter)) { ++#endif /* DEV_NETMAP */ + if (vmxnet3_rq_alloc_rx_buf(rq, 0, rq->rx_ring[0].size - 1, + adapter) == 0) { + xdp_rxq_info_unreg(&rq->xdp_rxq); +@@ -2089,6 +2116,9 @@ vmxnet3_rq_init(struct vmxnet3_rx_queue *rq, + return -ENOMEM; + } + vmxnet3_rq_alloc_rx_buf(rq, 1, rq->rx_ring[1].size - 1, adapter); ++#ifdef DEV_NETMAP ++ } ++#endif /* DEV_NETMAP */ + + /* reset the comp ring */ + rq->comp_ring.next2proc = 0; +@@ -2191,7 +2221,11 @@ vmxnet3_rq_create_all(struct vmxnet3_adapter *adapter) + { + int i, err = 0; + ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_set_rxdataring_enabled(adapter); ++#else + adapter->rxdataring_enabled = VMXNET3_VERSION_GE_3(adapter); ++#endif /* DEV_NETMAP */ + + for (i = 0; i < adapter->num_rx_queues; i++) { + err = vmxnet3_rq_create(&adapter->rx_queue[i], adapter); +@@ -3019,7 +3053,10 @@ vmxnet3_activate_dev(struct vmxnet3_adapter *adapter) + adapter->rx_queue[0].rx_ring[0].size, + adapter->rx_queue[0].rx_ring[1].size); + +- vmxnet3_tq_init_all(adapter); ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_init_buffers(adapter); ++#endif /* DEV_NETMAP */ ++ + err = vmxnet3_rq_init_all(adapter); + if (err) { + netdev_err(adapter->netdev, +@@ -3027,6 +3064,8 @@ vmxnet3_activate_dev(struct vmxnet3_adapter *adapter) + goto rq_err; + } + ++ vmxnet3_tq_init_all(adapter); ++ + err = vmxnet3_request_irqs(adapter); + if (err) { + netdev_err(adapter->netdev, +@@ -3312,7 +3351,12 @@ vmxnet3_create_queues(struct vmxnet3_adapter *adapter, u32 tx_ring_size, + adapter->rx_queue[0].rx_ring[1].size = rx_ring2_size; + vmxnet3_adjust_rx_ring_size(adapter); + ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_set_rxdataring_enabled(adapter); ++#else + adapter->rxdataring_enabled = VMXNET3_VERSION_GE_3(adapter); ++#endif /* DEV_NETMAP */ ++ + for (i = 0; i < adapter->num_rx_queues; i++) { + struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i]; + /* qid and qid2 for rx queues will be assigned later when num +@@ -4101,6 +4145,11 @@ vmxnet3_probe_device(struct pci_dev *pdev, + goto err_register; + } + ++ ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_attach(adapter); ++#endif /* DEV_NETMAP */ ++ + vmxnet3_check_link(adapter, false); + return 0; + +@@ -4176,6 +4225,10 @@ vmxnet3_remove_device(struct pci_dev *pdev) + + unregister_netdev(netdev); + ++#ifdef DEV_NETMAP ++ vmxnet3_netmap_detach(netdev); ++#endif /* DEV_NETMAP */ ++ + vmxnet3_free_intr_resources(adapter); + vmxnet3_free_pci_resources(adapter); + if (VMXNET3_VERSION_GE_3(adapter)) { diff --git a/LINUX/forcedeth_netmap.h b/LINUX/forcedeth_netmap.h index 0fcb3923c..35ac46b78 100644 --- a/LINUX/forcedeth_netmap.h +++ b/LINUX/forcedeth_netmap.h @@ -73,13 +73,13 @@ This makes sure that there is always a free slot. static int forcedeth_netmap_reg(struct netmap_adapter *na, int onoff) { - struct ifnet *ifp = na->ifp; + struct ifnet *ifp = na->ifp; struct SOFTC_T *np = netdev_priv(ifp); u8 __iomem *base = get_hwbase(ifp); // first half of nv_change_mtu() - down nv_disable_irq(ifp); - nv_napi_disable(ifp); + napi_disable(&np->napi); netif_tx_lock_bh(ifp); netif_addr_lock(ifp); spin_lock(&np->lock); @@ -112,7 +112,7 @@ forcedeth_netmap_reg(struct netmap_adapter *na, int onoff) spin_unlock(&np->lock); netif_addr_unlock(ifp); netif_tx_unlock_bh(ifp); - nv_napi_enable(ifp); + napi_enable(&np->napi); nv_enable_irq(ifp); return (0); @@ -242,8 +242,6 @@ forcedeth_netmap_rxsync(struct netmap_kring *kring, int flags) */ rmb(); if (netmap_no_pendintr || force_update) { - uint16_t slot_flags = kring->nkr_slot_flags; - nic_i = np->get_rx.ex - rxr; /* next pkt to check */ /* put_rx is the refill position, one before nr_hwcur. * This slot is not available @@ -257,7 +255,7 @@ forcedeth_netmap_rxsync(struct netmap_kring *kring, int flags) if (statlen & NV_RX2_AVAIL) /* still owned by the NIC */ break; ring->slot[nm_i].len = statlen & LEN_MASK_V2; // XXX crc? - ring->slot[nm_i].flags = slot_flags; + ring->slot[nm_i].flags = 0; // ifp->stats.rx_packets++; nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); @@ -320,29 +318,18 @@ forcedeth_netmap_rxsync(struct netmap_kring *kring, int flags) static int forcedeth_netmap_tx_init(struct SOFTC_T *np) { - struct ring_desc_ex *desc; - int i, n; struct netmap_adapter *na = NA(np->dev); struct netmap_slot *slot; - slot = netmap_reset(na, NR_TX, 0, 0); + slot = netmap_reset(na, NR_TX, 0, 0); /* slot is NULL if we are not in native netmap mode */ if (!slot) return 0; - /* in netmap mode, overwrite addresses and maps */ - //txd = np->rl_ldata.rl_tx_desc; - desc = np->tx_ring.ex; - n = np->tx_ring_size; - - /* l points in the netmap ring, i points in the NIC ring */ - for (i = 0; i < n; i++) { - int l = netmap_idx_n2k(&na->tx_rings[0], i); - uint64_t paddr; - PNMB(na, slot + l, &paddr); - desc[i].flaglen = 0; - desc[i].bufhigh = htole32(dma_high(paddr)); - desc[i].buflow = htole32(dma_low(paddr)); - } + + /* no need to pre-fill the tx rings, since txsync + * will always overwrite the tx slots + */ + return 1; } @@ -362,11 +349,11 @@ forcedeth_netmap_rx_init(struct SOFTC_T *np) * Do not release the slots owned by userspace, * and also keep one empty. */ - lim = np->rx_ring_size - 1 - nm_kr_rxspace(&na->rx_rings[0]); - for (i = 0; i < np->rx_ring_size; i++) { + lim = np->rx_ring_size - 1 - nm_kr_rxspace(na->rx_rings[0]); + for (i = 0; i < lim; i++) { void *addr; uint64_t paddr; - int l = netmap_idx_n2k(&na->rx_rings[0], i); + int l = netmap_idx_n2k(na->rx_rings[0], i); addr = PNMB(na, slot + l, &paddr); //netmap_reload_map(np->rl_ldata.rl_rx_mtag, diff --git a/LINUX/i40e_netmap_linux.h b/LINUX/i40e_netmap_linux.h index d275de920..76ec22187 100644 --- a/LINUX/i40e_netmap_linux.h +++ b/LINUX/i40e_netmap_linux.h @@ -24,8 +24,6 @@ */ /* - * $FreeBSD$ - * * netmap support for: i40e (LINUX version) * * derived from ixgbe @@ -46,7 +44,7 @@ int i40e_netmap_txsync(struct netmap_kring *kring, int flags); int i40e_netmap_rxsync(struct netmap_kring *kring, int flags); -extern int ix_rx_miss, ix_rx_miss_bufs, ix_crcstrip; +extern int ix_crcstrip; #ifdef NETMAP_LINUX_I40E_PTR_ARRAY #define NM_I40E_TX_RING(a, r) ((a)->tx_rings[(r)]) @@ -55,8 +53,16 @@ extern int ix_rx_miss, ix_rx_miss_bufs, ix_crcstrip; #define NM_I40E_TX_RING(a, r) (&(a)->tx_rings[(r)]) #define NM_I40E_RX_RING(a, r) (&(a)->rx_rings[(r)]) #endif +#ifdef NETMAP_LINUX_I40E_PTR_STATE +#define NM_I40E_STATE(pf) (&(pf)->state) +#else +#define NM_I40E_STATE(pf) ((pf)->state) +#endif #ifdef NETMAP_I40E_MAIN + +#define i40e_driver_name netmap_i40e_driver_name +char i40e_driver_name[] = "i40e" NETMAP_LINUX_DRIVER_SUFFIX; /* * device-specific sysctl variables: * @@ -66,18 +72,11 @@ extern int ix_rx_miss, ix_rx_miss_bufs, ix_crcstrip; * so using crcstrip=0 helps in benchmarks. * The driver by default strips CRCs and we do not override it. * - * ix_rx_miss, ix_rx_miss_bufs: - * count packets that might be missed due to lost interrupts. */ SYSCTL_DECL(_dev_netmap); -int ix_rx_miss = 0, ix_rx_miss_bufs = 0, ix_crcstrip = 1; +int ix_crcstrip = 1; SYSCTL_INT(_dev_netmap, OID_AUTO, ix_crcstrip, - CTLFLAG_RW, &ix_crcstrip, 1, "NIC strips CRC on rx frames"); -SYSCTL_INT(_dev_netmap, OID_AUTO, ix_rx_miss, - CTLFLAG_RW, &ix_rx_miss, 0, "potentially missed rx intr"); -SYSCTL_INT(_dev_netmap, OID_AUTO, ix_rx_miss_bufs, - CTLFLAG_RW, &ix_rx_miss_bufs, 0, "potentially missed rx intr bufs"); - + CTLFLAG_RW, &ix_crcstrip, 1, "NIC strips CRC on rx frames"); #if 0 static void set_crcstrip(struct ixgbe_hw *hw, int onoff) @@ -95,7 +94,7 @@ set_crcstrip(struct ixgbe_hw *hw, int onoff) hl = IXGBE_READ_REG(hw, IXGBE_HLREG0); rxc = IXGBE_READ_REG(hw, IXGBE_RDRXCTL); if (netmap_verbose) - D("%s read HLREG 0x%x rxc 0x%x", + nm_prinf("%s read HLREG 0x%x rxc 0x%x", onoff ? "enter" : "exit", hl, rxc); /* hw requirements ... */ rxc &= ~IXGBE_RDRXCTL_RSCFRSTSIZE; @@ -110,7 +109,7 @@ set_crcstrip(struct ixgbe_hw *hw, int onoff) rxc |= IXGBE_RDRXCTL_CRCSTRIP; } if (netmap_verbose) - D("%s write HLREG 0x%x rxc 0x%x", + nm_prinf("%s write HLREG 0x%x rxc 0x%x", onoff ? "enter" : "exit", hl, rxc); IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hl); IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, rxc); @@ -131,6 +130,27 @@ i40e_netmap_configure_tx_ring(struct i40e_ring *ring) netmap_reset(na, NR_TX, ring->queue_index, 0); } +static void +i40e_netmap_preconfigure_rx_ring(struct i40e_ring *ring, + struct i40e_hmc_obj_rxq *rx_ctx) +{ + struct netmap_adapter *na; + struct netmap_kring *kring; + + if (!ring->netdev) { + // XXX it this possible? + return; + } + + na = NA(ring->netdev); + + if (netmap_reset(na, NR_RX, ring->queue_index, 0) == NULL) + return; // not in native netmap mode + + kring = na->rx_rings[ring->queue_index]; + rx_ctx->dbuff = kring->hwbuf_len >> I40E_RXQ_CTX_DBUFF_SHIFT; +} + static int i40e_netmap_configure_rx_ring(struct i40e_ring *ring) { @@ -146,24 +166,24 @@ i40e_netmap_configure_rx_ring(struct i40e_ring *ring) na = NA(ring->netdev); ring_nr = ring->queue_index; - kring = &na->rx_rings[ring_nr]; slot = netmap_reset(na, NR_RX, ring_nr, 0); if (!slot) return 0; // not in native netmap mode + kring = na->rx_rings[ring_nr]; lim = na->num_rx_desc - 1 - nm_kr_rxspace(kring); - for (i = 0; i < na->num_rx_desc; i++) { + for (i = 0; i <= lim; i++) { int si = netmap_idx_n2k(kring, i); uint64_t paddr; union i40e_rx_desc *rx = I40E_RX_DESC(ring, i); - PNMB(na, slot + si, &paddr); + PNMB_O(kring, slot + si, &paddr); rx->read.pkt_addr = htole64(paddr); rx->read.hdr_addr = 0; } - ring->next_to_clean = netmap_idx_k2n(kring, 0); + ring->next_to_clean = 0; wmb(); writel(lim, ring->tail); return 1; @@ -178,11 +198,11 @@ i40e_netmap_reg(struct netmap_adapter *na, int onoff) { struct ifnet *ifp = na->ifp; struct i40e_netdev_priv *np = netdev_priv(ifp); - struct i40e_vsi *vsi = np->vsi; - struct i40e_pf *pf = (struct i40e_pf *)vsi->back; + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = (struct i40e_pf *)vsi->back; bool was_running; - while (test_and_set_bit(__I40E_CONFIG_BUSY, &pf->state)) + while (test_and_set_bit(__I40E_CONFIG_BUSY, NM_I40E_STATE(pf))) usleep_range(1000, 2000); if ( (was_running = netif_running(vsi->netdev)) ) @@ -200,11 +220,46 @@ i40e_netmap_reg(struct netmap_adapter *na, int onoff) } //set_crcstrip(&adapter->hw, onoff); // XXX why twice ? - clear_bit(__I40E_CONFIG_BUSY, &pf->state); + clear_bit(__I40E_CONFIG_BUSY, NM_I40E_STATE(pf)); return 0; } +static int +i40e_netmap_bufcfg(struct netmap_kring *kring, uint64_t target) +{ + uint64_t incr; + + kring->buf_align = 0; + + if (kring->tx == NR_TX) { + kring->hwbuf_len = target; + return 0; + } + + incr = 1UL << I40E_RXQ_CTX_DBUFF_SHIFT; + target &= ~(incr - 1); + if (target < 1024UL || target > 16384UL - incr) + return EINVAL; + + kring->hwbuf_len = target; + + return 0; +} + +static int +i40e_netmap_config(struct netmap_adapter *na, struct nm_config_info *info) +{ + int ret = netmap_rings_config_get(na, info); + + if (ret) { + return ret; + } + + info->rx_buf_maxsize = NETMAP_BUF_SIZE(na); + + return 0; +} /* * The attach routine, called near the end of i40e_attach(), @@ -221,14 +276,17 @@ i40e_netmap_attach(struct i40e_vsi *vsi) bzero(&na, sizeof(na)); na.ifp = vsi->netdev; - na.na_flags = NAF_BDG_MAYSLEEP; - // XXX check that queues is set. + na.pdev = &vsi->back->pdev->dev; + na.na_flags = NAF_MOREFRAG | NAF_OFFSETS; na.num_tx_desc = NM_I40E_TX_RING(vsi, 0)->count; na.num_rx_desc = NM_I40E_RX_RING(vsi, 0)->count; + na.num_tx_rings = na.num_rx_rings = vsi->num_queue_pairs; + na.rx_buf_maxsize = vsi->rx_buf_len; na.nm_txsync = i40e_netmap_txsync; na.nm_rxsync = i40e_netmap_rxsync; na.nm_register = i40e_netmap_reg; - na.num_tx_rings = na.num_rx_rings = vsi->num_queue_pairs; + na.nm_config = i40e_netmap_config; + na.nm_bufcfg = i40e_netmap_bufcfg; netmap_attach(&na); } @@ -279,14 +337,14 @@ i40e_netmap_txsync(struct netmap_kring *kring, int flags) struct i40e_vsi *vsi = np->vsi; struct i40e_ring *txr; - if (!netif_running(ifp)) + if (!netif_carrier_ok(ifp)) return 0; txr = NM_I40E_TX_RING(vsi, kring->ring_id); - if (!txr) + if (unlikely(!txr || !txr->desc)) { + nm_prlim(1, "ring %s is missing (txr=%p)", kring->name, txr); return ENXIO; - //bus_dmamap_sync(txr->dma.tag, txr->dma.map, - // BUS_DMASYNC_POSTREAD); + } /* * First part: process new packets to send. @@ -334,34 +392,45 @@ i40e_netmap_txsync(struct netmap_kring *kring, int flags) struct netmap_slot *slot = &ring->slot[nm_i]; u_int len = slot->len; uint64_t paddr; - void *addr = PNMB(na, slot, &paddr); + uint64_t offset = nm_get_offset(kring, slot); /* device-specific */ struct i40e_tx_desc *curr = I40E_TX_DESC(txr, nic_i); - u64 flags = (slot->flags & NS_REPORT || - nic_i == 0 || nic_i == report_frequency) ? - ((u64)I40E_TX_DESC_CMD_RS << I40E_TXD_QW1_CMD_SHIFT) : 0; + u64 hw_flags = 0; /* prefetch for next round */ __builtin_prefetch(&ring->slot[nm_i + 1]); __builtin_prefetch(I40E_TX_DESC(txr, nic_i)); - NM_CHECK_ADDR_LEN(na, addr, len); - + PNMB(na, slot, &paddr); + NM_CHECK_ADDR_LEN_OFF(na, len, offset); + + if (!(slot->flags & NS_MOREFRAG)) { + hw_flags |= ((u64)(I40E_TX_DESC_CMD_EOP) << + I40E_TXD_QW1_CMD_SHIFT); + if (slot->flags & NS_REPORT || nic_i == 0 || + nic_i == report_frequency) { + hw_flags |= ((u64)I40E_TX_DESC_CMD_RS << + I40E_TXD_QW1_CMD_SHIFT); + } + } if (slot->flags & NS_BUF_CHANGED) { /* buffer has changed, reload map */ //netmap_reload_map(na, txr->dma.tag, txbuf->map, addr); } - slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED); - - /* Fill the slot in the NIC ring. */ - /* Use legacy descriptor, they are faster? */ - curr->buffer_addr = htole64(paddr); + slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED | NS_MOREFRAG); + + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, len, NR_TX); + /* Fill the slot in the NIC ring. + * (we should investigate if using legacy descriptors + * is faster). */ + curr->buffer_addr = htole64(paddr + offset); curr->cmd_type_offset_bsz = htole64( ((u64)len << I40E_TXD_QW1_TX_BUF_SZ_SHIFT) | - flags | - ((u64)(I40E_TX_DESC_CMD_ICRC | I40E_TX_DESC_CMD_EOP) << I40E_TXD_QW1_CMD_SHIFT) - ); // XXX more ? + hw_flags | + ((u64)(I40E_TX_DESC_CMD_ICRC) << I40E_TXD_QW1_CMD_SHIFT) + ); /* more flags may be needed */ nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); @@ -382,9 +451,22 @@ i40e_netmap_txsync(struct netmap_kring *kring, int flags) */ nic_i = i40e_netmap_read_hwtail(txr->desc, kring->nkr_num_slots); if (nic_i != txr->next_to_clean) { + u_int tosync; + nm_i = netmap_idx_n2k(kring, nic_i); + /* some tx completed, increment avail */ txr->next_to_clean = nic_i; - kring->nr_hwtail = nm_prev(netmap_idx_n2k(kring, nic_i), lim); + tosync = nm_next(kring->nr_hwtail, lim); + /* sync all buffers that we are returning to userspace */ + for ( ; tosync != nm_i; tosync = nm_next(tosync, lim)) { + struct netmap_slot *slot = &ring->slot[tosync]; + uint64_t paddr; + (void)PNMB_O(kring, slot, &paddr); + + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, + &paddr, slot->len, NR_TX); + } + kring->nr_hwtail = nm_prev(nm_i, lim); } return 0; @@ -412,6 +494,7 @@ i40e_netmap_rxsync(struct netmap_kring *kring, int flags) struct netmap_ring *ring = kring->ring; u_int nm_i; /* index into the netmap ring */ u_int nic_i; /* index into the NIC ring */ + u_int ntail; /* new tail for the user */ u_int n; u_int const lim = kring->nkr_num_slots - 1; u_int const head = kring->rhead; @@ -424,16 +507,16 @@ i40e_netmap_rxsync(struct netmap_kring *kring, int flags) if (!netif_running(ifp)) return 0; - + rxr = NM_I40E_RX_RING(vsi, kring->ring_id); - if (!rxr) + if (unlikely(!rxr || !rxr->desc)) { + nm_prlim(1, "ring %s is missing (rxr=%p)", kring->name, rxr); return ENXIO; + } if (head > lim) return netmap_ring_reinit(kring); - if (!rxr) - return ENXIO; /* XXX check sync modes */ //bus_dmamap_sync(rxr->dma.tag, rxr->dma.map, // BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); @@ -455,36 +538,57 @@ i40e_netmap_rxsync(struct netmap_kring *kring, int flags) */ if (netmap_no_pendintr || force_update) { int crclen = ix_crcstrip ? 0 : 4; - uint16_t slot_flags = kring->nkr_slot_flags; + int complete; nic_i = rxr->next_to_clean; // or also k2n(kring->nr_hwtail) nm_i = netmap_idx_n2k(kring, nic_i); + /* we advance tail only when we see a complete packet */ + ntail = lim + 1; + complete = 0; for (n = 0; ; n++) { union i40e_rx_desc *curr = I40E_RX_DESC(rxr, nic_i); uint64_t qword = le64toh(curr->wb.qword1.status_error_len); uint32_t staterr = (qword & I40E_RXD_QW1_STATUS_MASK) >> I40E_RXD_QW1_STATUS_SHIFT; + uint16_t slot_flags = 0; + struct netmap_slot *slot; + uint64_t paddr; + + if (likely(complete)) { + ntail = nm_i; + complete = 0; + } if ((staterr & (1<slot[nm_i].len = ((qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) + slot = ring->slot + nm_i; + slot->len = ((qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> I40E_RXD_QW1_LENGTH_PBUF_SHIFT) - crclen; - ring->slot[nm_i].flags = slot_flags; - //bus_dmamap_sync(rxr->ptag, - // rxr->buffers[nic_i].pmap, BUS_DMASYNC_POSTREAD); + + if (!slot->len) + break; + + if (unlikely((staterr & (1<flags = slot_flags; + PNMB_O(kring, slot, &paddr); + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, + &paddr, slot->len, NR_RX); + nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); } if (n) { /* update the state variables */ - if (netmap_no_pendintr && !force_update) { - /* diagnostics */ - ix_rx_miss ++; - ix_rx_miss_bufs += n; - } rxr->next_to_clean = nic_i; - kring->nr_hwtail = nm_i; + if (likely(ntail <= lim)) { + kring->nr_hwtail = ntail; + nm_prdis("%s: nic_i %u nm_i %u ntail %u n %u", if_name(ifp), nic_i, nm_i, ntail, n); + } } kring->nr_kflags &= ~NKR_PENDINTR; } @@ -504,8 +608,9 @@ i40e_netmap_rxsync(struct netmap_kring *kring, int flags) struct netmap_slot *slot = &ring->slot[nm_i]; uint64_t paddr; void *addr = PNMB(na, slot, &paddr); + uint64_t offset = nm_get_offset(kring, slot); - union i40e_32byte_rx_desc *curr = I40E_RX_DESC(rxr, nic_i); + union i40e_rx_desc *curr = I40E_RX_DESC(rxr, nic_i); if (addr == NETMAP_BUF_BASE(na)) /* bad buf */ goto ring_reset; @@ -515,17 +620,15 @@ i40e_netmap_rxsync(struct netmap_kring *kring, int flags) //netmap_reload_map(na, rxr->ptag, rxbuf->pmap, addr); slot->flags &= ~NS_BUF_CHANGED; } - curr->read.pkt_addr = htole64(paddr); + curr->read.pkt_addr = htole64(paddr + offset); curr->read.hdr_addr = 0; // XXX needed - //bus_dmamap_sync(rxr->ptag, rxbuf->pmap, - // BUS_DMASYNC_PREREAD); + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, NETMAP_BUF_SIZE(na), NR_RX); nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); } kring->nr_hwcur = head; - //bus_dmamap_sync(rxr->dma.tag, rxr->dma.map, - // BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* * IMPORTANT: we must leave one free slot in the ring, * so move nic_i back by one unit diff --git a/LINUX/ice_netmap_linux.h b/LINUX/ice_netmap_linux.h new file mode 100644 index 000000000..f83588ef1 --- /dev/null +++ b/LINUX/ice_netmap_linux.h @@ -0,0 +1,584 @@ +#include +#include +#include +#ifdef NETMAP_LINUX_HAVE_ICE_XRINGS +#define NM_ICE_RXRING ice_rx_ring +#define NM_ICE_TXRING ice_tx_ring +#else +#define NM_ICE_RXRING ice_ring +#define NM_ICE_TXRING ice_ring +#endif /* NETMAP_LINUX_HAVE_ICE_XRINGS */ +#ifndef NETMAP_LINUX_HAVE_ICE_VSI_DOWN +#define ICE_VSI_DOWN __ICE_DOWN +#endif /* !NETMAP_LINUX_HAVE_ICE_VSI_DOWN */ + +extern int ix_crcstrip; + +#ifdef NETMAP_ICE_LIB + +/* + * Reconcile kernel and user view of the transmit ring. + * + * All information is in the kring. + * Userspace wants to send packets up to the one before kring->rhead, + * kernel knows kring->nr_hwcur is the first unsent packet. + * + * Here we push packets out (as many as possible), and possibly + * reclaim buffers from previously completed transmission. + * + * The caller (netmap) guarantees that there is only one instance + * running at any time. Any interference with other driver + * methods should be handled by the individual drivers. + */ + +static int +ice_netmap_txsync(struct netmap_kring *kring, int flags) +{ + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *ring = kring->ring; + u_int nm_i; /* index into the netmap ring */ + u_int nic_i; /* index into the NIC ring */ + u_int n; + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + /* + * interrupts on every tx packet are expensive so request + * them every half ring, or where NS_REPORT is set + */ + //u_int report_frequency = kring->nkr_num_slots >> 1; + + /* device-specific */ + struct ice_netdev_priv *np = netdev_priv(ifp); + struct ice_vsi *vsi = np->vsi; + struct NM_ICE_TXRING *txr; + + if (!netif_carrier_ok(ifp)) + return 0; + + txr = vsi->tx_rings[kring->ring_id]; + if (unlikely(!txr || !txr->desc)) { + nm_prlim(1, "ring %s is missing (txr=%p)", kring->name, txr); + return ENXIO; + } + + /* + * First part: process new packets to send. + * nm_i is the current index in the netmap ring, + * nic_i is the corresponding index in the NIC ring. + * The two numbers differ because upon a *_init() we reset + * the NIC ring but leave the netmap ring unchanged. + * For the transmit ring, we have + * + * nm_i = kring->nr_hwcur + * nic_i = IXGBE_TDT (not tracked in the driver) + * and + * nm_i == (nic_i + kring->nkr_hwofs) % ring_size + * + * In this driver kring->nkr_hwofs >= 0, but for other + * drivers it might be negative as well. + */ + + /* + * If we have packets to send (kring->nr_hwcur != kring->rhead) + * iterate over the netmap ring, fetch length and update + * the corresponding slot in the NIC ring. Some drivers also + * need to update the buffer's physical address in the NIC slot + * even NS_BUF_CHANGED is not set (PNMB computes the addresses). + * + * The netmap_reload_map() calls is especially expensive, + * even when (as in this case) the tag is 0, so do only + * when the buffer has actually changed. + * + * If possible do not set the report/intr bit on all slots, + * but only a few times per ring or when NS_REPORT is set. + * + * Finally, on 10G and faster drivers, it might be useful + * to prefetch the next slot and txr entry. + */ + + nm_i = kring->nr_hwcur; + if (nm_i != head) { /* we have new packets to send */ + nic_i = netmap_idx_k2n(kring, nm_i); + + __builtin_prefetch(&ring->slot[nm_i]); + __builtin_prefetch(ICE_TX_DESC(txr, nic_i)); + + for (n = 0; nm_i != head; n++) { + struct netmap_slot *slot = &ring->slot[nm_i]; + u_int len = slot->len; + uint64_t paddr; + uint64_t offset = nm_get_offset(kring, slot); + + /* device-specific */ + struct ice_tx_desc *curr = ICE_TX_DESC(txr, nic_i); + u64 hw_flags = 0; + + /* prefetch for next round */ + __builtin_prefetch(&ring->slot[nm_i + 1]); + __builtin_prefetch(ICE_TX_DESC(txr, nic_i)); + + PNMB(na, slot, &paddr); + NM_CHECK_ADDR_LEN_OFF(na, len, offset); + + if (!(slot->flags & NS_MOREFRAG)) { + hw_flags |= ((u64)(ICE_TX_DESC_CMD_EOP) << + ICE_TXD_QW1_CMD_S); + //if (slot->flags & NS_REPORT || nic_i == 0 || + // nic_i == report_frequency) { + // hw_flags |= ((u64)ICE_TX_DESC_CMD_RS << + // ICE_TXD_QW1_CMD_S); + //} + } + hw_flags |= ((u64)ICE_TX_DESC_CMD_RS << ICE_TXD_QW1_CMD_S); + if (slot->flags & NS_BUF_CHANGED) { + /* buffer has changed, reload map */ + //netmap_reload_map(na, txr->dma.tag, txbuf->map, addr); + } + slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED | NS_MOREFRAG); + + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, len, NR_TX); + /* Fill the slot in the NIC ring. + * (we should investigate if using legacy descriptors + * is faster). */ + curr->buf_addr = htole64(paddr + offset); + curr->cmd_type_offset_bsz = htole64( + ((u64)len << ICE_TXD_QW1_TX_BUF_SZ_S) | + hw_flags // TODO + ); /* more flags may be needed */ + + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + } + kring->nr_hwcur = head; + + /* synchronize the NIC ring */ + //bus_dmamap_sync(txr->dma.tag, txr->dma.map, + // BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* (re)start the tx unit up to slot nic_i (excluded) */ + wmb(); + writel(nic_i, txr->tail); + } + + /* + * Second part: reclaim buffers for completed transmissions. + */ + nic_i = txr->next_to_clean; + nm_i = netmap_idx_n2k(kring, nic_i); + for (n = 0; ; n++) { + struct ice_tx_desc *curr = ICE_TX_DESC(txr, nic_i); + struct netmap_slot *slot; + uint64_t paddr; + + if (!(curr->cmd_type_offset_bsz & + cpu_to_le64(ICE_TX_DESC_DTYPE_DESC_DONE))) + break; + curr->buf_addr = 0; + curr->cmd_type_offset_bsz = 0; + slot = &ring->slot[nm_i]; + (void)PNMB_O(kring, slot, &paddr); + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, + &paddr, slot->len, NR_TX); + + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + } + if (n) { + txr->next_to_clean = nic_i; + kring->nr_hwtail = nm_prev(nm_i, lim); + } + + return 0; +} + +/* + * Reconcile kernel and user view of the receive ring. + * Same as for the txsync, this routine must be efficient. + * The caller guarantees a single invocations, but races against + * the rest of the driver should be handled here. + * + * On call, kring->rhead is the first packet that userspace wants + * to keep, and kring->rcur is the wakeup point. + * The kernel has previously reported packets up to kring->rtail. + * + * If (flags & NAF_FORCE_READ) also check for incoming packets irrespective + * of whether or not we received an interrupt. + */ +static int +ice_netmap_rxsync(struct netmap_kring *kring, int flags) +{ + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *ring = kring->ring; + u_int nm_i; /* index into the netmap ring */ + u_int nic_i; /* index into the NIC ring */ + u_int ntail; /* new tail for the user */ + u_int n; + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + int force_update = (flags & NAF_FORCE_READ) || kring->nr_kflags & NKR_PENDINTR; + + /* device-specific */ + struct ice_netdev_priv *np = netdev_priv(ifp); + struct ice_vsi *vsi = np->vsi; + struct NM_ICE_RXRING *rxr; + + if (!netif_running(ifp)) + return 0; + + rxr = vsi->rx_rings[kring->ring_id]; + if (unlikely(!rxr || !rxr->desc)) { + nm_prlim(1, "ring %s is missing (rxr=%p)", kring->name, rxr); + return ENXIO; + } + + if (head > lim) + return netmap_ring_reinit(kring); + + /* XXX check sync modes */ + //bus_dmamap_sync(rxr->dma.tag, rxr->dma.map, + // BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + /* + * First part: import newly received packets. + * + * nm_i is the index of the next free slot in the netmap ring, + * nic_i is the index of the next received packet in the NIC ring, + * and they may differ in case if_init() has been called while + * in netmap mode. For the receive ring we have + * + * nic_i = rxr->next_check; + * nm_i = kring->nr_hwtail (previous) + * and + * nm_i == (nic_i + kring->nkr_hwofs) % ring_size + * + * rxr->next_check is set to 0 on a ring reinit + */ + if (netmap_no_pendintr || force_update) { + int crclen = ix_crcstrip ? 0 : 4; + int complete; + + nic_i = rxr->next_to_clean; // or also k2n(kring->nr_hwtail) + nm_i = netmap_idx_n2k(kring, nic_i); + /* we advance tail only when we see a complete packet */ + ntail = lim + 1; + complete = 0; + + for (n = 0; ; n++) { + union ice_32b_rx_flex_desc *curr = ICE_RX_DESC(rxr, nic_i); + uint16_t stat_err_bits; + uint16_t slot_flags = 0; + struct netmap_slot *slot; + uint64_t paddr; + + if (likely(complete)) { + ntail = nm_i; + complete = 0; + } + + stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_DD_S); + /* XXX ice_test_staterr() needs ice_txrx_lib.h */ + if ((curr->wb.status_error0 & + cpu_to_le16(stat_err_bits)) == 0) + break; + dma_rmb(); + slot = ring->slot + nm_i; + slot->len = (le16_to_cpu(curr->wb.pkt_len) & + ICE_RX_FLX_DESC_PKT_LEN_M) - crclen; + + /* XXX based on ice_is_non_eop() */ + if (likely(curr->wb.status_error0 & + cpu_to_le16(BIT(ICE_RX_FLEX_DESC_STATUS0_EOF_S)))) { + complete = 1; + } else { + slot_flags = NS_MOREFRAG; + } + + slot->flags = slot_flags; + PNMB_O(kring, slot, &paddr); + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, + &paddr, slot->len, NR_RX); + + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + } + if (n) { /* update the state variables */ + rxr->next_to_clean = nic_i; + if (likely(ntail <= lim)) { + kring->nr_hwtail = ntail; + nm_prdis("%s: nic_i %u nm_i %u ntail %u n %u", if_name(ifp), nic_i, nm_i, ntail, n); + } + } + kring->nr_kflags &= ~NKR_PENDINTR; + } + + /* + * Second part: skip past packets that userspace has released. + * (kring->nr_hwcur to kring->rhead excluded), + * and make the buffers available for reception. + * As usual nm_i is the index in the netmap ring, + * nic_i is the index in the NIC ring, and + * nm_i == (nic_i + kring->nkr_hwofs) % ring_size + */ + nm_i = kring->nr_hwcur; + if (nm_i != head) { + nic_i = netmap_idx_k2n(kring, nm_i); + for (n = 0; nm_i != head; n++) { + struct netmap_slot *slot = &ring->slot[nm_i]; + uint64_t paddr; + void *addr = PNMB(na, slot, &paddr); + uint64_t offset = nm_get_offset(kring, slot); + + union ice_32b_rx_flex_desc *curr = ICE_RX_DESC(rxr, nic_i); + + if (addr == NETMAP_BUF_BASE(na)) /* bad buf */ + goto ring_reset; + + if (slot->flags & NS_BUF_CHANGED) { + /* buffer has changed, reload map */ + //netmap_reload_map(na, rxr->ptag, rxbuf->pmap, addr); + slot->flags &= ~NS_BUF_CHANGED; + } + curr->read.pkt_addr = htole64(paddr + offset); + curr->read.hdr_addr = 0; // XXX needed + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, NETMAP_BUF_SIZE(na), NR_RX); + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + } + kring->nr_hwcur = head; + + /* + * IMPORTANT: we must leave one free slot in the ring, + * so move nic_i back by one unit + */ + nic_i = nm_prev(nic_i, lim); + wmb(); + writel(nic_i, rxr->tail); + } + + return 0; + +ring_reset: + return netmap_ring_reinit(kring); +} + +/* + * Register/unregister. We are already under netmap lock. + * Only called on the first register or the last unregister. + */ +static int +ice_netmap_reg(struct netmap_adapter *na, int onoff) +{ + struct ifnet *ifp = na->ifp; + struct ice_netdev_priv *np = netdev_priv(ifp); + struct ice_vsi *vsi = np->vsi; + struct ice_pf *pf = (struct ice_pf *)vsi->back; + bool was_running = false; + + while (ice_is_reset_in_progress(pf->state)) { + usleep_range(1000, 2000); + } + + if (!test_and_set_bit(ICE_VSI_DOWN, vsi->state)) { + was_running = true; + ice_down(vsi); + } + + //set_crcstrip(&adapter->hw, onoff); + /* enable or disable flags and callbacks in na and ifp */ + if (onoff) { + nm_set_native_flags(na); + } else { + nm_clear_native_flags(na); + } + if (was_running) { + ice_up(vsi); + } + //set_crcstrip(&adapter->hw, onoff); // XXX why twice ? + + return 0; +} + +static int +ice_netmap_bufcfg(struct netmap_kring *kring, uint64_t target) +{ + uint64_t incr; + + kring->buf_align = 0; + + if (kring->tx == NR_TX) { + kring->hwbuf_len = target; + return 0; + } + + incr = 1UL << ICE_RLAN_CTX_DBUF_S; + target &= ~(incr - 1); + if (target < 1024UL || target > 16384UL - incr) + return EINVAL; + + kring->hwbuf_len = target; + + return 0; +} + +static int +ice_netmap_config(struct netmap_adapter *na, struct nm_config_info *info) +{ + int ret = netmap_rings_config_get(na, info); + + if (ret) { + return ret; + } + + info->rx_buf_maxsize = NETMAP_BUF_SIZE(na); + + return 0; +} + +/* + * The attach routine, called near the end of ice_attach(), + * fills the parameters for netmap_attach() and calls it. + * It cannot fail, in the worst case (such as no memory) + * netmap mode will be disabled and the driver will only + * operate in standard mode. + */ +static void +ice_netmap_attach(struct ice_pf *pf) +{ + struct ice_vsi *vsi; + struct netmap_adapter na; + + bzero(&na, sizeof(na)); + + vsi = ice_get_main_vsi(pf); + if (!vsi || !vsi->netdev) { + nm_prerr("null %s, attach failed", vsi ? "vsi->netdev" : "vsi"); + return; + } + na.ifp = vsi->netdev; + na.pdev = &vsi->back->pdev->dev; + na.na_flags = NAF_MOREFRAG | NAF_OFFSETS; + na.num_tx_desc = vsi->tx_rings[0]->count; + na.num_rx_desc = vsi->rx_rings[0]->count; + na.num_tx_rings = vsi->num_txq; + na.num_rx_rings = vsi->num_rxq; + na.rx_buf_maxsize = vsi->rx_rings[0]->rx_buf_len; + na.nm_txsync = ice_netmap_txsync; + na.nm_rxsync = ice_netmap_rxsync; + na.nm_register = ice_netmap_reg; + na.nm_config = ice_netmap_config; + na.nm_bufcfg = ice_netmap_bufcfg; + netmap_attach(&na); +} + +static void +ice_netmap_detach(struct ice_pf *pf) +{ + struct ice_vsi *vsi; + + vsi = ice_get_main_vsi(pf); + if (!vsi || !vsi->netdev) + return; + netmap_detach(vsi->netdev); +} + +#endif // NETMAP_ICE_LIB + +#ifdef NETMAP_ICE_BASE + +#define ice_driver_name netmap_ice_driver_name +char ice_driver_name[] = "ice" NETMAP_LINUX_DRIVER_SUFFIX; +/* + * device-specific sysctl variables: + * + * ix_crcstrip: 0: NIC keeps CRC in rx frames (default), 1: NIC strips it. + * During regular operations the CRC is stripped, but on some + * hardware reception of frames not multiple of 64 is slower, + * so using crcstrip=0 helps in benchmarks. + * The driver by default strips CRCs and we do not override it. + * + */ +SYSCTL_DECL(_dev_netmap); +int ix_crcstrip = 1; +SYSCTL_INT(_dev_netmap, OID_AUTO, ix_crcstrip, + CTLFLAG_RW, &ix_crcstrip, 1, "NIC strips CRC on rx frames"); + +static void +ice_netmap_configure_tx_ring(struct NM_ICE_TXRING *ring) +{ + struct netmap_adapter *na; + + if (!ring->netdev) { + // XXX it this possible? + return; + } + + na = NA(ring->netdev); + netmap_reset(na, NR_TX, ring->q_index, 0); +} + +static void +ice_netmap_preconfigure_rx_ring(struct NM_ICE_RXRING *ring, + struct ice_rlan_ctx *rx_ctx) +{ + struct netmap_adapter *na; + struct netmap_kring *kring; + + if (!ring->netdev) { + // XXX it this possible? + return; + } + + na = NA(ring->netdev); + + if (netmap_reset(na, NR_RX, ring->q_index, 0) == NULL) + return; // not in native netmap mode + + kring = na->rx_rings[ring->q_index]; + rx_ctx->dbuf = kring->hwbuf_len >> ICE_RLAN_CTX_DBUF_S; +} + +static int +ice_netmap_configure_rx_ring(struct NM_ICE_RXRING *ring) +{ + struct netmap_adapter *na; + struct netmap_slot *slot; + struct netmap_kring *kring; + int lim, i, ring_nr; + + if (!ring->netdev) { + // XXX it this possible? + return 0; + } + + na = NA(ring->netdev); + ring_nr = ring->q_index; + + slot = netmap_reset(na, NR_RX, ring_nr, 0); + if (!slot) + return 0; // not in native netmap mode + + kring = na->rx_rings[ring_nr]; + lim = na->num_rx_desc - 1 - nm_kr_rxspace(kring); + + for (i = 0; i <= lim; i++) { + int si = netmap_idx_n2k(kring, i); + uint64_t paddr; + union ice_32b_rx_flex_desc *rx = ICE_RX_DESC(ring, i); + PNMB_O(kring, slot + si, &paddr); + + rx->read.pkt_addr = htole64(paddr); + rx->read.hdr_addr = 0; + } + ring->next_to_clean = 0; + wmb(); + writel(lim, ring->tail); + return 1; +} + +#endif /* NETMAP_ICE_BASE */ + +/* end of file */ diff --git a/LINUX/if_e1000_netmap.h b/LINUX/if_e1000_netmap.h index b88e42246..454c76f6c 100644 --- a/LINUX/if_e1000_netmap.h +++ b/LINUX/if_e1000_netmap.h @@ -62,6 +62,7 @@ e1000_netmap_reg(struct netmap_adapter *na, int onoff) } else { nm_clear_native_flags(na); } + netmap_krings_mode_commit(na, onoff); if (netif_running(adapter->netdev)) e1000_up(adapter); else @@ -100,8 +101,6 @@ e1000_netmap_txsync(struct netmap_kring *kring, int flags) u_int n; u_int const lim = kring->nkr_num_slots - 1; u_int const head = kring->rhead; - /* generate an interrupt approximately every half ring */ - u_int report_frequency = kring->nkr_num_slots >> 1; /* device-specific */ struct SOFTC_T *adapter = netdev_priv(ifp); @@ -123,28 +122,28 @@ e1000_netmap_txsync(struct netmap_kring *kring, int flags) struct netmap_slot *slot = &ring->slot[nm_i]; u_int len = slot->len; uint64_t paddr; - void *addr = PNMB(na, slot, &paddr); + uint64_t offset = nm_get_offset(kring, slot); /* device-specific */ struct e1000_tx_desc *curr = E1000_TX_DESC(*txr, nic_i); - int flags = (slot->flags & NS_REPORT || - nic_i == 0 || nic_i == report_frequency) ? - E1000_TXD_CMD_RS : 0; + int hw_flags = E1000_TXD_CMD_IFCS; - NM_CHECK_ADDR_LEN(na, addr, len); + PNMB(na, slot, &paddr); + NM_CHECK_ADDR_LEN_OFF(na, len, offset); - if (slot->flags & NS_BUF_CHANGED) { - /* buffer has changed, reload map */ - // netmap_reload_map(pdev, DMA_TO_DEVICE, old_addr, paddr); - curr->buffer_addr = htole64(paddr); + if (!(slot->flags & NS_MOREFRAG)) { + hw_flags |= adapter->txd_cmd; + /* For now E1000_TXD_CMD_RS is always set. + * We may set it only if NS_REPORT is set or + * at least once every half ring. */ } - slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED); + slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED | NS_MOREFRAG); + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, &paddr, len, NR_TX); /* Fill the slot in the NIC ring. */ + curr->buffer_addr = htole64(paddr + offset); curr->upper.data = 0; - curr->lower.data = htole32(adapter->txd_cmd | - len | flags | - E1000_TXD_CMD_EOP | E1000_TXD_CMD_IFCS); + curr->lower.data = htole32(len | hw_flags); nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); } @@ -154,20 +153,33 @@ e1000_netmap_txsync(struct netmap_kring *kring, int flags) txr->next_to_use = nic_i; /* XXX what for ? */ /* (re)start the tx unit up to slot nic_i (excluded) */ writel(nic_i, adapter->hw.hw_addr + txr->tdt); - mmiowb(); // XXX where do we need this ? + wmb(); // XXX where do we need this ? } /* * Second part: reclaim buffers for completed transmissions. */ if (flags & NAF_FORCE_RECLAIM || nm_kr_txempty(kring)) { + u_int tosync; + /* record completed transmissions using TDH */ nic_i = readl(adapter->hw.hw_addr + txr->tdh); - if (nic_i >= kring->nkr_num_slots) { /* XXX can it happen ? */ - D("TDH wrap %d", nic_i); + if (unlikely(nic_i >= kring->nkr_num_slots)) { + nm_prerr("TDH wrap %d", nic_i); nic_i -= kring->nkr_num_slots; } + nm_i = netmap_idx_n2k(kring, nic_i); txr->next_to_clean = nic_i; + tosync = nm_next(kring->nr_hwtail, lim); + /* sync all buffers that we are returning to userspace */ + for ( ; tosync != nm_i; tosync = nm_next(tosync, lim)) { + struct netmap_slot *slot = &ring->slot[tosync]; + uint64_t paddr; + (void)PNMB_O(kring, slot, &paddr); + + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, + &paddr, slot->len, NR_TX); + } kring->nr_hwtail = nm_prev(netmap_idx_n2k(kring, nic_i), lim); } out: @@ -210,7 +222,7 @@ e1000_netmap_rxsync(struct netmap_kring *kring, int flags) * First part: import newly received packets. */ if (netmap_no_pendintr || force_update) { - uint16_t slot_flags = kring->nkr_slot_flags; + u_int new_hwtail = (u_int)-1; nic_i = rxr->next_to_clean; nm_i = netmap_idx_n2k(kring, nic_i); @@ -218,17 +230,35 @@ e1000_netmap_rxsync(struct netmap_kring *kring, int flags) for (n = 0; ; n++) { struct e1000_rx_desc *curr = E1000_RX_DESC(*rxr, nic_i); uint32_t staterr = le32toh(curr->status); + struct netmap_slot *slot; + uint64_t paddr; + int complete = 0; if ((staterr & E1000_RXD_STAT_DD) == 0) break; - ring->slot[nm_i].len = le16toh(curr->length) - 4; - ring->slot[nm_i].flags = slot_flags; + dma_rmb(); /* read descriptor after status DD */ + + slot = ring->slot + nm_i; + PNMB_O(kring, slot, &paddr); + slot->len = le16toh(curr->length); + slot->flags = NS_MOREFRAG; + if (staterr & E1000_RXD_STAT_EOP) { + slot->len -= 4; /* exclude the CRC */ + slot->flags = 0; + complete = 1; + } + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, + &paddr, slot->len, NR_RX); nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); + + if (complete) + new_hwtail = nm_i; } if (n) { /* update the state variables */ rxr->next_to_clean = nic_i; - kring->nr_hwtail = nm_i; + if (new_hwtail != (u_int)-1) + kring->nr_hwtail = nm_i; } kring->nr_kflags &= ~NKR_PENDINTR; } @@ -247,15 +277,20 @@ e1000_netmap_rxsync(struct netmap_kring *kring, int flags) if (addr == NETMAP_BUF_BASE(na)) /* bad buf */ goto ring_reset; - if (slot->flags & NS_BUF_CHANGED) { - // netmap_reload_map(...) - curr->buffer_addr = htole64(paddr); + if (slot->flags & NS_BUF_CHANGED || kring->nkr_to_refill) { + uint64_t offset = nm_get_offset(kring, slot); + curr->buffer_addr = htole64(paddr + offset); slot->flags &= ~NS_BUF_CHANGED; + kring->nkr_to_refill--; } + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, NETMAP_BUF_SIZE(na), NR_RX); curr->status = 0; nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); } + if (kring->nkr_to_refill < 0) + kring->nkr_to_refill = 0; kring->nr_hwcur = head; rxr->next_to_use = nic_i; // XXX not really used wmb(); @@ -274,6 +309,66 @@ e1000_netmap_rxsync(struct netmap_kring *kring, int flags) return netmap_ring_reinit(kring); } +struct e1000_netmap_szdesc { + uint32_t bufsize; + uint32_t rctl; +}; + +#define E1000_NETMAP_RCTL_MASK 0x7A030000 +static struct e1000_netmap_szdesc e1000_netmap_bufsize[] = { + { 16384, 0x02010000}, + { 8192, 0x02020000}, + { 4096, 0x02030000}, + { 2048, 0x00000000}, + { 1024, 0x00010000}, + { 512, 0x00020000}, + { 256, 0x00030000}, + { 0, 0}, +}; + +static uint32_t +e1000_netmap_get_rctl(uint32_t bufsize) +{ + struct e1000_netmap_szdesc *sz; + + for (sz = e1000_netmap_bufsize; sz->bufsize; sz++) + if (bufsize == sz->bufsize) + return sz->rctl; + + return ((bufsize >> 10) & 0xF) << 27; +} + +static int +e1000_netmap_bufcfg(struct netmap_kring *kring, uint64_t target) +{ + uint64_t bufsz; + struct e1000_netmap_szdesc *sz; + + if (kring->tx == NR_TX) { + kring->hwbuf_len = target; + return 0; + } + + bufsz = 0; + for (sz = e1000_netmap_bufsize; sz->bufsize; sz++) + if (sz->bufsize <= target) { + bufsz = sz->bufsize; + break; + } + if (!bufsz) + return EINVAL; + /* check if we can find a better size using 1K increments */ + target >>= 10; + if (target >= 1 && target <= 15) { + target <<= 10; + if (target > bufsz) + bufsz = target; + } + kring->hwbuf_len = bufsz; + kring->buf_align = 0; /* no alignment */ + nm_prinf("%s: hwbuf_len %llu", kring->name, kring->hwbuf_len); + return 0; +} /* * Make the tx and rx rings point to the netmap buffers. @@ -283,57 +378,66 @@ static int e1000_netmap_init_buffers(struct SOFTC_T *adapter) struct e1000_hw *hw = &adapter->hw; struct ifnet *ifp = adapter->netdev; struct netmap_adapter* na = NA(ifp); + struct netmap_kring *kring; struct netmap_slot* slot; - struct e1000_tx_ring* txr = &adapter->tx_ring[0]; - unsigned int i, r, si; + unsigned int i, r, si, n; uint64_t paddr; + uint32_t rctl; if (!nm_native_on(na)) return 0; for (r = 0; r < na->num_rx_rings; r++) { struct e1000_rx_ring *rxr; + kring = na->rx_rings[r]; slot = netmap_reset(na, NR_RX, r, 0); if (!slot) { - D("Skipping RX ring %d, netmap mode not requested", r); + nm_prinf("Skipping RX ring %d, netmap mode not requested", r); continue; } rxr = &adapter->rx_ring[r]; - for (i = 0; i < rxr->count; i++) { - si = netmap_idx_n2k(&na->rx_rings[r], i); - PNMB(na, slot + si, &paddr); - // netmap_load_map(...) + /* preserve buffers already made available to clients */ + kring->nkr_to_refill = nm_kr_rxspace(kring); + n = rxr->count - 1 - kring->nkr_to_refill; + + for (i = 0; i < n; i++) { + si = netmap_idx_n2k(kring, i); + PNMB_O(kring, slot + si, &paddr); E1000_RX_DESC(*rxr, i)->buffer_addr = htole64(paddr); } rxr->next_to_use = 0; - /* preserve buffers already made available to clients */ - i = rxr->count - 1 - nm_kr_rxspace(&na->rx_rings[0]); - if (i < 0) // XXX something wrong here, can it really happen ? - i += rxr->count; - D("i now is %d", i); + + /* program the RCTL */ + rctl = er32(RCTL); + rctl = (rctl & ~E1000_NETMAP_RCTL_MASK) | + e1000_netmap_get_rctl(kring->hwbuf_len); + ew32(RCTL, rctl); + wmb(); /* Force memory writes to complete */ - writel(i, hw->hw_addr + rxr->rdt); + writel(n, hw->hw_addr + rxr->rdt); } - /* now initialize the tx ring(s) */ - for (r = 0; r < na->num_tx_rings; r++) { - slot = netmap_reset(na, NR_TX, r, 0); - if (!slot) { - D("Skipping TX ring %d, netmap mode not requested", r); - continue; - } + /* no need to initialize the tx rings, since txsync will always + * overwrite the tx slots + */ - for (i = 0; i < na->num_tx_desc; i++) { - si = netmap_idx_n2k(&na->tx_rings[r], i); - PNMB(na, slot + si, &paddr); - // netmap_load_map(...) - E1000_TX_DESC(*txr, i)->buffer_addr = htole64(paddr); - } + return 1; +} + +static int +e1000_netmap_config(struct netmap_adapter *na, struct nm_config_info *info) +{ + int ret = netmap_rings_config_get(na, info); + + if (ret) { + return ret; } - return 1; + info->rx_buf_maxsize = NETMAP_BUF_SIZE(na); + + return 0; } static void @@ -345,13 +449,17 @@ e1000_netmap_attach(struct SOFTC_T *adapter) na.ifp = adapter->netdev; na.pdev = &adapter->pdev->dev; + na.na_flags = NAF_MOREFRAG | NAF_OFFSETS; na.num_tx_desc = adapter->tx_ring[0].count; na.num_rx_desc = adapter->rx_ring[0].count; + na.num_tx_rings = na.num_rx_rings = 1; + na.rx_buf_maxsize = adapter->rx_buffer_len; na.nm_register = e1000_netmap_reg; na.nm_txsync = e1000_netmap_txsync; na.nm_rxsync = e1000_netmap_rxsync; - na.num_tx_rings = na.num_rx_rings = 1; na.nm_intr = e1000_netmap_intr; + na.nm_config = e1000_netmap_config; + na.nm_bufcfg = e1000_netmap_bufcfg; netmap_attach(&na); } diff --git a/LINUX/if_e1000e_netmap.h b/LINUX/if_e1000e_netmap.h index 8afefe34f..a9a80301f 100644 --- a/LINUX/if_e1000e_netmap.h +++ b/LINUX/if_e1000e_netmap.h @@ -48,13 +48,13 @@ char netmap_e1000e_driver_name[] = "e1000e" NETMAP_LINUX_DRIVER_SUFFIX; * Adaptation to different versions of the driver. */ #ifdef NETMAP_LINUX_HAVE_E1000E_EXT_RXDESC -#warning this driver uses extended descriptors +//#warning this driver uses extended descriptors #define NM_E1K_RX_DESC_T union e1000_rx_desc_extended #define NM_E1R_RX_STATUS wb.upper.status_error #define NM_E1R_RX_LENGTH wb.upper.length #define NM_E1R_RX_BUFADDR read.buffer_addr #else -#warning this driver uses regular descriptors +//#warning this driver uses regular descriptors #define E1000_RX_DESC_EXT E1000_RX_DESC // XXX workaround #define NM_E1K_RX_DESC_T struct e1000_rx_desc #define NM_E1R_RX_STATUS status @@ -62,9 +62,10 @@ char netmap_e1000e_driver_name[] = "e1000e" NETMAP_LINUX_DRIVER_SUFFIX; #define NM_E1R_RX_LENGTH length #endif /* up to 3.2.x */ +/* Macros to write to the head and tail registers of TX and RX rings. */ #ifndef NETMAP_LINUX_HAVE_E1000E_HWADDR -#define NM_WR_TX_TAIL(_x) writel(_x, txr->tail) // XXX tx_ring -#define NM_WR_RX_TAIL(_x) writel(_x, rxr->tail) // XXX rx_ring +#define NM_WR_TX_TAIL(_x) writel(_x, txr->tail) +#define NM_WR_RX_TAIL(_x) writel(_x, rxr->tail) #define NM_RD_TX_HEAD() readl(txr->head) #else #define NM_WR_TX_TAIL(_x) writel(_x, adapter->hw.hw_addr + txr->tail) @@ -111,6 +112,66 @@ e1000_netmap_reg(struct netmap_adapter *na, int onoff) return (0); } +struct e1000e_netmap_szdesc { + uint32_t bufsize; + uint32_t rctl; +}; + +#define E1000_NETMAP_RCTL_MASK 0x7A030000 +static struct e1000e_netmap_szdesc e1000e_netmap_bufsize[] = { + { 16384, 0x02010000}, + { 8192, 0x02020000}, + { 4096, 0x02030000}, + { 2048, 0x00000000}, + { 1024, 0x00010000}, + { 512, 0x00020000}, + { 256, 0x00030000}, + { 0, 0}, +}; + +static uint32_t +e1000e_netmap_get_rctl(uint32_t bufsize) +{ + struct e1000e_netmap_szdesc *sz; + + for (sz = e1000e_netmap_bufsize; sz->bufsize; sz++) + if (bufsize == sz->bufsize) + return sz->rctl; + + return ((bufsize >> 10) & 0xF) << 27; +} + +static int +e1000e_netmap_bufcfg(struct netmap_kring *kring, uint64_t target) +{ + uint64_t bufsz; + struct e1000e_netmap_szdesc *sz; + + if (kring->tx == NR_TX) { + kring->hwbuf_len = target; + return 0; + } + + bufsz = 0; + for (sz = e1000e_netmap_bufsize; sz->bufsize; sz++) + if (sz->bufsize <= target) { + bufsz = sz->bufsize; + break; + } + if (!bufsz) + return EINVAL; + /* check if we can find a better size using 1K increments */ + target >>= 10; + if (target >= 1 && target <= 15) { + target <<= 10; + if (target > bufsz) + bufsz = target; + } + kring->hwbuf_len = bufsz; + kring->buf_align = 0; /* no alignment */ + nm_prinf("%s: hwbuf_len %llu", kring->name, kring->hwbuf_len); + return 0; +} /* * Reconcile kernel and user view of the transmit ring. @@ -127,8 +188,6 @@ e1000_netmap_txsync(struct netmap_kring *kring, int flags) u_int n; u_int const lim = kring->nkr_num_slots - 1; u_int const head = kring->rhead; - /* generate an interrupt approximately every half ring */ - u_int report_frequency = kring->nkr_num_slots >> 1; /* device-specific */ struct SOFTC_T *adapter = netdev_priv(ifp); @@ -150,27 +209,28 @@ e1000_netmap_txsync(struct netmap_kring *kring, int flags) struct netmap_slot *slot = &ring->slot[nm_i]; u_int len = slot->len; uint64_t paddr; - void *addr = PNMB(na, slot, &paddr); + uint64_t offset = nm_get_offset(kring, slot); /* device-specific */ struct e1000_tx_desc *curr = E1000_TX_DESC(*txr, nic_i); - int flags = (slot->flags & NS_REPORT || - nic_i == 0 || nic_i == report_frequency) ? - E1000_TXD_CMD_RS : 0; + int hw_flags = E1000_TXD_CMD_IFCS; - NM_CHECK_ADDR_LEN(na, addr, len); + PNMB(na, slot, &paddr); + NM_CHECK_ADDR_LEN_OFF(na, len, offset); - if (slot->flags & NS_BUF_CHANGED) { - /* buffer has changed, reload map */ - // netmap_reload_map(pdev, DMA_TO_DEVICE, old_paddr, addr) - curr->buffer_addr = htole64(paddr); + if (!(slot->flags & NS_MOREFRAG)) { + hw_flags |= adapter->txd_cmd; + /* For now E1000_TXD_CMD_RS is always set. + * We may set it only if NS_REPORT is set or + * at least once every half ring. */ } - slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED); + slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED | NS_MOREFRAG); + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, &paddr, len, NR_TX); /* Fill the slot in the NIC ring. */ + curr->buffer_addr = htole64(paddr + offset); curr->upper.data = 0; - curr->lower.data = htole32(adapter->txd_cmd | len | flags | - E1000_TXD_CMD_EOP); + curr->lower.data = htole32(len | hw_flags); nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); } @@ -178,23 +238,39 @@ e1000_netmap_txsync(struct netmap_kring *kring, int flags) wmb(); /* synchronize writes to the NIC ring */ - txr->next_to_use = nic_i; + txr->next_to_use = nic_i; /* for consistency */ NM_WR_TX_TAIL(nic_i); - mmiowb(); // XXX where do we need this ? + wmb(); /* needed after writing to TX ring tail */ } /* * Second part: reclaim buffers for completed transmissions. */ if (flags & NAF_FORCE_RECLAIM || nm_kr_txempty(kring)) { - /* record completed transmissions using TDH */ - nic_i = NM_RD_TX_HEAD(); // XXX could scan descriptors ? - if (nic_i >= kring->nkr_num_slots) { /* XXX can it happen ? */ - D("TDH wrap %d", nic_i); + u_int tosync; + + /* Record completed transmissions using TDH. + * Alternative approach would be to scan descriptors and read + * the DD bit until we found one that is not set. */ + nic_i = NM_RD_TX_HEAD(); + if (unlikely(nic_i >= kring->nkr_num_slots)) { + /* This should never happen. */ + nm_prerr("TDH wrap at idx %d", nic_i); nic_i -= kring->nkr_num_slots; } + nm_i = netmap_idx_n2k(kring, nic_i); txr->next_to_clean = nic_i; - kring->nr_hwtail = nm_prev(netmap_idx_n2k(kring, nic_i), lim); + tosync = nm_next(kring->nr_hwtail, lim); + /* sync all buffers that we are returning to userspace */ + for ( ; tosync != nm_i; tosync = nm_next(tosync, lim)) { + struct netmap_slot *slot = &ring->slot[tosync]; + uint64_t paddr; + (void)PNMB_O(kring, slot, &paddr); + + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, + &paddr, slot->len, NR_TX); + } + kring->nr_hwtail = nm_prev(nm_i, lim); } out: @@ -235,8 +311,8 @@ e1000_netmap_rxsync(struct netmap_kring *kring, int flags) * First part: import newly received packets. */ if (netmap_no_pendintr || force_update) { - uint16_t slot_flags = kring->nkr_slot_flags; int strip_crc = (adapter->flags2 & FLAG2_CRC_STRIPPING) ? 0 : 4; + u_int new_hwtail = (u_int)-1; nic_i = rxr->next_to_clean; nm_i = netmap_idx_n2k(kring, nic_i); @@ -244,17 +320,29 @@ e1000_netmap_rxsync(struct netmap_kring *kring, int flags) for (n = 0; ; n++) { NM_E1K_RX_DESC_T *curr = E1000_RX_DESC_EXT(*rxr, nic_i); uint32_t staterr = le32toh(curr->NM_E1R_RX_STATUS); + struct netmap_slot *slot = &ring->slot[nm_i]; + uint64_t paddr; + int complete; if ((staterr & E1000_RXD_STAT_DD) == 0) break; - ring->slot[nm_i].len = le16toh(curr->NM_E1R_RX_LENGTH) - strip_crc; - ring->slot[nm_i].flags = slot_flags; + dma_rmb(); /* read descriptor after status DD */ + PNMB_O(kring, slot, &paddr); + slot->len = le16toh(curr->NM_E1R_RX_LENGTH) - strip_crc; + complete = staterr & E1000_RXD_STAT_EOP; + slot->flags = complete ? NS_MOREFRAG : 0; + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, &paddr, + slot->len, NR_RX); nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); + + if (complete) + new_hwtail = nm_i; } if (n) { /* update the state variables */ rxr->next_to_clean = nic_i; - kring->nr_hwtail = nm_i; + if (new_hwtail != (u_int)-1) + kring->nr_hwtail = nm_i; } kring->nr_kflags &= ~NKR_PENDINTR; } @@ -273,10 +361,10 @@ e1000_netmap_rxsync(struct netmap_kring *kring, int flags) if (addr == NETMAP_BUF_BASE(na)) /* bad buf */ goto ring_reset; + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, NETMAP_BUF_SIZE(na), NR_RX); curr->NM_E1R_RX_BUFADDR = htole64(paddr); /* reload ext.desc. addr. */ if (slot->flags & NS_BUF_CHANGED) { - /* buffer has changed, reload map */ - // netmap_reload_map(pdev, DMA_TO_DEVICE, old_paddr, addr) slot->flags &= ~NS_BUF_CHANGED; } curr->NM_E1R_RX_STATUS = 0; @@ -284,7 +372,7 @@ e1000_netmap_rxsync(struct netmap_kring *kring, int flags) nic_i = nm_next(nic_i, lim); } kring->nr_hwcur = head; - rxr->next_to_use = nic_i; // XXX not really used + rxr->next_to_use = nic_i; /* for consistency */ wmb(); /* * IMPORTANT: we must leave one free slot in the ring, @@ -305,7 +393,7 @@ e1000_netmap_rxsync(struct netmap_kring *kring, int flags) /* diagnostic routine to catch errors */ static void e1000e_no_rx_alloc(struct SOFTC_T *a, int n) { - D("e1000->alloc_rx_buf should not be called"); + nm_prerr("alloc_rx_buf() should not be called"); } @@ -314,47 +402,70 @@ static void e1000e_no_rx_alloc(struct SOFTC_T *a, int n) */ static int e1000e_netmap_init_buffers(struct SOFTC_T *adapter) { + struct e1000_hw *hw = &adapter->hw; struct ifnet *ifp = adapter->netdev; struct netmap_adapter* na = NA(ifp); + struct netmap_kring *kring; struct netmap_slot* slot; struct e1000_ring *rxr = adapter->rx_ring; - struct e1000_ring *txr = adapter->tx_ring; - int i, si; + int i, si, n; uint64_t paddr; + uint32_t rctl; + + if (!nm_native_on(na)) + return 0; slot = netmap_reset(na, NR_RX, 0, 0); - if (!slot) - return 0; // not in netmap native mode - - adapter->alloc_rx_buf = (void*)e1000e_no_rx_alloc; - for (i = 0; i < rxr->count; i++) { - // XXX the skb check and cleanup can go away - struct e1000_buffer *bi = &rxr->buffer_info[i]; - si = netmap_idx_n2k(&na->rx_rings[0], i); - PNMB(na, slot + si, &paddr); - if (bi->skb) - D("rx buf %d was set", i); - bi->skb = NULL; // XXX leak if set - // netmap_load_map(...) - E1000_RX_DESC_EXT(*rxr, i)->NM_E1R_RX_BUFADDR = htole64(paddr); - } - rxr->next_to_use = 0; - /* preserve buffers already made available to clients */ - i = rxr->count - 1 - nm_kr_rxspace(&na->rx_rings[0]); - wmb(); /* Force memory writes to complete */ - NM_WR_RX_TAIL(i); - - /* now initialize the tx ring */ - slot = netmap_reset(na, NR_TX, 0, 0); - for (i = 0; i < na->num_tx_desc; i++) { - si = netmap_idx_n2k(&na->tx_rings[0], i); - PNMB(na, slot + si, &paddr); - // netmap_load_map(...) - E1000_TX_DESC(*txr, i)->buffer_addr = htole64(paddr); + if (slot) { + kring = na->rx_rings[0]; + /* initialize the RX ring for netmap mode */ + adapter->alloc_rx_buf = (void*)e1000e_no_rx_alloc; + /* preserve buffers already made available to clients */ + n = rxr->count - 1 - nm_kr_rxspace(kring); + for (i = 0; i < n; i++) { + struct e1000_buffer *bi = &rxr->buffer_info[i]; + si = netmap_idx_n2k(kring, i); + PNMB_O(kring, slot + si, &paddr); + if (bi->skb) + nm_prerr("Warning: rx skb still set on slot #%d", i); + E1000_RX_DESC_EXT(*rxr, i)->NM_E1R_RX_BUFADDR = htole64(paddr); + } + rxr->next_to_use = 0; + + /* program the RCTL */ + rctl = er32(RCTL); + rctl = (rctl & ~E1000_NETMAP_RCTL_MASK) | + e1000e_netmap_get_rctl(kring->hwbuf_len); + ew32(RCTL, rctl); + + wmb(); /* Force memory writes to complete */ + NM_WR_RX_TAIL(n); } + + netmap_reset(na, NR_TX, 0, 0); + + /* no need to fill the tx ring, since txsync will always + * overwrite the tx slots + */ + return 1; } +static int +e1000e_netmap_config(struct netmap_adapter *na, struct nm_config_info *info) +{ + struct SOFTC_T *adapter = netdev_priv(na->ifp); + int ret = netmap_rings_config_get(na, info); + + if (ret) { + return ret; + } + + info->rx_buf_maxsize = adapter->rx_buffer_len; + + return 0; +} + static void e1000_netmap_attach(struct SOFTC_T *adapter) @@ -365,12 +476,16 @@ e1000_netmap_attach(struct SOFTC_T *adapter) na.ifp = adapter->netdev; na.pdev = &adapter->pdev->dev; + na.na_flags = NAF_MOREFRAG | NAF_OFFSETS; na.num_tx_desc = adapter->tx_ring->count; na.num_rx_desc = adapter->rx_ring->count; + na.num_tx_rings = na.num_rx_rings = 1; + na.rx_buf_maxsize = adapter->rx_buffer_len; na.nm_register = e1000_netmap_reg; na.nm_txsync = e1000_netmap_txsync; na.nm_rxsync = e1000_netmap_rxsync; - na.num_tx_rings = na.num_rx_rings = 1; + na.nm_config = e1000e_netmap_config; + na.nm_bufcfg = e1000e_netmap_bufcfg; netmap_attach(&na); } diff --git a/LINUX/if_igb_netmap.h b/LINUX/if_igb_netmap.h index 2cc556a3c..576a002f0 100644 --- a/LINUX/if_igb_netmap.h +++ b/LINUX/if_igb_netmap.h @@ -45,38 +45,67 @@ char netmap_igb_driver_name[] = "igb" NETMAP_LINUX_DRIVER_SUFFIX; * E1000_TX_DESC_ADV etc. have dropped the _ADV suffix at some point. * Also the first argument is now a pointer not the object. */ -#ifdef NETMAP_LINUX_HAVE_IGB_PHY_OPS -static inline u16 nm_igb_read(struct igb_adapter *adapter, u32 offset) +#ifdef NETMAP_LINUX_HAVE_IGB_RD32 +#define READ_TDH(_adapter, _txr) igb_rd32(&(_adapter)->hw, E1000_TDH((_txr)->reg_idx)) +#define READ_RCTL(_adapter, _rxr) igb_rd32(&(_adapter)->hw, E1000_RXDCTL((_rxr)->reg_idx)) +#elif defined(E1000_READ_REG) +#define READ_TDH(_adapter, _txr) E1000_READ_REG(&(_adapter)->hw, E1000_TDH((_txr)->reg_idx)) +#define READ_RCTL(_adapter, _rxr) E1000_READ_REG(&(_adapter)->hw, E1000_RXDCTL((_rxr)->reg_idx)) +#elif defined rd32 +static inline u32 READ_TDH(struct igb_adapter *adapter, struct igb_ring *txr) { - u16 rv = 0; - if (igb_read_phy_reg(&adapter->hw, offset, &rv)) { - RD(5, "%s: read failure at offset %x", - adapter->netdev->name, offset); - } - return rv; - + struct e1000_hw *hw = &adapter->hw; + return rd32(E1000_TDH(txr->reg_idx)); } -#elif defined(E1000_READ_REG) -static inline u16 nm_igb_read(struct igb_adapter *adapter, u32 offset) +static inline u32 READ_RCTL(struct igb_adapter *adapter, struct igb_ring *rxr) { - return E1000_READ_REG(&adapter->hw, offset); + struct e1000_hw *hw = &adapter->hw; + return rd32(E1000_RXDCTL(rxr->reg_idx)); } #else -#error "I don't know how to read registers in igb" +#define READ_TDH(_adapter, _txr) readl((_txr)->head) +#define READ_RCTL(_adapter, _rxr) readl(E1000_RXDCRL((_rxr)->reg_idx)) +#endif +#ifdef E1000_WRITE_REG +#define NM_WRITE_RCTL(_adapter, _rxr, _rxdctl) \ + E1000_WRITE_REG(&(_adapter)->hw, E1000_RXDCTL((_rxr)->reg_idx), (rxdctl)) +#define NM_WRITE_SRRCTL(_adapter, _rxr, _srrctl) \ + E1000_WRITE_REG(&(_adapter)->hw, E1000_SRRCTL((_rxr)->reg_idx), (srrctl)) +#elif defined(wr32) +static inline void NM_WRITE_RCTL(struct igb_adapter *adapter, struct igb_ring *rxr, + u32 rxdctl) +{ + struct e1000_hw *hw = &adapter->hw; + wr32(E1000_RXDCTL(rxr->reg_idx), rxdctl); +} +static inline void NM_WRITE_SRRCTL(struct igb_adapter *adapter, struct igb_ring *rxr, + u32 srrctl) +{ + struct e1000_hw *hw = &adapter->hw; + wr32(E1000_SRRCTL(rxr->reg_idx), srrctl); +} +#else +#define NM_WRITE_RCTL(_adapter, _rxr, _rxdctl) \ + writel(E1000_RCTL((_rxr)->reg_idx, (_rxdctl)) +#define NM_WRITE_SRRCTL(_adapter, _rxr, _srrctl) \ + writel(E1000_SRRCTL((_rxr)->reg_idx, (_srrctl)) #endif #ifndef E1000_TX_DESC_ADV #define E1000_TX_DESC_ADV(_r, _i) IGB_TX_DESC(&(_r), _i) #define E1000_RX_DESC_ADV(_r, _i) IGB_RX_DESC(&(_r), _i) -#define READ_TDH(_adapter, _txr) nm_igb_read(_adapter, E1000_TDH((_txr)->reg_idx)) #else /* up to 3.2, approximately */ #define igb_tx_buffer igb_buffer #define tx_buffer_info buffer_info #define igb_rx_buffer igb_buffer #define rx_buffer_info buffer_info -#define READ_TDH(_adapter, _txr) readl((_txr)->head) #endif +#ifdef NETMAP_LINUX_HAVE_IGB_STATE_INDIR +#define NM_IGB_STATE(a_) (&(a_)->state) +#else +#define NM_IGB_STATE(a_) ((a_)->state) +#endif /* NETMAP_LINUX_HAVE_IGB_STATE_INDIR */ /* * Register/unregister. We are already under netmap lock. @@ -89,7 +118,7 @@ igb_netmap_reg(struct netmap_adapter *na, int onoff) struct SOFTC_T *adapter = netdev_priv(ifp); /* protect against other reinit */ - while (test_and_set_bit(__IGB_RESETTING, &adapter->state)) + while (test_and_set_bit(__IGB_RESETTING, NM_IGB_STATE(adapter))) usleep_range(1000, 2000); if (netif_running(adapter->netdev)) @@ -106,7 +135,7 @@ igb_netmap_reg(struct netmap_adapter *na, int onoff) else igb_reset(adapter); // XXX is it needed ? - clear_bit(__IGB_RESETTING, &adapter->state); + clear_bit(__IGB_RESETTING, NM_IGB_STATE(adapter)); return (0); } @@ -127,14 +156,12 @@ igb_netmap_txsync(struct netmap_kring *kring, int flags) u_int const lim = kring->nkr_num_slots - 1; u_int const head = kring->rhead; /* generate an interrupt approximately every half ring */ - u_int report_frequency = kring->nkr_num_slots >> 1; + u_int report_frequency = kring->nkr_num_slots >> 1, report; /* device-specific */ struct SOFTC_T *adapter = netdev_priv(ifp); struct igb_ring* txr = adapter->tx_ring[ring_nr]; - rmb(); // XXX not in ixgbe ? - /* * First part: process new packets to send. */ @@ -144,39 +171,91 @@ igb_netmap_txsync(struct netmap_kring *kring, int flags) nm_i = kring->nr_hwcur; if (nm_i != head) { /* we have new packets to send */ - uint32_t olinfo_status=0; - nic_i = netmap_idx_k2n(kring, nm_i); for (n = 0; nm_i != head; n++) { struct netmap_slot *slot = &ring->slot[nm_i]; u_int len = slot->len; uint64_t paddr; void *addr = PNMB(na, slot, &paddr); + uint64_t offset = nm_get_offset(kring, slot); /* device-specific */ union e1000_adv_tx_desc *curr = E1000_TX_DESC_ADV(*txr, nic_i); - int flags = (slot->flags & NS_REPORT || - nic_i == 0 || nic_i == report_frequency) ? - E1000_TXD_CMD_RS : 0; - - NM_CHECK_ADDR_LEN(na, addr, len); - - if (slot->flags & NS_BUF_CHANGED) { - /* buffer has changed, reload map */ - // netmap_reload_map(pdev, DMA_TO_DEVICE, old_paddr, addr); + int hw_flags = E1000_ADVTXD_DTYP_DATA | E1000_ADVTXD_DCMD_DEXT | + E1000_ADVTXD_DCMD_IFCS; + u_int totlen = len; + + PNMB(na, slot, &paddr); + NM_CHECK_ADDR_LEN_OFF(na, len, offset); + + report = slot->flags & NS_REPORT || + nic_i == 0 || + nic_i == report_frequency; + if (slot->flags & NS_MOREFRAG) { + /* There is some duplicated code here, but + * mixing everything up in the outer loop makes + * things less transparent, and it also adds + * unnecessary instructions in the fast path + */ + union e1000_adv_tx_desc *first = curr; + + first->read.buffer_addr = htole64(paddr + offset); + first->read.cmd_type_len = htole32(len | hw_flags); + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, len, NR_TX); + /* avoid setting the FCS flag in the + * descriptors after the first, for safety + */ + hw_flags &= ~E1000_ADVTXD_DCMD_IFCS; + for (;;) { + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + /* remember that we have to ask for a + * report each time we move past half a + * ring + */ + report |= nic_i == 0 || + nic_i == report_frequency; + if (nm_i == head) { + // XXX should we accept incomplete packets? + return EINVAL; + } + slot = &ring->slot[nm_i]; + len = slot->len; + addr = PNMB(na, slot, &paddr); + PNMB(na, slot, &paddr); + offset = nm_get_offset(kring, slot); + NM_CHECK_ADDR_LEN_OFF(na, len, offset); + curr = E1000_TX_DESC_ADV(*txr, nic_i); + totlen += len; + if (!(slot->flags & NS_MOREFRAG)) + break; + curr->read.buffer_addr = htole64(paddr + offset); + curr->read.olinfo_status = 0; + curr->read.cmd_type_len = htole32(len | hw_flags); + + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, len, NR_TX); + } + first->read.olinfo_status = + htole32(totlen << E1000_ADVTXD_PAYLEN_SHIFT); + totlen = 0; } - slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED); + /* curr now always points to the last descriptor of a packet + * (which is also the first for single-slot packets) + * + * EOP and RS must be set only in this descriptor. + */ + hw_flags |= E1000_TXD_CMD_EOP | (report ? E1000_TXD_CMD_RS : 0); + slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED | NS_MOREFRAG); /* Fill the slot in the NIC ring. */ - curr->read.buffer_addr = htole64(paddr); + curr->read.buffer_addr = htole64(paddr + offset); // XXX check olinfo and cmd_type_len - curr->read.olinfo_status = - htole32(olinfo_status | - (len<< E1000_ADVTXD_PAYLEN_SHIFT)); - curr->read.cmd_type_len = htole32(len | flags | - E1000_ADVTXD_DTYP_DATA | E1000_ADVTXD_DCMD_DEXT | - E1000_ADVTXD_DCMD_IFCS | E1000_TXD_CMD_EOP); + curr->read.olinfo_status = htole32(totlen<< E1000_ADVTXD_PAYLEN_SHIFT); + curr->read.cmd_type_len = htole32(len | hw_flags); + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, &paddr, len, NR_TX); nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); } @@ -184,24 +263,35 @@ igb_netmap_txsync(struct netmap_kring *kring, int flags) wmb(); /* synchronize writes to the NIC ring */ - txr->next_to_use = nic_i; /* XXX what for ? */ /* (re)start the tx unit up to slot nic_i (excluded) */ writel(nic_i, txr->tail); - mmiowb(); // XXX where do we need this ? + wmb(); } /* * Second part: reclaim buffers for completed transmissions. */ if (flags & NAF_FORCE_RECLAIM || nm_kr_txempty(kring)) { + u_int tosync; + /* record completed transmissions using TDH */ nic_i = READ_TDH(adapter, txr); - if (nic_i >= kring->nkr_num_slots) { /* XXX can it happen ? */ - D("TDH wrap %d", nic_i); + if (unlikely(nic_i >= kring->nkr_num_slots)) { + nm_prerr("TDH wrap at idx %d", nic_i); nic_i -= kring->nkr_num_slots; } - txr->next_to_use = nic_i; - kring->nr_hwtail = nm_prev(netmap_idx_n2k(kring, nic_i), lim); + nm_i = netmap_idx_n2k(kring, nic_i); + tosync = nm_next(kring->nr_hwtail, lim); + /* sync all buffers that we are returning to userspace */ + for ( ; tosync != nm_i; tosync = nm_next(tosync, lim)) { + struct netmap_slot *slot = &ring->slot[tosync]; + uint64_t paddr; + (void)PNMB_O(kring, slot, &paddr); + + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, + &paddr, slot->len, NR_TX); + } + kring->nr_hwtail = nm_prev(nm_i, lim); } out: @@ -225,7 +315,6 @@ igb_netmap_rxsync(struct netmap_kring *kring, int flags) u_int const lim = kring->nkr_num_slots - 1; u_int const head = kring->rhead; int force_update = (flags & NAF_FORCE_READ) || kring->nr_kflags & NKR_PENDINTR; - /* device-specific */ struct SOFTC_T *adapter = netdev_priv(ifp); struct igb_ring *rxr = adapter->rx_ring[ring_nr]; @@ -242,7 +331,7 @@ igb_netmap_rxsync(struct netmap_kring *kring, int flags) * First part: import newly received packets. */ if (netmap_no_pendintr || force_update) { - uint16_t slot_flags = kring->nkr_slot_flags; + u_int new_hwtail = (u_int)-1; nic_i = rxr->next_to_clean; nm_i = netmap_idx_n2k(kring, nic_i); @@ -251,17 +340,31 @@ igb_netmap_rxsync(struct netmap_kring *kring, int flags) union e1000_adv_rx_desc *curr = E1000_RX_DESC_ADV(*rxr, nic_i); uint32_t staterr = le32toh(curr->wb.upper.status_error); + struct netmap_slot *slot = &ring->slot[nm_i]; + uint64_t paddr; + int complete; if ((staterr & E1000_RXD_STAT_DD) == 0) break; - ring->slot[nm_i].len = le16toh(curr->wb.upper.length); - ring->slot[nm_i].flags = slot_flags; + dma_rmb(); /* read descriptor after status DD */ + PNMB_O(kring, slot, &paddr); + slot->len = le16toh(curr->wb.upper.length); + complete = (staterr & E1000_RXD_STAT_EOP); + slot->flags = complete ? 0 : NS_MOREFRAG; + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, &paddr, slot->len, NR_RX); nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); + + if (complete) + new_hwtail = nm_i; } if (n) { /* update the state variables */ rxr->next_to_clean = nic_i; - kring->nr_hwtail = nm_i; +#ifdef NETMAP_LINUX_HAVE_IGB_NTA + rxr->next_to_alloc = nic_i; +#endif /* NETMAP_LINUX_HAVE_IGB_NTA */ + if (new_hwtail != (u_int)-1) + kring->nr_hwtail = nm_i; } kring->nr_kflags &= ~NKR_PENDINTR; } @@ -276,23 +379,24 @@ igb_netmap_rxsync(struct netmap_kring *kring, int flags) struct netmap_slot *slot = &ring->slot[nm_i]; uint64_t paddr; void *addr = PNMB(na, slot, &paddr); + uint64_t offset = nm_get_offset(kring, slot); union e1000_adv_rx_desc *curr = E1000_RX_DESC_ADV(*rxr, nic_i); if (addr == NETMAP_BUF_BASE(na)) /* bad buf */ goto ring_reset; if (slot->flags & NS_BUF_CHANGED) { - // netmap_reload_map(pdev, DMA_FROM_DEVICE, old_paddr, addr); slot->flags &= ~NS_BUF_CHANGED; } - curr->read.pkt_addr = htole64(paddr); + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, NETMAP_BUF_SIZE(na), NR_RX); + curr->read.pkt_addr = htole64(paddr + offset); curr->read.hdr_addr = 0; nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); } kring->nr_hwcur = head; wmb(); - rxr->next_to_use = nic_i; // XXX not really used /* * IMPORTANT: we must leave one free slot in the ring, * so move nic_i back by one unit @@ -315,25 +419,53 @@ igb_netmap_configure_tx_ring(struct SOFTC_T *adapter, int ring_nr) struct ifnet *ifp = adapter->netdev; struct netmap_adapter* na = NA(ifp); struct netmap_slot* slot; - struct igb_ring *txr = adapter->tx_ring[ring_nr]; - int i, si; - void *addr; - uint64_t paddr; - slot = netmap_reset(na, NR_TX, ring_nr, 0); + slot = netmap_reset(na, NR_TX, ring_nr, 0); if (!slot) return 0; // not in netmap native mode - for (i = 0; i < na->num_tx_desc; i++) { - union e1000_adv_tx_desc *tx_desc; - si = netmap_idx_n2k(&na->tx_rings[ring_nr], i); - addr = PNMB(na, slot + si, &paddr); - tx_desc = E1000_TX_DESC_ADV(*txr, i); - tx_desc->read.buffer_addr = htole64(paddr); - /* actually we don't care to init the rings here */ - } + + /* no need to fill the tx rings, since txsync will + * always overwrite the tx slots + */ + return 1; // success } +static int +igb_netmap_bufcfg(struct netmap_kring *kring, uint64_t target) +{ + if (kring->tx == NR_TX) { + kring->hwbuf_len = target; + return 0; + } + + target >>= 10; + if (target >= 1 && target <= 16) { + target <<= 10; + } else { + return EINVAL; + } + kring->hwbuf_len = target; + kring->buf_align = 0; /* no alignment */ + nm_prinf("%s: hwbuf_len %llu", kring->name, kring->hwbuf_len); + return 0; +} + +static void +igb_netmap_configure_srrctl(struct igb_ring *rxr) +{ + struct ifnet *ifp = rxr->netdev; + struct netmap_adapter* na = NA(ifp); + struct igb_adapter *adapter = netdev_priv(ifp); + struct netmap_kring *kring = na->rx_rings[rxr->reg_idx]; + u32 srrctl; + + srrctl = kring->hwbuf_len >> E1000_SRRCTL_BSIZEPKT_SHIFT; + srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF; + srrctl |= E1000_SRRCTL_DROP_EN; + NM_WRITE_SRRCTL(adapter, rxr, srrctl); +} + static int igb_netmap_configure_rx_ring(struct igb_ring *rxr) @@ -342,7 +474,8 @@ igb_netmap_configure_rx_ring(struct igb_ring *rxr) struct netmap_adapter* na = NA(ifp); int reg_idx = rxr->reg_idx; struct netmap_slot* slot; - u_int i; + struct netmap_kring *kring; + u_int i, n; /* * XXX watch out, the main driver must not use @@ -354,38 +487,46 @@ igb_netmap_configure_rx_ring(struct igb_ring *rxr) * srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF; * srrctl |= E1000_SRRCTL_DROP_EN; */ - slot = netmap_reset(na, NR_RX, reg_idx, 0); + slot = netmap_reset(na, NR_RX, reg_idx, 0); if (!slot) return 0; // not in native netmap mode - for (i = 0; i < rxr->count; i++) { + igb_netmap_configure_srrctl(rxr); + + kring = na->rx_rings[reg_idx]; + /* preserve buffers already made available to clients */ + n = rxr->count - 1 - nm_kr_rxspace(na->rx_rings[reg_idx]); + for (i = 0; i <= n; i++) { union e1000_adv_rx_desc *rx_desc; uint64_t paddr; - int si = netmap_idx_n2k(&na->rx_rings[reg_idx], i); - -#if 0 - // XXX the skb check can go away - struct igb_rx_buffer *bi = &rxr->rx_buffer_info[i]; - if (bi->skb) - D("rx buf %d was set", i); - bi->skb = NULL; // XXX leak if set -#endif /* useless */ + int si = netmap_idx_n2k(kring, i); - PNMB(na, slot + si, &paddr); + PNMB_O(kring, slot + si, &paddr); rx_desc = E1000_RX_DESC_ADV(*rxr, i); rx_desc->read.hdr_addr = 0; rx_desc->read.pkt_addr = htole64(paddr); } - rxr->next_to_use = 0; - /* preserve buffers already made available to clients */ - i = rxr->count - 1 - nm_kr_rxspace(&na->rx_rings[reg_idx]); wmb(); /* Force memory writes to complete */ - ND("%s rxr%d.tail %d", na->name, reg_idx, i); - writel(i, rxr->tail); + nm_prdis("%s rxr%d.tail %d", na->name, reg_idx, i); + writel(n, rxr->tail); return 1; // success } +static int +igb_netmap_config(struct netmap_adapter *na, struct nm_config_info *info) +{ + int ret = netmap_rings_config_get(na, info); + + if (ret) { + return ret; + } + + info->rx_buf_maxsize = NETMAP_BUF_SIZE(na); + + return 0; +} + static void igb_netmap_attach(struct SOFTC_T *adapter) @@ -396,13 +537,17 @@ igb_netmap_attach(struct SOFTC_T *adapter) na.ifp = adapter->netdev; na.pdev = &adapter->pdev->dev; + na.na_flags = NAF_MOREFRAG | NAF_OFFSETS; na.num_tx_desc = adapter->tx_ring_count; na.num_rx_desc = adapter->rx_ring_count; + na.num_tx_rings = adapter->num_tx_queues; + na.num_rx_rings = adapter->num_rx_queues; + na.rx_buf_maxsize = 1500; /* will be overwritten by config */ na.nm_register = igb_netmap_reg; na.nm_txsync = igb_netmap_txsync; na.nm_rxsync = igb_netmap_rxsync; - na.num_tx_rings = adapter->num_tx_queues; - na.num_rx_rings = adapter->num_rx_queues; + na.nm_config = igb_netmap_config; + na.nm_bufcfg = igb_netmap_bufcfg; netmap_attach(&na); } diff --git a/LINUX/if_igc_netmap.h b/LINUX/if_igc_netmap.h new file mode 100644 index 000000000..e2c293721 --- /dev/null +++ b/LINUX/if_igc_netmap.h @@ -0,0 +1,455 @@ +/* + * netmap support for: igc (linux version) + * For details on netmap support please see ixgbe_netmap.h + */ + +#ifndef _IF_IGC_NETMAP_H_ +#define _IF_IGC_NETMAP_H_ + +#include +#include +#include + +#define SOFTC_T igc_adapter +#ifndef IGC_SRRCTL_BSIZEPKT_SHIFT +#define IGC_SRRCTL_BSIZEPKT_SHIFT 10 +#endif /* IGC_SRRCTL_BSIZEPKT_SHIFT */ + +#define igc_driver_name netmap_igc_driver_name +char netmap_igc_driver_name[] = "igc" NETMAP_LINUX_DRIVER_SUFFIX; + +/* + * Register/unregister. We are already under netmap lock. + * Only called on the first register or the last unregister. + */ +static int +igc_netmap_reg(struct netmap_adapter *na, int onoff) +{ + struct ifnet *ifp = na->ifp; + struct SOFTC_T *adapter = netdev_priv(ifp); + + /* protect against other reinit */ + while (test_and_set_bit(__IGC_RESETTING, &adapter->state)) + usleep_range(1000, 2000); + + if (netif_running(adapter->netdev)) + igc_down(adapter); + + /* enable or disable flags and callbacks in na and ifp */ + if (onoff) { + nm_set_native_flags(na); + } else { + nm_clear_native_flags(na); + } + + if (netif_running(adapter->netdev)) + igc_up(adapter); + else + igc_reset(adapter); // XXX is it needed ? + + clear_bit(__IGC_RESETTING, &adapter->state); + return (0); +} + +static inline void NM_WRITE_SRRCTL(struct igc_adapter *adapter, + struct igc_ring *rxr, u32 srrctl) +{ + struct igc_hw *hw = &adapter->hw; + wr32(IGC_SRRCTL(rxr->reg_idx), srrctl); +} + +static void +igc_netmap_configure_srrctl(struct igc_ring *rxr) +{ + struct ifnet *ifp = rxr->netdev; + struct netmap_adapter* na = NA(ifp); + struct igc_adapter *adapter = netdev_priv(ifp); + u32 srrctl; + + /* set descriptor configuration not using split header */ + srrctl = ALIGN(NETMAP_BUF_SIZE(na), 1024) >> IGC_SRRCTL_BSIZEPKT_SHIFT; + srrctl |= IGC_SRRCTL_DESCTYPE_ADV_ONEBUF; + // XXX: DROP_ENABLE neither defined or enabled in the main driver + NM_WRITE_SRRCTL(adapter, rxr, srrctl); +} + +static int +igc_netmap_configure_rx_ring(struct igc_ring *rxr) +{ + struct ifnet *ifp = rxr->netdev; + struct netmap_adapter* na = NA(ifp); + int reg_idx = rxr->reg_idx; + struct netmap_slot* slot; + struct netmap_kring *kring; + u_int i, n; + + slot = netmap_reset(na, NR_RX, reg_idx, 0); + if (!slot) + return 0; // not in native netmap mode + + igc_netmap_configure_srrctl(rxr); + + kring = na->rx_rings[reg_idx]; + + /* preserve buffers already made available to clients */ + n = rxr->count - 1 - nm_kr_rxspace(na->rx_rings[reg_idx]); + for (i = 0; i < rxr->count; i++) { + union igc_adv_rx_desc *rx_desc; + uint64_t paddr; + int si = netmap_idx_n2k(kring, i); + PNMB(na, slot + si, &paddr); + rx_desc = IGC_RX_DESC(rxr, i); + rx_desc->read.hdr_addr = 0; + rx_desc->read.pkt_addr = htole64(paddr); + } + + wmb(); /* Force memory writes to complete */ + nm_prdis("%s rxr%d.tail %d", na->name, reg_idx, i); + writel(n, rxr->tail); + + return 1; // success +} + +static int +igc_netmap_configure_tx_ring(struct SOFTC_T *adapter, int ring_nr) +{ + struct ifnet *ifp = adapter->netdev; + struct netmap_adapter* na = NA(ifp); + struct netmap_slot* slot; + struct igc_ring *txr = adapter->tx_ring[ring_nr]; + int i, si; + void *addr; + uint64_t paddr; + + slot = netmap_reset(na, NR_TX, ring_nr, 0); + if (!slot) + return 0; // not in netmap native mode + + for (i = 0; i < na->num_tx_desc; i++) { + union igc_adv_tx_desc *tx_desc; + si = netmap_idx_n2k(na->tx_rings[ring_nr], i); + addr = PNMB(na, slot + si, &paddr); + tx_desc = IGC_TX_DESC(txr, i); + tx_desc->read.buffer_addr = htole64(paddr); + /* actually we don't care to init the rings here */ + } + + return 1; // success +} + +/* + * Reconcile kernel and user view of the transmit ring. + */ +static int +igc_netmap_txsync(struct netmap_kring *kring, int flags) +{ + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *ring = kring->ring; + u_int ring_nr = kring->ring_id; + u_int nm_i; /* index into the netmap ring */ + u_int nic_i; /* index into the NIC ring */ + u_int n; + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + /* generate an interrupt approximately every half ring */ + u_int report_frequency = kring->nkr_num_slots >> 1, report; + + /* device-specific */ + struct SOFTC_T *adapter = netdev_priv(ifp); + struct igc_ring* txr = adapter->tx_ring[ring_nr]; + + if (!netif_carrier_ok(ifp) || !netif_device_present(ifp)) { + goto out; + } + + /* + * First part: process new packets to send. + */ + nm_i = kring->nr_hwcur; + if (nm_i != head) { /* we have new packets to send */ + unsigned int total_packets = 0, total_bytes = 0; + nic_i = netmap_idx_k2n(kring, nm_i); + for (n = 0; nm_i != head; n++) { + struct netmap_slot *slot = &ring->slot[nm_i]; + u_int len = slot->len; + uint64_t paddr; + __le32 cmd_type = 0; + uint32_t olinfo_status=0; + void *addr = PNMB(na, slot, &paddr); + + /* device-specific */ + union igc_adv_tx_desc *curr = + IGC_TX_DESC(txr, nic_i); + int hw_flags = IGC_ADVTXD_DTYP_DATA | IGC_ADVTXD_DCMD_DEXT | + IGC_ADVTXD_DCMD_IFCS; + u_int totlen = len; + + NM_CHECK_ADDR_LEN(na, addr, len); + + report = slot->flags & NS_REPORT || + nic_i == 0 || + nic_i == report_frequency; + total_packets++; + total_bytes += len; + + if (slot->flags & NS_MOREFRAG) { + /* There is some duplicated code here, but + * mixing everything up in the outer loop makes + * things less transparent, and it also adds + * unnecessary instructions in the fast path + */ + union igc_adv_tx_desc *first = curr; + first->read.buffer_addr = htole64(paddr); + first->read.cmd_type_len = htole32(len | hw_flags); + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, len, NR_TX); + /* avoid setting the FCS flag in the + * descriptors after the first, for safety + */ + hw_flags &= ~IGC_ADVTXD_DCMD_IFCS; + for (;;) { + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + /* remember that we have to ask for a + * report each time we move past half a + * ring + */ + report |= nic_i == 0 || + nic_i == report_frequency; + if (nm_i == head) { + // XXX should we accept incomplete packets? + return EINVAL; + } + slot = &ring->slot[nm_i]; + len = slot->len; + addr = PNMB(na, slot, &paddr); + NM_CHECK_ADDR_LEN(na, addr, len); + curr = IGC_TX_DESC(txr, nic_i); + totlen += len; + total_packets++; + total_bytes += len; + if (!(slot->flags & NS_MOREFRAG)) + break; + curr->read.buffer_addr = htole64(paddr); + curr->read.olinfo_status = 0; + curr->read.cmd_type_len = htole32(len | hw_flags); + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, len, NR_TX); + } + first->read.olinfo_status = + htole32(totlen << IGC_ADVTXD_PAYLEN_SHIFT); + totlen = 0; + } + /* curr now always points to the last descriptor of a packet + * (which is also the first for single-slot packets) + * + * EOP and RS must be set only in this descriptor. + */ + hw_flags |= IGC_ADVTXD_DCMD_EOP | (report ? IGC_ADVTXD_DCMD_RS : 0); + slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED | NS_MOREFRAG); + + /* Fill the slot in the NIC ring. */ + curr->read.buffer_addr = htole64(paddr); + curr->read.olinfo_status = htole32(olinfo_status | (totlen << IGC_ADVTXD_PAYLEN_SHIFT)); + curr->read.cmd_type_len = cmd_type | htole32(len | hw_flags); + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, &paddr, len, NR_TX); + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + } + kring->nr_hwcur = head; + + wmb(); /* synchronize writes to the NIC ring */ + + /* (re)start the tx unit up to slot nic_i (excluded) */ + writel(nic_i, txr->tail); + wmb(); + txr->tx_stats.bytes += total_bytes; + txr->tx_stats.packets += total_packets; + } + + /* + * Second part: reclaim buffers for completed transmissions. + */ + if (flags & NAF_FORCE_RECLAIM || nm_kr_txempty(kring)) { + u_int tosync; + struct igc_hw *hw = &adapter->hw; + + /* record completed transmissions using TDH */ + nic_i = rd32(IGC_TDH(txr->reg_idx)); + if (nic_i >= kring->nkr_num_slots) { /* XXX can it happen ? */ + nm_prdis("TDH wrap %d", nic_i); + nic_i -= kring->nkr_num_slots; + } + nm_i = netmap_idx_n2k(kring, nic_i); + tosync = nm_next(kring->nr_hwtail, lim); + /* sync all buffers that we are returning to userspace */ + for ( ; tosync != nm_i; tosync = nm_next(tosync, lim)) { + struct netmap_slot *slot = &ring->slot[tosync]; + uint64_t paddr; + (void)PNMB(na, slot, &paddr); + + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, + &paddr, slot->len, NR_TX); + } + kring->nr_hwtail = nm_prev(nm_i, lim); + } +out: + return 0; +} + +/* + * Reconcile kernel and user view of the receive ring. + */ +static int +igc_netmap_rxsync(struct netmap_kring *kring, int flags) +{ + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *ring = kring->ring; + u_int ring_nr = kring->ring_id; + u_int nm_i; /* index into the netmap ring */ + u_int nic_i; /* index into the NIC ring */ + u_int n; + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + int force_update = (flags & NAF_FORCE_READ) || kring->nr_kflags & NKR_PENDINTR; + + /* device-specific */ + struct SOFTC_T *adapter = netdev_priv(ifp); + struct igc_ring *rxr = adapter->rx_ring[ring_nr]; + + if (!netif_carrier_ok(ifp) || !netif_device_present(ifp)) + return 0; + + if (head > lim) { + nm_prlim(10, " rxsync lim %d head %d kring %p", lim, head, kring); + return netmap_ring_reinit(kring); + } + + rmb(); + /* + * First part: import newly received packets. + */ + if (netmap_no_pendintr || force_update) { + unsigned int total_packets = 0, total_bytes = 0; + u_int new_hwtail = (u_int)-1; + nic_i = rxr->next_to_clean; + nm_i = netmap_idx_n2k(kring, nic_i); + + for (n = 0; ; n++) { + union igc_adv_rx_desc *curr = + IGC_RX_DESC(rxr, nic_i); + uint32_t size = le16_to_cpu(curr->wb.upper.length); + struct netmap_slot *slot = &ring->slot[nm_i]; + uint64_t paddr; + int complete; + + if (!size) + break; + + dma_rmb(); + + PNMB(na, slot, &paddr); + slot->len = size; + complete = igc_test_staterr(curr, IGC_RXD_STAT_EOP); + slot->flags = complete ? 0 : NS_MOREFRAG; + total_packets++; + total_bytes += slot->len; + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, &paddr, slot->len, NR_RX); + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + + if (complete) + new_hwtail = nm_i; + } + + if (n) { /* update the state variables */ + rxr->next_to_clean = nic_i; + rxr->next_to_alloc = nic_i; + if (new_hwtail != (u_int)-1) + kring->nr_hwtail = nm_i; + } + kring->nr_kflags &= ~NKR_PENDINTR; + rxr->rx_stats.bytes += total_bytes; + rxr->rx_stats.packets += total_packets; + } + + /* + * Second part: skip past packets that userspace has released. + */ + nm_i = kring->nr_hwcur; + if (nm_i != head) { + nic_i = netmap_idx_k2n(kring, nm_i); + for (n = 0; nm_i != head; n++) { + struct netmap_slot *slot = &ring->slot[nm_i]; + uint64_t paddr; + void *addr = PNMB(na, slot, &paddr); + union igc_adv_rx_desc *curr = IGC_RX_DESC(rxr, nic_i); + + if (addr == NETMAP_BUF_BASE(na)) /* bad buf */ + goto ring_reset; + + if (slot->flags & NS_BUF_CHANGED) { + slot->flags &= ~NS_BUF_CHANGED; + } + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, NETMAP_BUF_SIZE(na), NR_RX); + curr->read.pkt_addr = htole64(paddr); + curr->read.hdr_addr = 0; + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + } + kring->nr_hwcur = head; + wmb(); + /* + * IMPORTANT: we must leave one free slot in the ring, + * so move nic_i back by one unit + */ + nic_i = nm_prev(nic_i, lim); + writel(nic_i, rxr->tail); + } + + return 0; + +ring_reset: + return netmap_ring_reinit(kring); +} + +static int +igc_netmap_config(struct netmap_adapter *na, struct nm_config_info *info) +{ + int ret = netmap_rings_config_get(na, info); + + if (ret) { + return ret; + } + + info->rx_buf_maxsize = NETMAP_BUF_SIZE(na); + + return 0; +} + +static void +igc_netmap_attach(struct SOFTC_T *adapter) +{ + struct netmap_adapter na; + + bzero(&na, sizeof(na)); + + na.ifp = adapter->netdev; + na.pdev = &adapter->pdev->dev; + na.na_flags = NAF_MOREFRAG; + na.num_tx_desc = adapter->tx_ring_count; + na.num_rx_desc = adapter->rx_ring_count; + na.num_tx_rings = adapter->num_tx_queues; + na.num_rx_rings = adapter->num_rx_queues; + na.rx_buf_maxsize = 1500; /* will be overwritten by config */ + na.nm_register = igc_netmap_reg; + na.nm_txsync = igc_netmap_txsync; + na.nm_rxsync = igc_netmap_rxsync; + na.nm_config = igc_netmap_config; + netmap_attach(&na); +} + +#endif // _IF_IGC_NETMAP_H_ diff --git a/LINUX/if_re_netmap_linux.h b/LINUX/if_re_netmap_linux.h index fdbe46e99..dd8175de6 100644 --- a/LINUX/if_re_netmap_linux.h +++ b/LINUX/if_re_netmap_linux.h @@ -198,7 +198,6 @@ re_netmap_rxsync(struct netmap_kring *kring, int flags) * is to stop right before nm_hwcur. */ if (netmap_no_pendintr || force_update) { - uint16_t slot_flags = kring->nkr_slot_flags; uint32_t stop_i = nm_prev(kring->nr_hwcur, lim); nic_i = sc->cur_rx; /* next pkt to check */ @@ -215,7 +214,7 @@ re_netmap_rxsync(struct netmap_kring *kring, int flags) /* XXX subtract crc */ total_len = (total_len < 4) ? 0 : total_len - 4; ring->slot[nm_i].len = total_len; - ring->slot[nm_i].flags = slot_flags; + ring->slot[nm_i].flags = 0; // ifp->stats.rx_packets++; nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); @@ -279,13 +278,13 @@ re_netmap_tx_init(struct SOFTC_T *sc) int i, l; uint64_t paddr; - slot = netmap_reset(na, NR_TX, 0, 0); + slot = netmap_reset(na, NR_TX, 0, 0); if (!slot) return 0; // not in native netmap mode /* l points in the netmap ring, i points in the NIC ring */ for (i = 0; i < na->num_tx_desc; i++) { - l = netmap_idx_n2k(&na->tx_rings[0], i); + l = netmap_idx_n2k(na->tx_rings[0], i); PNMB(na, slot + l, &paddr); desc[i].addr = htole64(paddr); } @@ -303,7 +302,7 @@ re_netmap_rx_init(struct SOFTC_T *sc) int i, lim, l; uint64_t paddr; - slot = netmap_reset(na, NR_RX, 0, 0); + slot = netmap_reset(na, NR_RX, 0, 0); if (!slot) return 0; // not in native netmap mode /* @@ -312,8 +311,8 @@ re_netmap_rx_init(struct SOFTC_T *sc) * XXX do we need -1 instead ? */ lim = na->num_rx_desc /* - 1 */ - nm_kr_rxspace(&na->rx_rings[0]); - for (i = 0; i < na->num_rx_desc; i++) { - l = netmap_idx_n2k(&na->rx_rings[0], i); + for (i = 0; i < lim; i++) { + l = netmap_idx_n2k(na->rx_rings[0], i); PNMB(na, slot + l, &paddr); cmdstat = NETMAP_BUF_SIZE(na); if (i == na->num_rx_desc - 1) diff --git a/LINUX/if_stmmac_netmap_linux.h b/LINUX/if_stmmac_netmap_linux.h new file mode 100644 index 000000000..e7c201462 --- /dev/null +++ b/LINUX/if_stmmac_netmap_linux.h @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2021 Savoir-faire Linux, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * $Id: if_stmmac_netmap_linux.h 10679 2021-01-18 13:42:18E SFL $ + * + * netmap support for: stmmac (re, linux version) + * For details on netmap support please see ixgbe_netmap.h + * 1 tx ring, 1 rx ring, 1 lock, crcstrip ? reinit tx addr, + */ + +#include +#include +#include + +static int stmmac_open(struct net_device *dev); +static int stmmac_release(struct net_device *dev); + +#ifdef MODULENAME +#undef MODULENAME +#define MODULENAME "stmmac" NETMAP_LINUX_DRIVER_SUFFIX +#endif + +/* + * Register/unregister, mostly the reinit task + */ +static int stmmac_netmap_reg(struct netmap_adapter *na, int onoff) +{ + struct ifnet *ifp = na->ifp; + int error = 0; + + stmmac_release(ifp); + + /* enable or disable flags and callbacks in na and ifp */ + if (onoff) { + nm_set_native_flags(na); + + if (stmmac_open(ifp) < 0) { + error = ENOMEM; + goto fail; + } + } else { + fail: + nm_clear_native_flags(na); + error = stmmac_open(ifp) ? EINVAL : 0; + } + + return (error); +} + +/* + * Reconcile kernel and user view of the transmit ring. + */ +static int stmmac_netmap_txsync(struct netmap_kring *kring, int flags) +{ + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *ring = kring->ring; + u_int nm_i; /* index into the netmap ring */ + u_int nic_i; /* index into the NIC ring */ + u_int n; + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + + /* device-specific */ + struct stmmac_priv *stmac_priv = netdev_priv(ifp); + + rmb(); + + /* + * First part: process new packets to send. + */ + if (!netif_carrier_ok(ifp)) { + goto out; + } + + nm_i = kring->nr_hwcur; + /* we have new packets to send */ + if (nm_i != head) { + nic_i = netmap_idx_k2n(kring, nm_i); + for (n = 0; nm_i != head; n++) { + struct netmap_slot *slot = &ring->slot[nm_i]; + int len = slot->len; + uint64_t paddr; + void *addr = PNMB(na, slot, &paddr); + uint32_t etdes1 = + (slot->len & ETDES1_BUFFER1_SIZE_MASK); + uint32_t etdes0 = ETDES0_LAST_SEGMENT | ETDES0_OWN | + ETDES0_FIRST_SEGMENT; + + /* device-specific */ + struct dma_desc *pdam_desc = NULL; + if (stmac_priv->extend_desc) + pdam_desc = + (struct dma_desc *)(stmac_priv->dma_etx + + nic_i); + else + pdam_desc = stmac_priv->dma_tx + nic_i; + + NM_CHECK_ADDR_LEN(na, addr, len); + + if (nic_i == lim) /* mark end of ring */ + etdes0 |= ETDES0_END_RING; + + if (slot->flags & NS_BUF_CHANGED) { + /* buffer has changed, reload map */ + // netmap_reload_map(pdev, DMA_TO_DEVICE, old_paddr, addr); + pdam_desc->des2 = paddr; + } + + slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED); + pdam_desc->des0 = etdes0; + pdam_desc->des1 = etdes1; + + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + } + + kring->nr_hwcur = head; + + stmac_priv->cur_tx = nic_i; + wmb(); /* synchronize writes to the NIC ring */ + } + + /* + * Second part: reclaim buffers for completed transmissions. + */ + if (flags & NAF_FORCE_RECLAIM || nm_kr_txempty(kring)) { + for (n = 0, nic_i = stmac_priv->dirty_tx; + nic_i != stmac_priv->cur_tx; n++) { + struct dma_desc *pdam_desc = NULL; + if (stmac_priv->extend_desc) + pdam_desc = + (struct dma_desc *)(stmac_priv->dma_etx + + nic_i); + else + pdam_desc = stmac_priv->dma_tx + nic_i; + + /* check if DMA owned */ + if (pdam_desc->des0 & ETDES0_OWN) + break; + + if (++nic_i == na->num_tx_desc) + nic_i = 0; + } + + if (n > 0) { + stmac_priv->dirty_tx = nic_i; + kring->nr_hwtail = + nm_prev(netmap_idx_n2k(kring, nic_i), lim); + } + } +out: + return 0; +} + +/* + * Reconcile kernel and user view of the receive ring. + * static int stmmac_rx(struct stmmac_priv *priv, int limit) + */ +static int stmmac_netmap_rxsync(struct netmap_kring *kring, int flags) +{ + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct stmmac_priv *stmac_priv = netdev_priv(ifp); + struct netmap_ring *ring = kring->ring; + unsigned int nm_i; /* index into the netmap ring */ + unsigned int entry; /* index into the NIC ring */ + unsigned int n; + unsigned int const lim = kring->nkr_num_slots - 1; + unsigned int const head = kring->rhead; + + int force_update = + (flags & NAF_FORCE_READ) || kring->nr_kflags & NKR_PENDINTR; + + if (!netif_carrier_ok(ifp)) + return 0; + + if (head > lim) + return netmap_ring_reinit(kring); + + rmb(); + + /* + * First part: import newly received packets. + */ + if (netmap_no_pendintr || force_update) { + uint32_t stop_i = nm_prev(kring->nr_hwcur, lim); + int coe = stmac_priv->hw->rx_csum; + uint32_t frame_len = 0x0; + + entry = stmac_priv->cur_rx; /* next pkt to check */ + nm_i = netmap_idx_n2k(kring, entry); + + while (nm_i != stop_i) { + int status; + struct dma_desc *pdam_desc; + + if (stmac_priv->extend_desc) + pdam_desc = + (struct dma_desc *)(stmac_priv->dma_erx + + entry); + else + pdam_desc = stmac_priv->dma_rx + entry; + + /* read the status of the incoming frame */ + status = stmac_priv->hw->desc->rx_status( + &stmac_priv->dev->stats, &stmac_priv->xstats, + pdam_desc); + + /* check if managed by the DMA otherwise go ahead */ + if (unlikely(status & dma_own)) + break; + + if ((stmac_priv->extend_desc) && + (stmac_priv->hw->desc->rx_extended_status)) + stmac_priv->hw->desc->rx_extended_status( + &stmac_priv->dev->stats, + &stmac_priv->xstats, + stmac_priv->dma_erx + entry); + + frame_len = stmac_priv->hw->desc->get_rx_frame_len( + pdam_desc, coe); + + /* ACS is set; GMAC core strips PAD/FCS for IEEE 802.3 + * Type frames (LLC/LLC-SNAP) + */ + if (unlikely(status != llc_snap)) + frame_len -= ETH_FCS_LEN; + + ring->slot[nm_i].len = frame_len; + ring->slot[nm_i].flags = 0; + + nm_i = nm_next(nm_i, lim); + entry = nm_next(entry, lim); + } + + stmac_priv->cur_rx = entry; + + kring->nr_hwtail = nm_i; + kring->nr_kflags &= ~NKR_PENDINTR; + } + + /* + * Second part: skip past packets that userspace has released. + */ + nm_i = kring->nr_hwcur; + if (nm_i != head) { + entry = netmap_idx_k2n(kring, nm_i); + for (n = 0; nm_i != head; n++) { + uint32_t erdes1 = 0x0; + + struct netmap_slot *slot = &ring->slot[nm_i]; + uint64_t paddr; + void *addr = PNMB(na, slot, &paddr); + + struct dma_desc *pdam_desc; + + if (stmac_priv->extend_desc) + pdam_desc = + (struct dma_desc *)(stmac_priv->dma_erx + + entry); + else + pdam_desc = stmac_priv->dma_rx + entry; + + erdes1 = NETMAP_BUF_SIZE(na); + + if (addr == NETMAP_BUF_BASE(na)) /* bad buf */ + goto ring_reset; + + if (entry == lim) /* mark end of ring */ + erdes1 |= ERDES1_END_RING; + + if (slot->flags & NS_BUF_CHANGED) { + /* buffer has changed, reload map */ + // netmap_reload_map(pdev, DMA_TO_DEVICE, old_paddr, addr); + pdam_desc->des2 = paddr; + slot->flags &= ~NS_BUF_CHANGED; + } + + pdam_desc->des1 |= erdes1; + + nm_i = nm_next(nm_i, lim); + entry = nm_next(entry, lim); + } + + kring->nr_hwcur = head; + wmb(); + } + + return 0; + +ring_reset: + return netmap_ring_reinit(kring); +} + +/* + * Make the Tx desc rings point to the netmap buffers. + * static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) + */ +static int stmmac_netmap_tx_init(struct stmmac_priv *stmac_priv) +{ + struct netmap_adapter *na = NA(stmac_priv->dev); + struct netmap_slot *slot = NULL; + int i, l; + uint64_t paddr = 0x0; + + slot = netmap_reset(na, NR_TX, 0, 0); + if (!slot) + return 0; + + /* l points in the netmap ring, i points in the NIC ring */ + for (i = 0; i < na->num_tx_desc; i++) { + uint32_t etdes0 = 0x0; + struct dma_desc *pdam_desc = NULL; + + stmac_priv->tx_skbuff[i] = NULL; + if (stmac_priv->extend_desc) + pdam_desc = &((stmac_priv->dma_etx + i)->basic); + + else + pdam_desc = stmac_priv->dma_tx + i; + + if (IS_ERR(pdam_desc)) + return 0; + + l = netmap_idx_n2k(na->tx_rings[0], i); + PNMB(na, slot + l, &paddr); + + /* ETDES2 */ + pdam_desc->des2 = paddr; + + /* ETDES0 */ + if (i == na->num_tx_desc - 1) + etdes0 |= ETDES0_END_RING; + + pdam_desc->des0 = etdes0; + } + + return 1; +} + +/* + * Make the Rx desc rings point to the netmap buffers. + * static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) + */ +static int stmmac_netmap_rx_init(struct stmmac_priv *stmac_priv) +{ + struct netmap_adapter *na = NA(stmac_priv->dev); + struct netmap_slot *slot = NULL; + int i, lim, l; + uint64_t paddr = 0x0; + + slot = netmap_reset(na, NR_RX, 0, 0); + if (!slot) + return 0; + + lim = na->num_rx_desc - nm_kr_rxspace(na->rx_rings[0]); + for (i = 0; i < na->num_rx_desc; i++) { + void *addr; + uint32_t erdes1 = 0x0; + struct dma_desc *pdam_desc = NULL; + + stmac_priv->rx_skbuff[i] = NULL; + + if (stmac_priv->extend_desc) + pdam_desc = &((stmac_priv->dma_erx + i)->basic); + else + pdam_desc = stmac_priv->dma_rx + i; + + if (IS_ERR(pdam_desc)) + return 0; + + l = netmap_idx_n2k(na->rx_rings[0], i); + addr = PNMB(na, slot + l, &paddr); + + /* NOTE:is not set: ERDES3 and erdes1 |= ((BUF_SIZE_8KiB - 1) << ERDES1_BUFFER2_SIZE_SHIFT) & ERDES1_BUFFER2_SIZE_MASK; */ + + /* ERDES2 */ + pdam_desc->des2 = paddr; + + /* ERDES1 */ + erdes1 |= + ((NETMAP_BUF_SIZE(na) - 1) & ERDES1_BUFFER1_SIZE_MASK); + + /* operate in ring mode only, and set last ERDES accordingly*/ + if (i == na->num_rx_desc - 1) { + erdes1 |= ERDES1_END_RING; + } + + erdes1 |= ERDES1_DISABLE_IC; + + pdam_desc->des1 |= erdes1; + + /* ERDES0 */ + if (i < lim) + pdam_desc->des0 |= RDES0_OWN; + } + + return 1; +} + +static int stmmac_netmap_bufcfg(struct netmap_kring *kring, uint64_t target) +{ + kring->hwbuf_len = BUF_SIZE_8KiB; + kring->buf_align = 0; /* no alignment */ + + return 0; +} + +static int stmmac_netmap_config(struct netmap_adapter *na, + struct nm_config_info *info) +{ + struct stmmac_priv *stmac_priv = netdev_priv(na->ifp); + int ret = netmap_rings_config_get(na, info); + + if (ret) + return ret; + + info->rx_buf_maxsize = stmac_priv->dma_buf_sz; + + return 0; +} + +static void stmmac_netmap_attach(struct stmmac_priv *stmac_priv) +{ + struct netmap_adapter na; + + bzero(&na, sizeof(na)); + + na.ifp = stmac_priv->dev; /* struct net_device *dev; */ + na.pdev = &stmac_priv->device; /* struct device *device; */ + na.num_tx_desc = DMA_TX_SIZE; + na.num_rx_desc = DMA_RX_SIZE; + na.rx_buf_maxsize = BUF_SIZE_8KiB; + na.num_tx_rings = na.num_rx_rings = 1; + na.nm_txsync = stmmac_netmap_txsync; + na.nm_rxsync = stmmac_netmap_rxsync; + na.nm_register = stmmac_netmap_reg; + na.nm_config = stmmac_netmap_config; + na.nm_bufcfg = stmmac_netmap_bufcfg; + netmap_attach(&na); +} + +/* end of file */ diff --git a/LINUX/if_virtio_net_netmap.h b/LINUX/if_virtio_net_netmap.h new file mode 100644 index 000000000..e48195ebd --- /dev/null +++ b/LINUX/if_virtio_net_netmap.h @@ -0,0 +1,945 @@ +/* + * Copyright (C) 2018 Vincenzo Maffione. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +/* Netmap support for the virtio-net driver build from fixed "external" + * sources (currently taken from Linux 4.9, and patched by + * patches/custom--virtio_net.c--4.9). This driver should be preferred + * to the one included with the running Linux version. + */ + +/*************************************************************************/ +/* COMPATIBILITY LAYER */ +/*************************************************************************/ + +#ifndef VIRTIO_F_VERSION_1 +#define VIRTIO_F_VERSION_1 32 +#endif + +#ifndef VIRTIO_F_ANY_LAYOUT +#define VIRTIO_F_ANY_LAYOUT 27 +#endif + +#ifndef NETMAP_LINUX_HAVE_ETHTOOL_VALIDATE +static inline int ethtool_validate_speed(__u32 speed) +{ + return speed <= INT_MAX || speed == SPEED_UNKNOWN; +} + +static inline int ethtool_validate_duplex(__u8 duplex) +{ + switch (duplex) { + case DUPLEX_HALF: + case DUPLEX_FULL: + case DUPLEX_UNKNOWN: + return 1; + } + + return 0; +} +#endif /* NETMAP_LINUX_HAVE_ETHTOOL_VALIDATE */ + +#ifndef NETMAP_LINUX_HAVE_U64_STATS_INIT +#define u64_stats_init(x) +#endif /* !NETMAP_LINUX_HAVE_U64_STATS_INIT */ + +#ifndef NETMAP_LINUX_HAVE_U64_STATS_IRQ +#define u64_stats_fetch_begin_irq u64_stats_fetch_begin_bh +#define u64_stats_fetch_retry_irq u64_stats_fetch_retry_bh +#endif /* !NETMAP_LINUX_HAVE_U64_STATS_IRQ */ + +#ifdef NETMAP_LINUX_HAVE_SKB_COALESCE_RX_FRAG +#define WITH_MERGEABLE_RX_BUFS +#endif /* !NETMAP_LINUX_HAVE_SKB_COALESCE_RX_FRAG */ + +#ifndef NETMAP_LINUX_HAVE_VIRTIO_BYTEORDER +#include + +/* + * __virtio{16,32,64} have the following meaning: + * - __u{16,32,64} for virtio devices in legacy mode, accessed in native endian + * - __le{16,32,64} for standard-compliant virtio devices + */ + +typedef __u16 __bitwise__ __virtio16; +typedef __u32 __bitwise__ __virtio32; +typedef __u64 __bitwise__ __virtio64; + +static inline u16 __virtio16_to_cpu(bool little_endian, __virtio16 val) +{ + if (little_endian) + return le16_to_cpu((__force __le16)val); + else + return be16_to_cpu((__force __be16)val); +} + +static inline __virtio16 __cpu_to_virtio16(bool little_endian, u16 val) +{ + if (little_endian) + return (__force __virtio16)cpu_to_le16(val); + else + return (__force __virtio16)cpu_to_be16(val); +} + +static inline u32 __virtio32_to_cpu(bool little_endian, __virtio32 val) +{ + if (little_endian) + return le32_to_cpu((__force __le32)val); + else + return be32_to_cpu((__force __be32)val); +} + +static inline __virtio32 __cpu_to_virtio32(bool little_endian, u32 val) +{ + if (little_endian) + return (__force __virtio32)cpu_to_le32(val); + else + return (__force __virtio32)cpu_to_be32(val); +} + +static inline u64 __virtio64_to_cpu(bool little_endian, __virtio64 val) +{ + if (little_endian) + return le64_to_cpu((__force __le64)val); + else + return be64_to_cpu((__force __be64)val); +} + +static inline __virtio64 __cpu_to_virtio64(bool little_endian, u64 val) +{ + if (little_endian) + return (__force __virtio64)cpu_to_le64(val); + else + return (__force __virtio64)cpu_to_be64(val); +} +#endif /* NETMAP_LINUX_HAVE_VIRTIO_BYTEORDER */ + +#ifndef NETMAP_LINUX_HAVE_VIRTIO_IS_LITTLE_ENDIAN +static inline bool virtio_legacy_is_little_endian(void) +{ +#ifdef __LITTLE_ENDIAN + return true; +#else + return false; +#endif +} + +static inline bool virtio_is_little_endian(struct virtio_device *vdev) +{ + return virtio_has_feature(vdev, VIRTIO_F_VERSION_1) || + virtio_legacy_is_little_endian(); +} +#endif /* NETMAP_LINUX_HAVE_VIRTIO_IS_LITTLE_ENDIAN */ + +#ifndef NETMAP_LINUX_HAVE_VIRTIO_MEMORY_ACCESSORS +static inline u16 virtio16_to_cpu(struct virtio_device *vdev, __virtio16 val) +{ + return __virtio16_to_cpu(virtio_is_little_endian(vdev), val); +} + +static inline __virtio16 cpu_to_virtio16(struct virtio_device *vdev, u16 val) +{ + return __cpu_to_virtio16(virtio_is_little_endian(vdev), val); +} + +static inline u32 virtio32_to_cpu(struct virtio_device *vdev, __virtio32 val) +{ + return __virtio32_to_cpu(virtio_is_little_endian(vdev), val); +} + +static inline __virtio32 cpu_to_virtio32(struct virtio_device *vdev, u32 val) +{ + return __cpu_to_virtio32(virtio_is_little_endian(vdev), val); +} + +static inline u64 virtio64_to_cpu(struct virtio_device *vdev, __virtio64 val) +{ + return __virtio64_to_cpu(virtio_is_little_endian(vdev), val); +} + +static inline __virtio64 cpu_to_virtio64(struct virtio_device *vdev, u64 val) +{ + return __cpu_to_virtio64(virtio_is_little_endian(vdev), val); +} +#endif /* NETMAP_LINUX_HAVE_VIRTIO_MEMORY_ACCESSORS */ + +#if !defined(NETMAP_LINUX_VIRTIO_NET_HDR_FROM_SKB_5ARGS) && !defined(NETMAP_LINUX_VIRTIO_NET_HDR_FROM_SKB_4ARGS) && !defined(NETMAP_LINUX_VIRTIO_NET_HDR_FROM_SKB_3ARGS) +static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, + const struct virtio_net_hdr *hdr, + bool little_endian) +{ + unsigned short gso_type = 0; + + if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { + switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { + case VIRTIO_NET_HDR_GSO_TCPV4: + gso_type = SKB_GSO_TCPV4; + break; + case VIRTIO_NET_HDR_GSO_TCPV6: + gso_type = SKB_GSO_TCPV6; + break; + case VIRTIO_NET_HDR_GSO_UDP: + gso_type = SKB_GSO_UDP; + break; + default: + return -EINVAL; + } + + if (hdr->gso_type & VIRTIO_NET_HDR_GSO_ECN) + gso_type |= SKB_GSO_TCP_ECN; + + if (hdr->gso_size == 0) + return -EINVAL; + } + + if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { + u16 start = __virtio16_to_cpu(little_endian, hdr->csum_start); + u16 off = __virtio16_to_cpu(little_endian, hdr->csum_offset); + + if (!skb_partial_csum_set(skb, start, off)) + return -EINVAL; + } + + if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { + u16 gso_size = __virtio16_to_cpu(little_endian, hdr->gso_size); + + skb_shinfo(skb)->gso_size = gso_size; + skb_shinfo(skb)->gso_type = gso_type; + + /* Header must be checked, and gso_segs computed. */ + skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; + skb_shinfo(skb)->gso_segs = 0; + } + + return 0; +} + +static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, + struct virtio_net_hdr *hdr, + bool little_endian) +{ + memset(hdr, 0, sizeof(*hdr)); + + if (skb_is_gso(skb)) { + struct skb_shared_info *sinfo = skb_shinfo(skb); + + /* This is a hint as to how much should be linear. */ + hdr->hdr_len = __cpu_to_virtio16(little_endian, + skb_headlen(skb)); + hdr->gso_size = __cpu_to_virtio16(little_endian, + sinfo->gso_size); + if (sinfo->gso_type & SKB_GSO_TCPV4) + hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; + else if (sinfo->gso_type & SKB_GSO_TCPV6) + hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + else if (sinfo->gso_type & SKB_GSO_UDP) + hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP; + else + return -EINVAL; + if (sinfo->gso_type & SKB_GSO_TCP_ECN) + hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN; + } else + hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + if (skb->vlan_tci & VLAN_TAG_PRESENT) + hdr->csum_start = __cpu_to_virtio16(little_endian, + skb_checksum_start_offset(skb) + VLAN_HLEN); + else + hdr->csum_start = __cpu_to_virtio16(little_endian, + skb_checksum_start_offset(skb)); + hdr->csum_offset = __cpu_to_virtio16(little_endian, + skb->csum_offset); + } /* else everything is zero */ + + return 0; +} +#endif + +#ifndef NETMAP_LINUX_VIRTIO_GET_VRSIZE +/* Not yet found a way to find out virtqueue length in these + kernel series. Use the virtio default value. */ +#define virtqueue_get_vring_size(_vq) ({ (void)(_vq); 256; }) +#endif /* !VIRTIO_GET_VRSIZE */ + +#ifndef NETMAP_LINUX_HAVE_VIRTIO_DEVICE_READY +static inline +void virtio_device_ready(struct virtio_device *dev) +{ + unsigned status = dev->config->get_status(dev); + + BUG_ON(status & VIRTIO_CONFIG_S_DRIVER_OK); + dev->config->set_status(dev, status | VIRTIO_CONFIG_S_DRIVER_OK); +} +#endif /* NETMAP_LINUX_HAVE_VIRTIO_DEVICE_READY */ + +#ifndef NETMAP_LINUX_HAVE_VIRTQUEUE_IS_BROKEN +#define virtqueue_is_broken(_x) false +#endif /* NETMAP_LINUX_HAVE_VIRTQUEUE_IS_BROKEN */ + +#ifndef NETMAP_LINUX_VIRTIO_CB_DELAYED +/* The delayed optimization did not exists before version 3.0. */ +#define virtqueue_enable_cb_delayed(_vq) virtqueue_enable_cb(_vq) +#endif /* !VIRTIO_CB_DELAYED */ + +#ifndef NETMAP_LINUX_HAVE_VIRTIO_CONFIG_ACCESSORS +#define virtio_cread(vdev, structname, member, ptr) \ + do { \ + /* Must match the member's type, and be integer */ \ + if (!typecheck(typeof((((structname*)0)->member)), *(ptr))) \ + (*ptr) = 1; \ + \ + switch (sizeof(*ptr)) { \ + case 1: \ + *(ptr) = virtio_cread8(vdev, \ + offsetof(structname, member)); \ + break; \ + case 2: \ + *(ptr) = virtio_cread16(vdev, \ + offsetof(structname, member)); \ + break; \ + case 4: \ + *(ptr) = virtio_cread32(vdev, \ + offsetof(structname, member)); \ + break; \ + case 8: \ + *(ptr) = virtio_cread64(vdev, \ + offsetof(structname, member)); \ + break; \ + default: \ + BUG(); \ + } \ + } while(0) + +/* Config space accessors. */ +#define virtio_cwrite(vdev, structname, member, ptr) \ + do { \ + /* Must match the member's type, and be integer */ \ + if (!typecheck(typeof((((structname*)0)->member)), *(ptr))) \ + BUG_ON((*ptr) == 1); \ + \ + switch (sizeof(*ptr)) { \ + case 1: \ + virtio_cwrite8(vdev, \ + offsetof(structname, member), \ + *(ptr)); \ + break; \ + case 2: \ + virtio_cwrite16(vdev, \ + offsetof(structname, member), \ + *(ptr)); \ + break; \ + case 4: \ + virtio_cwrite32(vdev, \ + offsetof(structname, member), \ + *(ptr)); \ + break; \ + case 8: \ + virtio_cwrite64(vdev, \ + offsetof(structname, member), \ + *(ptr)); \ + break; \ + default: \ + BUG(); \ + } \ + } while(0) + +static inline u8 virtio_cread8(struct virtio_device *vdev, unsigned int offset) +{ + u8 ret; + vdev->config->get(vdev, offset, &ret, sizeof(ret)); + return ret; +} + +static inline void virtio_cread_bytes(struct virtio_device *vdev, + unsigned int offset, + void *buf, size_t len) +{ + vdev->config->get(vdev, offset, buf, len); +} + +static inline void virtio_cwrite8(struct virtio_device *vdev, + unsigned int offset, u8 val) +{ + vdev->config->set(vdev, offset, &val, sizeof(val)); +} + +static inline u16 virtio_cread16(struct virtio_device *vdev, + unsigned int offset) +{ + u16 ret; + vdev->config->get(vdev, offset, &ret, sizeof(ret)); + return ret; +} + +static inline void virtio_cwrite16(struct virtio_device *vdev, + unsigned int offset, u16 val) +{ + vdev->config->set(vdev, offset, &val, sizeof(val)); +} + +static inline u32 virtio_cread32(struct virtio_device *vdev, + unsigned int offset) +{ + u32 ret; + vdev->config->get(vdev, offset, &ret, sizeof(ret)); + return ret; +} + +static inline void virtio_cwrite32(struct virtio_device *vdev, + unsigned int offset, u32 val) +{ + vdev->config->set(vdev, offset, &val, sizeof(val)); +} + +static inline u64 virtio_cread64(struct virtio_device *vdev, + unsigned int offset) +{ + u64 ret; + vdev->config->get(vdev, offset, &ret, sizeof(ret)); + return ret; +} + +static inline void virtio_cwrite64(struct virtio_device *vdev, + unsigned int offset, u64 val) +{ + vdev->config->set(vdev, offset, &val, sizeof(val)); +} + +/* Conditional config space accessors. */ +#define virtio_cread_feature(vdev, fbit, structname, member, ptr) \ + ({ \ + int _r = 0; \ + if (!virtio_has_feature(vdev, fbit)) \ + _r = -ENOENT; \ + else \ + virtio_cread((vdev), structname, member, ptr); \ + _r; \ + }) +#endif /* !NETMAP_LINUX_HAVE_VIRTIO_CONFIG_ACCESSORS */ + +/*************************************************************************/ +/* NETMAP SUPPORT */ +/*************************************************************************/ + +static int virtnet_open(struct net_device *dev); +static int virtnet_close(struct net_device *dev); + +static void +virtio_net_netmap_free_os_buf(struct virtnet_info *vi, enum txrx t, + int idx, void *buf) +{ + if (t == NR_TX) { + dev_kfree_skb(buf); + } else { + if (vi->mergeable_rx_bufs) { + unsigned long ctx = (unsigned long)buf; + void *base = mergeable_ctx_to_buf_address(ctx); + put_page(virt_to_head_page(base)); + } else if (vi->big_packets) { + give_pages(&vi->rq[idx], buf); + } else { + dev_kfree_skb(buf); + } + } +} + +static void +virtio_net_netmap_detach_unused(struct virtnet_info *vi, bool onoff, + enum txrx t, int idx) +{ + struct virtqueue* vq = (t == NR_RX) ? vi->rq[idx].vq : vi->sq[idx].vq; + unsigned int n = 0; + void *buf; + + while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) { + if (!onoff) { + /* This is a netmap buffer, so there is + * nothing to do. */ + } else { + virtio_net_netmap_free_os_buf(vi, t, idx, buf); + } + n++; + } + + if (n) + nm_prinf("%d sgs detached on %s-%d (onoff=%d)", + n, nm_txrx2str(t), idx, onoff); +} + +static void +virtio_net_netmap_drain_used(struct virtnet_info *vi, bool onoff, + enum txrx t, int idx) +{ + struct virtqueue* vq = (t == NR_RX) ? vi->rq[idx].vq : vi->sq[idx].vq; + unsigned int len, n = 0; + void *buf; + + while ((buf = virtqueue_get_buf(vq, &len)) != NULL) { + if (!onoff) { + /* This is a netmap buffer, so there is + * nothing to do. */ + } else { + virtio_net_netmap_free_os_buf(vi, t, idx, buf); + } + n++; + } + + if (n) + nm_prinf("%d sgs drained on %s-%d (onoff=%d)", + n, nm_txrx2str(t), idx, onoff); +} + +/* Initialize scatter-gather lists used to publish netmap + * buffers through virtio descriptors, in such a way that each + * each scatter-gather list contains exactly two descriptors + * (which can point to a netmap buffer). This initialization is + * necessary to prevent the virtio frontend (host) to think + * we are using multi-descriptors scatter-gather lists. */ +static void +virtio_net_netmap_init_sgs(struct virtnet_info *vi) +{ + int i; + + for (i = 0; i < vi->max_queue_pairs; i++) { + sg_init_table(vi->sq[i].sg, 2); + sg_init_table(vi->rq[i].sg, 2); + } +} + +/* Register and unregister. */ +static int +virtio_net_netmap_reg(struct netmap_adapter *na, int onoff) +{ + struct ifnet *ifp = na->ifp; + struct virtnet_info *vi = netdev_priv(ifp); + bool was_up = false; + enum txrx t; + int i; + + /* It's important to make sure each virtnet_close() matches + * a virtnet_open(), otherwise a napi_disable() is not matched by + * a napi_enable(), which results in a deadlock. */ + if (netif_running(ifp)) { + was_up = true; + /* Down the interface. This also disables napi. */ + virtnet_close(ifp); + } + + if (onoff) { + /* Enable netmap mode before draining and detaching OS + * buffers, to prevent the OS to transmit packets + * while we are doing that. */ + nm_set_native_flags(na); + + for_rx_tx(t) { + /* Hardware rings. */ + for (i = 0; i < nma_get_nrings(na, t); i++) { + struct netmap_kring *kring = NMR(na, t)[i]; + + if (!nm_kring_pending_on(kring)) + continue; + + /* Get used OS buffers. */ + virtio_net_netmap_drain_used(vi, onoff, t, i); + + /* Detach and free any unused OS buffers. */ + virtio_net_netmap_detach_unused(vi, onoff, t, i); + + /* Initialize scatter-gater buffers for + * netmap mode. */ + virtio_net_netmap_init_sgs(vi); + + kring->nr_mode = NKR_NETMAP_ON; + } + + /* Host rings. */ + for (i = 0; i < nma_get_host_nrings(na, t); i++) { + struct netmap_kring *kring = + NMR(na, t)[nma_get_nrings(na, t) + i]; + + if (nm_kring_pending_on(kring)) { + kring->nr_mode = NKR_NETMAP_ON; + } + } + } + } else { + for_rx_tx(t) { + /* Hardware rings. */ + for (i = 0; i < nma_get_nrings(na, t); i++) { + struct netmap_kring *kring = NMR(na, t)[i]; + + if (!nm_kring_pending_off(kring)) + continue; + + /* Get used netmap buffers. */ + virtio_net_netmap_drain_used(vi, onoff, t, i); + + /* Detach and free any unused netmap buffers. */ + virtio_net_netmap_detach_unused(vi, onoff, t, i); + + kring->nr_mode = NKR_NETMAP_OFF; + } + + /* Host rings. */ + for (i = 0; i < nma_get_host_nrings(na, t); i++) { + struct netmap_kring *kring = + NMR(na, t)[nma_get_nrings(na, t) + i]; + + if (nm_kring_pending_off(kring)) { + kring->nr_mode = NKR_NETMAP_OFF; + } + } + } + + /* Disable netmap mode after netmap buffers have been drained + * and detached, to prevent the OS to start transmitting while + * we are doing that. */ + nm_clear_native_flags(na); + } + + if (was_up) { + /* Up the interface. This also enables the napi. */ + virtnet_open(ifp); + } + + return 0; +} + +/* Prepare an RX virtqueue for netmap operation. Returns true if + * the queue is ready for netmap and false if it is not going to + * work in netmap mode. */ +static bool +virtio_net_netmap_init_buffers(struct virtnet_info *vi, int r) +{ + size_t vnet_hdr_len = vi->mergeable_rx_bufs ? + sizeof(vi->rq[r].shared_rxvhdr) : + sizeof(vi->rq[r].shared_rxvhdr.hdr); + struct netmap_adapter *na = NA(vi->dev); + struct netmap_kring *kring; + struct virtqueue *vq; + int i; + + if (!nm_netmap_on(na)) { + return false; + } + + kring = na->rx_rings[r]; + if (kring->nr_mode != NKR_NETMAP_ON) { + return false; + } + + vq = vi->rq[r].vq; + + /* + * Add exactly na->num_rx_desc descriptor chains to this RX + * virtqueue, as virtio_netmap_rxsync() assumes the chains + * are returned in the same order by virtqueue_get_buf(). + * It is technically possible that the hypervisor returns + * na->num_rx_desc chains before the user can consume them, + * so virtio_netmap_rxsync() must prevent ring->tail to + * wrap around ring->head. + */ + for (i = 0; i < na->num_rx_desc && vq->num_free > 0; i++) { + struct netmap_ring *ring = kring->ring; + struct scatterlist *sg = vi->rq[r].sg; + struct netmap_slot *slot; + void *addr; + int err; + + slot = &ring->slot[i]; + addr = NMB(na, slot); + sg_set_buf(sg, &vi->rq[r].shared_rxvhdr, vnet_hdr_len); + sg_set_buf(sg + 1, addr, NETMAP_BUF_SIZE(na)); + err = virtqueue_add_inbuf(vq, sg, 2, na, GFP_ATOMIC); + if (err < 0) { + nm_prerr("virtqueue_add_inbuf() failed"); + return 0; + } + } + nm_prinf("%s-rx-%d: %d netmap buffers published", na->name, + r, i); + + return true; +} + +/* Reconcile kernel and user view of the transmit ring. */ +static int +virtio_net_netmap_txsync(struct netmap_kring *kring, int flags) +{ + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *ring = kring->ring; + u_int ring_nr = kring->ring_id; + u_int nm_i; /* index into the netmap ring */ + u_int nic_i; /* index into the NIC ring */ + u_int n; + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + + /* device-specific */ + struct virtnet_info *vi = netdev_priv(ifp); + struct send_queue *sq = vi->sq + ring_nr; + struct virtqueue *vq = sq->vq; + struct scatterlist *sg = sq->sg; + size_t vnet_hdr_len = vi->mergeable_rx_bufs ? + sizeof(sq->shared_txvhdr) : + sizeof(sq->shared_txvhdr.hdr); + int interrupts = !(kring->nr_kflags & NKR_NOINTR); + struct netmap_adapter *token; + + if (!netif_running(ifp)) { + /* All the new slots are now unavailable. */ + goto out; + } + + /* + * First part: process new packets to send. + */ + nm_i = kring->nr_hwcur; + if (nm_i != head) { /* we have new packets to send */ + nic_i = netmap_idx_k2n(kring, nm_i); + for (n = 0; nm_i != head; n++) { + struct netmap_slot *slot = &ring->slot[nm_i]; + u_int len = slot->len; + void *addr = NMB(na, slot); + int nospace; + + NM_CHECK_ADDR_LEN(na, addr, len); + + slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED); + /* Initialize the scatterlist and expose it to + * the hypervisor. */ + sg_set_buf(sg, &sq->shared_txvhdr, vnet_hdr_len); + sg_set_buf(sg + 1, addr, len); + nospace = virtqueue_add_outbuf(vq, sg, 2, na, GFP_ATOMIC); + if (nospace) { + nm_prerr("virtqueue_add_outbuf failed [err=%d]", + nospace); + break; + } + + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + } + + virtqueue_kick(vq); + + /* Update hwcur depending on where we stopped. */ + kring->nr_hwcur = nm_i; /* note we might break early */ + } +out: + if (interrupts && vq->num_free < 32) + virtqueue_enable_cb_delayed(vq); + + /* Free used slots. We only consider our own used buffers, recognized + * by the token we passed to virtqueue_add_outbuf. + */ + n = 0; + for (;;) { + token = virtqueue_get_buf(vq, &nic_i); /* dummy 2nd arg */ + if (token == NULL) + break; + if (unlikely(token != na)) + nm_prerr("BUG: token mismatch"); + else + n++; + } + if (n > 0) { + kring->nr_hwtail += n; + if (kring->nr_hwtail > lim) + kring->nr_hwtail -= lim + 1; + } + + return 0; +} + +/* Reconcile kernel and user view of the receive ring. */ +static int +virtio_net_netmap_rxsync(struct netmap_kring *kring, int flags) +{ + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *ring = kring->ring; + u_int ring_nr = kring->ring_id; + u_int nm_i; /* index into the netmap ring */ + u_int n; + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + int force_update = (flags & NAF_FORCE_READ) || + (kring->nr_kflags & NKR_PENDINTR); + int interrupts = !(kring->nr_kflags & NKR_NOINTR); + + /* device-specific */ + struct virtnet_info *vi = netdev_priv(ifp); + struct receive_queue *rq = vi->rq + ring_nr; + struct virtqueue *vq = rq->vq; + struct scatterlist *sg = rq->sg; + size_t vnet_hdr_len = vi->mergeable_rx_bufs ? + sizeof(rq->shared_rxvhdr) : + sizeof(rq->shared_rxvhdr.hdr); + + virtqueue_disable_cb(vq); + + /* + * First part: import newly received packets. + * Only accept our own buffers (matching the token). We should only get + * matching buffers, because of virtio_net_netmap_detach_unused() and + * virtio_net_netmap_init_buffers(). We may need to stop early to avoid + * hwtail to overrun hwcur. + */ + if (netmap_no_pendintr || force_update) { + uint32_t hwtail_lim = nm_prev(kring->nr_hwcur, lim); + struct netmap_adapter *token; + + + nm_i = kring->nr_hwtail; + n = 0; + while (nm_i != hwtail_lim) { + int len; + token = virtqueue_get_buf(vq, &len); + if (token == NULL) + break; + + if (unlikely(token != na)) { + nm_prlim(5, "Received unexpected virtqueue token %p\n", + token); + } else { + /* Skip the virtio-net header. */ + len -= vnet_hdr_len; + if (unlikely(len < 0)) { + nm_prlim(1, "Truncated virtio-net-header, missing %d" + " bytes", -len); + len = 0; + } + + ring->slot[nm_i].len = len; + ring->slot[nm_i].flags = 0; + nm_i = nm_next(nm_i, lim); + n++; + } + } + kring->nr_hwtail = nm_i; + kring->nr_kflags &= ~NKR_PENDINTR; + } + nm_prdis("[B] h %d c %d hwcur %d hwtail %d", + ring->head, ring->cur, kring->nr_hwcur, + kring->nr_hwtail); + + /* + * Second part: skip past packets that userspace has released. + */ + nm_i = kring->nr_hwcur; /* netmap ring index */ + if (nm_i != head) { + int nospace = 0; + + for (; nm_i != head; nm_i = nm_next(nm_i, lim)) { + struct netmap_slot *slot = &ring->slot[nm_i]; + void *addr = NMB(na, slot); + + if (addr == NETMAP_BUF_BASE(na)) /* bad buf */ + return netmap_ring_reinit(kring); + + slot->flags &= ~NS_BUF_CHANGED; + + /* Initialize the scatterlist and expose it to + * the hypervisor. */ + sg_set_buf(sg, &rq->shared_rxvhdr, vnet_hdr_len); + sg_set_buf(sg + 1, addr, NETMAP_BUF_SIZE(na)); + nospace = virtqueue_add_inbuf(vq, sg, 2, na, GFP_ATOMIC); + if (nospace) { + nm_prlim(2, "virtqueue_add_inbuf failed [err=%d]", + nospace); + break; + } + } + virtqueue_kick(vq); + kring->nr_hwcur = nm_i; + } + + /* We have finished processing used RX buffers, so we have to tell + * the hypervisor to make a call when more used RX buffers will be + * ready. + */ + if (interrupts) + virtqueue_enable_cb(vq); + + + nm_prdis("[C] h %d c %d t %d hwcur %d hwtail %d", + ring->head, ring->cur, ring->tail, + kring->nr_hwcur, kring->nr_hwtail); + + return 0; +} + +/* Enable/disable interrupts on all virtqueues. */ +static void +virtio_net_netmap_intr(struct netmap_adapter *na, int onoff) +{ + struct virtnet_info *vi = netdev_priv(na->ifp); + enum txrx t; + int i; + + for_rx_tx(t) { + for (i = 0; i < nma_get_nrings(na, t); i++) { + struct virtqueue *vq; + + vq = t == NR_RX ? vi->rq[i].vq : vi->sq[i].vq; + + if (onoff) + virtqueue_enable_cb(vq); + else + virtqueue_disable_cb(vq); + } + } +} + +static void +virtio_net_netmap_attach(struct virtnet_info *vi) +{ + struct netmap_adapter na; + + bzero(&na, sizeof(na)); + + na.ifp = vi->dev; + na.na_flags = 0; + na.num_tx_desc = virtqueue_get_vring_size(vi->sq[0].vq); + na.num_rx_desc = virtqueue_get_vring_size(vi->rq[0].vq); + na.num_tx_rings = na.num_rx_rings = vi->max_queue_pairs; + na.rx_buf_maxsize = 0; + na.nm_register = virtio_net_netmap_reg; + na.nm_txsync = virtio_net_netmap_txsync; + na.nm_rxsync = virtio_net_netmap_rxsync; + na.nm_intr = virtio_net_netmap_intr; + na.nm_config = NULL; + + netmap_attach(&na); +} + +/* end of file */ diff --git a/LINUX/if_vmxnet3_netmap.h b/LINUX/if_vmxnet3_netmap.h new file mode 100644 index 000000000..dd70dc6d8 --- /dev/null +++ b/LINUX/if_vmxnet3_netmap.h @@ -0,0 +1,593 @@ + +#ifndef _IF_VMXNET3_NETMAP_H_ +#define _IF_VMXNET3_NETMAP_H_ + +#include +#include +#include + +#define SOFTC_T vmxnet3_adapter + +static int vmxnet3_rq_create_all(struct vmxnet3_adapter *adapter); +static void vmxnet3_unmap_tx_buf(struct vmxnet3_tx_buf_info *tbi, + struct pci_dev *pdev); + +static int vmxnet3_netmap_reg(struct netmap_adapter *na, int onoff) +{ + int err = 0; + + struct ifnet *ifp = na->ifp; + struct SOFTC_T *adapter = netdev_priv(ifp); + + /* protect against other reinit */ + while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) + usleep_range(1000, 2000); + + if (netif_running(adapter->netdev)) { + vmxnet3_quiesce_dev(adapter); + vmxnet3_reset_dev(adapter); + + vmxnet3_rq_destroy_all(adapter); + + err = vmxnet3_rq_create_all(adapter); + if (err) + goto out; + } + + /* enable or disable flags and callbacks in na and ifp */ + if (onoff) { + nm_set_native_flags(na); + } else { + nm_clear_native_flags(na); + } + + if (netif_running(adapter->netdev)) { + err = vmxnet3_activate_dev(adapter); + if (err) + goto out; + } else { + vmxnet3_reset_dev(adapter); + } + +out: + clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); + + if (err) { + vmxnet3_force_close(adapter); + } + + return 0; +} + +static int vmxnet3_netmap_unmap_pkt(u32 eop_idx, struct vmxnet3_tx_queue *tq, + struct pci_dev *pdev) +{ + int entries = 0; + + // + // no out of order completion + // + + BUG_ON(tq->buf_info[eop_idx].sop_idx != tq->tx_ring.next2comp); + BUG_ON(VMXNET3_TXDESC_GET_EOP(&(tq->tx_ring.base[eop_idx].txd)) != 1); + + BUG_ON(tq->buf_info[eop_idx].skb != NULL); + + VMXNET3_INC_RING_IDX_ONLY(eop_idx, tq->tx_ring.size); + + while (tq->tx_ring.next2comp != eop_idx) { + vmxnet3_unmap_tx_buf(tq->buf_info + tq->tx_ring.next2comp, + pdev); + + // + // update next2comp w/o tx_lock. Since we are marking more, + // instead of less, tx ring entries avail, the worst case is + // that the tx routine incorrectly re-queues a pkt due to + // insufficient tx ring entries. + // + + vmxnet3_cmd_ring_adv_next2comp(&tq->tx_ring); + entries++; + } + + return entries; +} + +static int vmxnet3_netmap_tq_tx_complete(struct vmxnet3_tx_queue *tq, + struct pci_dev *pdev) +{ + int completed = 0; + union Vmxnet3_GenericDesc *gdesc; + + gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; + + while (VMXNET3_TCD_GET_GEN(&gdesc->tcd) == tq->comp_ring.gen) { + completed += vmxnet3_netmap_unmap_pkt( + VMXNET3_TCD_GET_TXIDX(&gdesc->tcd), tq, pdev); + + vmxnet3_comp_ring_adv_next2proc(&tq->comp_ring); + gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; + } + + return completed; +} + +static int vmxnet3_netmap_txsync(struct netmap_kring *kring, int flags) +{ +#define kUseTwoTxDescForPacket 0 +#define kMinFreeTxDescForPacket (kUseTwoTxDescForPacket ? 2 : 1) + + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *ring = kring->ring; + + u_int n; + u_int nm_i; // index into the netmap ring + int completed; + u_int deferred = 0; + u_int ring_nr = kring->ring_id; + + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + + union Vmxnet3_GenericDesc *gdesc; + struct SOFTC_T *adapter = netdev_priv(ifp); + struct vmxnet3_tx_queue *tq = &adapter->tx_queue[ring_nr]; + + if (!netif_carrier_ok(ifp)) + return 0; + + // + // Free up the comp_descriptors aggressively + // + + completed = vmxnet3_netmap_tq_tx_complete(tq, adapter->pdev); + + // + // Reclaim buffers for completed transmissions + // + + kring->nr_hwtail = + nm_prev(tq->comp_ring.next2proc, tq->comp_ring.size - 1); + + // + // Process new packets to send + // + + nm_i = kring->nr_hwcur; + + if (nm_i != head) { + for (n = 0; nm_i != head; n++) { + u32 copy_size = 0; + int free_cmd_desc_count; + unsigned long lock_flags; + + union Vmxnet3_GenericDesc *sop_txd; + union Vmxnet3_GenericDesc *eop_txd; + + struct netmap_slot *slot = &ring->slot[nm_i]; + + dma_addr_t dma_addr; + u_int packet_len = slot->len; + void *packet_addr = PNMB(na, slot, &dma_addr); + + slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED); + netmap_sync_map_dev(na, (bus_dma_tag_t)na->pdev, &dma_addr, + packet_len, NR_TX); + + spin_lock_irqsave(&tq->tx_lock, lock_flags); + + free_cmd_desc_count = + vmxnet3_cmd_ring_desc_avail(&tq->tx_ring); + + if (free_cmd_desc_count < kMinFreeTxDescForPacket) { + tq->stats.tx_ring_full++; + spin_unlock_irqrestore(&tq->tx_lock, + lock_flags); + break; + } + + // + // Copy header + // + + if (kUseTwoTxDescForPacket) { + struct Vmxnet3_TxDataDesc *tdd = tdd = + tq->data_ring.base + + tq->tx_ring.next2fill; + + copy_size = min((u_int)VMXNET3_HDR_COPY_SIZE, + packet_len); + memcpy(tdd->data, packet_addr, copy_size); + } + + // + // Map rest of data + // + + { + u32 dw2; + u32 len; + u32 buf_offset; + union Vmxnet3_GenericDesc *gdesc; + struct vmxnet3_tx_buf_info *tbi = NULL; + + // + // use the previous gen bit for the SOP desc + // + + dw2 = (tq->tx_ring.gen ^ 0x1) + << VMXNET3_TXD_GEN_SHIFT; + + sop_txd = tq->tx_ring.base + + tq->tx_ring.next2fill; + gdesc = sop_txd; + + // + // Setup TX descriptor for the header + // + + if (copy_size) { + sop_txd->txd.addr = cpu_to_le64( + tq->data_ring.basePA + + tq->tx_ring.next2fill * + sizeof(struct + Vmxnet3_TxDataDesc)); + sop_txd->dword[2] = + cpu_to_le32(dw2 | copy_size); + sop_txd->dword[3] = 0; + + tbi = tq->buf_info + + tq->tx_ring.next2fill; + tbi->map_type = VMXNET3_MAP_NONE; + + vmxnet3_cmd_ring_adv_next2fill( + &tq->tx_ring); + + // + // use the right gen for non-SOP desc + // + + dw2 = tq->tx_ring.gen + << VMXNET3_TXD_GEN_SHIFT; + } + + // + // Handle linear part + // + + len = packet_len - copy_size; + buf_offset = copy_size; + + if (len) { + u32 buf_size; + + BUG_ON(len > VMXNET3_MAX_TX_BUF_SIZE); + buf_size = len; + dw2 |= len; + + tbi = tq->buf_info + + tq->tx_ring.next2fill; + tbi->map_type = VMXNET3_MAP_NONE; + tbi->dma_addr = dma_addr + buf_offset; + tbi->len = buf_size; + + gdesc = tq->tx_ring.base + + tq->tx_ring.next2fill; + BUG_ON(gdesc->txd.gen == + tq->tx_ring.gen); + + gdesc->txd.addr = + cpu_to_le64(tbi->dma_addr); + gdesc->dword[2] = cpu_to_le32(dw2); + gdesc->dword[3] = 0; + + vmxnet3_cmd_ring_adv_next2fill( + &tq->tx_ring); + dw2 = tq->tx_ring.gen + << VMXNET3_TXD_GEN_SHIFT; + } + + eop_txd = gdesc; + + tbi->skb = NULL; + tbi->sop_idx = sop_txd - tq->tx_ring.base; + } + + // + // setup the EOP desc + // + + eop_txd->dword[3] = + cpu_to_le32(VMXNET3_TXD_CQ | VMXNET3_TXD_EOP); + + // + // setup the SOP desc + // + + gdesc = sop_txd; + + gdesc->txd.om = 0; + gdesc->txd.msscof = 0; + + // + // finally flips the GEN bit of the SOP desc + // + + gdesc->dword[2] = cpu_to_le32( + le32_to_cpu(gdesc->dword[2]) ^ VMXNET3_TXD_GEN); + + deferred++; + + spin_unlock_irqrestore(&tq->tx_lock, lock_flags); + + // + // go to the next netmap slot + // + + nm_i = nm_next(nm_i, lim); + } + + kring->nr_hwcur = head; + } + + // + // Notify vSwitch that packets are available. + // + + if (deferred >= 1) { + VMXNET3_WRITE_BAR0_REG(adapter, (VMXNET3_REG_TXPROD + + tq->qid * VMXNET3_REG_ALIGN), + tq->tx_ring.next2fill); + } + + return 0; +} + +static int vmxnet3_netmap_rxsync(struct netmap_kring *kring, int flags) +{ + static const u32 rxprod_reg[] = { VMXNET3_REG_RXPROD, + VMXNET3_REG_RXPROD2 }; + + u32 num_pkts = 0; + u32 netmap_offset = 0; + u_int nm_i = 0; // index into the netmap ring + + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *nmring = kring->ring; + + u_int ring_nr = kring->ring_id; + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + int force_update = + (flags & NAF_FORCE_READ) || kring->nr_kflags & NKR_PENDINTR; + + struct Vmxnet3_RxCompDesc *rcd; + struct SOFTC_T *adapter = netdev_priv(ifp); + struct vmxnet3_rx_queue *rq = &adapter->rx_queue[ring_nr]; + + if (!netif_carrier_ok(ifp)) + return 0; + + if (head > lim) + return netmap_ring_reinit(kring); + + // + // First part: import newly received packets. + // + + if (netmap_no_pendintr || force_update) { + uint32_t hwtail_lim = nm_prev(kring->nr_hwcur, lim); + + nm_i = kring->nr_hwtail; + + vmxnet3_getRxComp( + rcd, &rq->comp_ring.base[rq->comp_ring.next2proc].rcd, + &rxComp); + + while (rcd->gen == rq->comp_ring.gen && nm_i != hwtail_lim) { + u32 idx; + u32 ring_idx; + int num_to_alloc; + void *packet_addr; + void *packet_nic_addr; + struct netmap_slot *slot; + struct Vmxnet3_RxDesc *rxd; + struct vmxnet3_rx_buf_info *rbi; + struct vmxnet3_cmd_ring *ring = NULL; + + slot = nmring->slot + nm_i; + packet_addr = NMB(na, slot); + + BUG_ON(rcd->rqID != rq->qid && rcd->rqID != rq->qid2); + idx = rcd->rxdIdx; + ring_idx = rcd->rqID < adapter->num_rx_queues ? 0 : 1; + ring = rq->rx_ring + ring_idx; + vmxnet3_getRxDesc(rxd, + &rq->rx_ring[ring_idx].base[idx].rxd, + &rxCmdDesc); + rbi = rq->buf_info[ring_idx] + idx; + BUG_ON(rxd->addr != rbi->dma_addr || + rxd->len != rbi->len); + + if (rcd->eop && rcd->err) { + rq->stats.drop_total++; + rq->stats.drop_err++; + + if (!rcd->fcs) + rq->stats.drop_fcs++; + + goto rcd_done; + } + + if (rcd->sop) { + BUG_ON(rxd->btype != VMXNET3_RXD_BTYPE_HEAD || + rcd->rqID != rq->qid); + BUG_ON(rbi->buf_type != VMXNET3_RX_BUF_SKB); + + if (rcd->len == 0) { + BUG_ON(!(rcd->sop && rcd->eop)); + goto rcd_done; + } + + packet_nic_addr = rbi->skb->data; + memcpy(packet_addr, packet_nic_addr, rcd->len); + + netmap_offset = rcd->len; + } else { + // non SOP buffer must be type 1 in most cases + BUG_ON(rbi->buf_type != VMXNET3_RX_BUF_PAGE); + BUG_ON(rxd->btype != VMXNET3_RXD_BTYPE_BODY); + + packet_nic_addr = page_address(rbi->page); + memcpy(packet_addr + netmap_offset, + packet_nic_addr, rcd->len); + + netmap_offset += rcd->len; + } + + if (rcd->eop) { + dma_addr_t dma_addr; + + slot->len = netmap_offset; + slot->flags = 0; + + PNMB(na, slot, &dma_addr); + netmap_sync_map_cpu(na, (bus_dma_tag_t)na->pdev, + &dma_addr, slot->len, NR_RX); + + num_pkts++; + nm_i = nm_next(nm_i, lim); + } + + rcd_done: + ring->next2comp = idx; + + num_to_alloc = vmxnet3_cmd_ring_desc_avail(ring); + ring = rq->rx_ring + ring_idx; + + while (num_to_alloc) { + vmxnet3_getRxDesc( + rxd, &ring->base[ring->next2fill].rxd, + &rxCmdDesc); + BUG_ON(!rxd->addr); + + // Recv desc is ready to be used by the device + rxd->gen = ring->gen; + vmxnet3_cmd_ring_adv_next2fill(ring); + num_to_alloc--; + } + + // if needed, update the register + if (unlikely(rq->shared->updateRxProd)) { + VMXNET3_WRITE_BAR0_REG( + adapter, + rxprod_reg[ring_idx] + + rq->qid * VMXNET3_REG_ALIGN, + ring->next2fill); + } + + vmxnet3_comp_ring_adv_next2proc(&rq->comp_ring); + vmxnet3_getRxComp( + rcd, + &rq->comp_ring.base[rq->comp_ring.next2proc] + .rcd, + &rxComp); + } + + if (num_pkts) { + kring->nr_hwtail = nm_i; + } + + kring->nr_kflags &= ~NKR_PENDINTR; + } + + // + // Second part: skip past packets that userspace has released. + // + + nm_i = kring->nr_hwcur; + + if (nm_i != head) { + int n; + + for (n = 0; nm_i != head; n++) { + struct netmap_slot *slot = &nmring->slot[nm_i]; + uint64_t paddr; + void *addr = PNMB(na, slot, &paddr); + + if (addr == NETMAP_BUF_BASE(na)) // bad buf + goto ring_reset; + + slot->flags &= ~NS_BUF_CHANGED; + + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, NETMAP_BUF_SIZE(na), NR_RX); + + nm_i = nm_next(nm_i, lim); + } + kring->nr_hwcur = head; + } + + return 0; + +ring_reset: + return netmap_ring_reinit(kring); +} + +static void vmxnet3_netmap_intr(struct netmap_adapter *na, int onoff) +{ + struct ifnet *ifp = na->ifp; + struct SOFTC_T *adapter = netdev_priv(ifp); + + if (onoff) + vmxnet3_enable_all_intrs(adapter); + else + vmxnet3_disable_all_intrs(adapter); +} + +static void vmxnet3_netmap_init_buffers(struct SOFTC_T *adapter) +{ + u32 r; + struct ifnet *ifp = adapter->netdev; + struct netmap_adapter *na = NA(ifp); + + if (!nm_native_on(na)) + return; + + for (r = 0; r < na->num_rx_rings; r++) { + (void)netmap_reset(na, NR_RX, r, 0); + } + + for (r = 0; r < na->num_tx_rings; r++) { + (void)netmap_reset(na, NR_TX, r, 0); + } +} + +static void vmxnet3_netmap_attach(struct SOFTC_T *adapter) +{ + struct netmap_adapter na; + + bzero(&na, sizeof(na)); + + na.ifp = adapter->netdev; + na.pdev = &adapter->pdev->dev; + na.num_tx_desc = adapter->tx_ring_size; + na.num_rx_desc = adapter->rx_ring_size; + na.nm_register = vmxnet3_netmap_reg; + na.nm_txsync = vmxnet3_netmap_txsync; + na.nm_rxsync = vmxnet3_netmap_rxsync; + na.num_tx_rings = adapter->num_tx_queues; + na.num_rx_rings = adapter->num_rx_queues; + na.nm_intr = vmxnet3_netmap_intr; + + netmap_attach(&na); +} + +static void vmxnet3_netmap_detach(struct net_device *device) +{ + netmap_detach(device); +} + +#endif // _IF_VMXNET3_NETMAP_H_ diff --git a/LINUX/if_vmxnet3_netmap_v2.h b/LINUX/if_vmxnet3_netmap_v2.h new file mode 100644 index 000000000..392f67336 --- /dev/null +++ b/LINUX/if_vmxnet3_netmap_v2.h @@ -0,0 +1,562 @@ + +#ifndef _IF_VMXNET3_NETMAP_H_ +#define _IF_VMXNET3_NETMAP_H_ + +#include +#include +#include + +#define SOFTC_T vmxnet3_adapter + +#ifdef NETMAP_LINUX_HAVE_VMXNET3_STATIC_RQ_CREATE +static +#endif /* NETMAP_LINUX_HAVE_STATIC_RQ_CREATE */ +int vmxnet3_rq_create_all(struct vmxnet3_adapter *adapter); + +static int +vmxnet3_netmap_reg(struct netmap_adapter *na, int onoff) +{ + int err = 0; + + struct ifnet *ifp = na->ifp; + struct SOFTC_T *adapter = netdev_priv(ifp); + + /* protect against other reinit */ + while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) + usleep_range(1000, 2000); + + if (netif_running(adapter->netdev)) { + vmxnet3_quiesce_dev(adapter); + vmxnet3_reset_dev(adapter); + + vmxnet3_rq_destroy_all(adapter); + } + + /* enable or disable flags and callbacks in na and ifp */ + if (onoff) { + nm_set_native_flags(na); + } else { + nm_clear_native_flags(na); + } + + err = vmxnet3_rq_create_all(adapter); + if (err) + goto out; + + if (netif_running(adapter->netdev)) { + err = vmxnet3_activate_dev(adapter); + if (err) + goto out; + } else { + vmxnet3_reset_dev(adapter); + } + +out: + clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); + + if (err) { + vmxnet3_force_close(adapter); + } + + return 0; +} + +static u_int +vmxnet3_netmap_tq_tx_complete(struct vmxnet3_tx_queue *tq, struct pci_dev *pdev) +{ + u_int completed = 0; + union Vmxnet3_GenericDesc *gdesc; + + gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; + + while (VMXNET3_TCD_GET_GEN(&gdesc->tcd) == tq->comp_ring.gen) { + vmxnet3_cmd_ring_adv_next2comp(&tq->tx_ring); + vmxnet3_comp_ring_adv_next2proc(&tq->comp_ring); + + gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; + + completed++; + } + + return completed; +} + +static int +vmxnet3_netmap_txsync(struct netmap_kring *kring, int flags) +{ + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *ring = kring->ring; + + u_int n; + u_int nm_i; // index into the netmap ring + u_int completed; + u_int transmitted = 0; + u_int ring_nr = kring->ring_id; + + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + + struct SOFTC_T *adapter = netdev_priv(ifp); + struct vmxnet3_tx_queue *tq = &adapter->tx_queue[ring_nr]; + + if (!netif_carrier_ok(ifp)) + return 0; + + // + // Free up the comp_descriptors aggressively + // + + completed = vmxnet3_netmap_tq_tx_complete(tq, adapter->pdev); + + // + // Reclaim buffers for completed transmissions + // + + kring->nr_hwtail = + nm_prev(tq->comp_ring.next2proc, tq->comp_ring.size - 1); + + // + // Process new packets to send + // + + nm_i = kring->nr_hwcur; + + if (nm_i != head) { + for (n = 0; nm_i != head; n++) { + int free_cmd_desc_count; + unsigned long lock_flags; + + struct netmap_slot *slot = ring->slot + nm_i; + u_int packet_len = slot->len; + struct vmxnet3_tx_buf_info *tbi; + union Vmxnet3_GenericDesc *gdesc; + uint64_t paddr; + + PNMB(na, slot, &paddr); + + slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED); + netmap_sync_map_dev(na, (bus_dma_tag_t)na->pdev, &paddr, + packet_len, NR_TX); + + spin_lock_irqsave(&tq->tx_lock, lock_flags); + + tbi = tq->buf_info + tq->tx_ring.next2fill; + gdesc = tq->tx_ring.base + tq->tx_ring.next2fill; + + free_cmd_desc_count = + vmxnet3_cmd_ring_desc_avail(&tq->tx_ring); + + if (free_cmd_desc_count < 1) { + tq->stats.tx_ring_full++; + spin_unlock_irqrestore(&tq->tx_lock, + lock_flags); + break; + } + + BUG_ON(packet_len > VMXNET3_MAX_TX_BUF_SIZE); + BUG_ON(gdesc->txd.addr != tbi->dma_addr); + BUG_ON(gdesc->txd.gen == tq->tx_ring.gen); + + /* comments in other driver implementations + *indicate a size of 0 denotes a packet of + *VMXNET3_MAX_TX_BUF_SIZE bytes */ + tbi->len = packet_len == VMXNET3_MAX_TX_BUF_SIZE + ? 0 + : packet_len; + + gdesc->dword[3] = + cpu_to_le32(VMXNET3_TXD_CQ | VMXNET3_TXD_EOP); + + dma_wmb(); + + // set the packet length and flip the GEN bit + gdesc->dword[2] = cpu_to_le32( + tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT | + packet_len); + + vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring); + + transmitted++; + spin_unlock_irqrestore(&tq->tx_lock, lock_flags); + + // + // go to the next netmap slot + // + nm_i = nm_next(nm_i, lim); + } + + kring->nr_hwcur = head; + } + + // + // Notify vSwitch that packets are available. + + if (transmitted >= tq->shared->txThreshold) { + tq->shared->txThreshold = 0; + VMXNET3_WRITE_BAR0_REG( + adapter, + (VMXNET3_REG_TXPROD + tq->qid * VMXNET3_REG_ALIGN), + tq->tx_ring.next2fill); + } + + return 0; +} + +static int +vmxnet3_netmap_rxsync(struct netmap_kring *kring, int flags) +{ + static const u32 rxprod_reg[] = {VMXNET3_REG_RXPROD, + VMXNET3_REG_RXPROD2}; + + u_int nm_i; + u_int nic_i; + + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *nmring = kring->ring; + + u_int ring_nr = kring->ring_id; + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + int force_update = + (flags & NAF_FORCE_READ) || kring->nr_kflags & NKR_PENDINTR; + + struct Vmxnet3_RxCompDesc *rcd; + struct SOFTC_T *adapter = netdev_priv(ifp); + struct vmxnet3_rx_queue *rq = &adapter->rx_queue[ring_nr]; + struct vmxnet3_cmd_ring *cmd_ring = rq->rx_ring; + + if (!netif_carrier_ok(ifp)) + return 0; + + if (head > lim) + return netmap_ring_reinit(kring); + + // + // First part: import newly received packets. + // + + if (netmap_no_pendintr || force_update) { + nm_i = kring->nr_hwtail; + nic_i = netmap_idx_k2n(kring, nm_i); + for (;;) { + struct netmap_slot *slot; + u_int rx_idx; + uint64_t paddr; + + vmxnet3_getRxComp( + rcd, + &rq->comp_ring.base[rq->comp_ring.next2proc] + .rcd, + &rxComp); + + if (rcd->gen != rq->comp_ring.gen) + break; + + dma_rmb(); + + // data ring has been disabled on device init + BUG_ON(rcd->rqID != rq->qid && rcd->rqID != rq->qid2); + + /* RX queues were configured to not fragment + *packets, so we expect both the SOP and EOP flags to be + *set in the RX completion desc + */ + BUG_ON(!(rcd->sop && rcd->eop)); + BUG_ON(rcd->len > NETMAP_BUF_SIZE(na)); + BUG_ON(VMXNET3_GET_RING_IDX(adapter, rcd->rqID) != 0); + + rx_idx = rcd->rxdIdx; + + /* device may have skipped some rx descs */ + while (unlikely(nic_i != rx_idx)) { + nm_prinf("%u skipped! rx_idx %u", nic_i, rx_idx); + /* the nic has skipped some slots because who + * knows why. To shelter the application from + * this we would need to rotate the + * kernel-owned segments of the netmap and nic + * rings. For now, we just set len=0 in the + * skipped slots and hope that this never + * happens. + */ + + nmring->slot[nm_i].len = 0; + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + } + + slot = nmring->slot + nm_i; + PNMB(na, slot, &paddr); + + slot->len = rcd->len; + slot->flags = 0; + netmap_sync_map_cpu(na, (bus_dma_tag_t)na->pdev, &paddr, + slot->len, NR_RX); + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + + /* XXX can this ever happen with all offloads disabled? + */ + if (rcd->err) { + rq->stats.drop_total++; + rq->stats.drop_err++; + + if (!rcd->fcs) + rq->stats.drop_fcs++; + } + + vmxnet3_comp_ring_adv_next2proc(&rq->comp_ring); + } + + kring->nr_hwtail = nm_i; + kring->nr_kflags &= ~NKR_PENDINTR; + } + + // + // Second part: skip past packets that userspace has released. + // + + nm_i = kring->nr_hwcur; + + if (nm_i != head) { + nic_i = netmap_idx_k2n(kring, nm_i); + while (nm_i != head) { + struct netmap_slot *slot = &nmring->slot[nm_i]; + struct Vmxnet3_RxDesc *rxd; + uint64_t paddr; + void *addr = PNMB(na, slot, &paddr); + + if (slot->flags & NS_BUF_CHANGED) { + + if (addr == NETMAP_BUF_BASE(na)) // bad buf + goto ring_reset; + + vmxnet3_getRxDesc( + rxd, + &cmd_ring->base[nic_i].rxd, + &rxCmdDesc); + + rxd->addr = paddr; + slot->flags &= ~NS_BUF_CHANGED; + /* Ensure that the writes to rxd->gen bits will be + * observed after all other writes to rxd objects. + */ + dma_wmb(); + } + netmap_sync_map_dev(na, + (bus_dma_tag_t)na->pdev, &paddr, + NETMAP_BUF_SIZE(na), NR_RX); + + vmxnet3_getRxDesc( + rxd, + &cmd_ring->base[cmd_ring->next2fill].rxd, + &rxCmdDesc); + rxd->gen = cmd_ring->gen; + vmxnet3_cmd_ring_adv_next2fill(cmd_ring); + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + } + kring->nr_hwcur = head; + + /* if needed, update the register */ + if (unlikely(rq->shared->updateRxProd)) { + VMXNET3_WRITE_BAR0_REG( + adapter, + rxprod_reg[kring->ring_id] + + rq->qid * VMXNET3_REG_ALIGN, + cmd_ring->next2fill); + } + } + + return 0; + +ring_reset: + return netmap_ring_reinit(kring); +} + +static void +vmxnet3_netmap_intr(struct netmap_adapter *na, int onoff) +{ + struct ifnet *ifp = na->ifp; + struct SOFTC_T *adapter = netdev_priv(ifp); + + if (onoff) + vmxnet3_enable_all_intrs(adapter); + else + vmxnet3_disable_all_intrs(adapter); +} + +/* configure RX queue buffers to point to Netmap buffers */ +static int +vmxnet3_netmap_rq_config_rx_buf(struct vmxnet3_rx_queue *rq, + struct SOFTC_T *adapter) +{ + struct ifnet *ifp = adapter->netdev; + struct netmap_adapter *na = NA(ifp); + + u_int i; + u_int nm_i; + u_int ring_idx; + u_int ring_nr = rq - adapter->rx_queue; + struct netmap_slot *slot = netmap_reset(na, NR_RX, ring_nr, 0); + + if (!slot) { + return 0; // not in native netmap mode + } + + nm_i = 0; + /* use only the 0th ring of each RX queue as it appears that the 1st + ring can only be used for packet fragments (VMXNET3_RXD_BTYPE_BODY), + which this driver doesn't support */ + for (ring_idx = 0; ring_idx < 1; ring_idx++) { + struct vmxnet3_cmd_ring *cmd_ring = rq->rx_ring + ring_idx; + + for (i = 0; i < cmd_ring->size; i++) { + struct vmxnet3_rx_buf_info *rbi = + rq->buf_info[ring_idx] + i; + union Vmxnet3_GenericDesc *gd = cmd_ring->base + i; + uint64_t paddr; + u_int si = netmap_idx_n2k(na->rx_rings[ring_nr], nm_i); + + PNMB(na, slot + si, &paddr); + + rbi->buf_type = VMXNET3_RX_BUF_NONE; + rbi->len = NETMAP_BUF_SIZE(na); + rbi->dma_addr = (dma_addr_t)paddr; + + gd->rxd.addr = cpu_to_le64(rbi->dma_addr); + gd->dword[2] = cpu_to_le32( + (!cmd_ring->gen << VMXNET3_RXD_GEN_SHIFT) | + (VMXNET3_RXD_BTYPE_HEAD + << VMXNET3_RXD_BTYPE_SHIFT) | + rbi->len); + nm_i++; + + if (i == cmd_ring->size - 1) + break; + + gd->dword[2] = cpu_to_le32( + gd->dword[2] | + (cmd_ring->gen << VMXNET3_RXD_GEN_SHIFT)); + vmxnet3_cmd_ring_adv_next2fill(cmd_ring); + } + } + + return 1; +} + +/* configure TX queue buffers to point to Netmap buffers */ +static int +vmxnet3_netmap_tq_config_tx_buf(struct vmxnet3_tx_queue *tq, + struct SOFTC_T *adapter) +{ + struct ifnet *ifp = adapter->netdev; + struct netmap_adapter *na = NA(ifp); + + u_int i; + u_int ring_nr = tq - adapter->tx_queue; + struct vmxnet3_cmd_ring *cmd_ring = &tq->tx_ring; + struct netmap_slot *slot = netmap_reset(na, NR_TX, ring_nr, 0); + + if (!slot) { + return 0; // not in native netmap mode + } + + for (i = 0; i < cmd_ring->size; i++) { + struct vmxnet3_tx_buf_info *tbi = tq->buf_info + i; + union Vmxnet3_GenericDesc *gd = cmd_ring->base + i; + uint64_t paddr; + u_int si = netmap_idx_n2k(na->tx_rings[ring_nr], i); + + PNMB(na, slot + si, &paddr); + + tbi->map_type = VMXNET3_MAP_NONE; + /* the buffer length will get overridden by the actual + packet length on transmit */ + tbi->len = NETMAP_BUF_SIZE(na); + tbi->dma_addr = (dma_addr_t)paddr; + tbi->sop_idx = i; + + gd->txd.addr = cpu_to_le64(tbi->dma_addr); + gd->dword[2] = 0; + gd->dword[3] = 0; + } + + return 1; +} + +static void +vmxnet3_netmap_set_rxdataring_enabled(struct SOFTC_T *adapter) +{ + struct ifnet *ifp = adapter->netdev; + struct netmap_adapter *na = NA(ifp); + + adapter->rxdataring_enabled = + nm_native_on(na) ? 0 : VMXNET3_VERSION_GE_3(adapter); +} + +static void +vmxnet3_netmap_init_buffers(struct SOFTC_T *adapter) +{ + struct ifnet *ifp = adapter->netdev; + struct netmap_adapter *na = NA(ifp); + + u_int r; + + if (!nm_native_on(na)) + return; + + for (r = 0; r < na->num_rx_rings; r++) { + (void)netmap_reset(na, NR_RX, r, 0); + } + + for (r = 0; r < na->num_tx_rings; r++) { + (void)netmap_reset(na, NR_TX, r, 0); + } + + return; +} + +static int +vmxnet3_netmap_config(struct netmap_adapter *na, struct nm_config_info *info) +{ + int ret = netmap_rings_config_get(na, info); + if (ret) { + return ret; + } + + info->rx_buf_maxsize = NETMAP_BUF_SIZE(na); + + return 0; +} + +static void +vmxnet3_netmap_attach(struct SOFTC_T *adapter) +{ + struct netmap_adapter na; + + bzero(&na, sizeof(na)); + + na.ifp = adapter->netdev; + na.pdev = &adapter->pdev->dev; + na.num_tx_desc = adapter->tx_ring_size; + na.num_rx_desc = adapter->rx_ring_size; + na.nm_register = vmxnet3_netmap_reg; + na.nm_txsync = vmxnet3_netmap_txsync; + na.nm_rxsync = vmxnet3_netmap_rxsync; + na.num_tx_rings = adapter->num_tx_queues; + na.num_rx_rings = adapter->num_rx_queues; + na.nm_intr = vmxnet3_netmap_intr; + na.nm_config = vmxnet3_netmap_config; + + netmap_attach(&na); +} + +static void +vmxnet3_netmap_detach(struct net_device *device) +{ + netmap_detach(device); +} + +#endif // _IF_VMXNET3_NETMAP_H_ diff --git a/LINUX/intel-fix.sh_ b/LINUX/intel-fix.sh_ new file mode 100755 index 000000000..df732c56e --- /dev/null +++ b/LINUX/intel-fix.sh_ @@ -0,0 +1,24 @@ +#!/bin/sh + +cd $1 + +[ -e common.mk ] || exit 0 + +patch -p1 <|#include "linux/auxiliary_bus.h"|' *_client.h || true +fi diff --git a/LINUX/ixgbe_netmap_linux.h b/LINUX/ixgbe_netmap_linux.h index 2bf57b016..7d0f86b15 100644 --- a/LINUX/ixgbe_netmap_linux.h +++ b/LINUX/ixgbe_netmap_linux.h @@ -24,8 +24,6 @@ */ /* - * $FreeBSD: head/sys/dev/netmap/ixgbe_netmap.h 230572 2012-01-26 09:55:16Z luigi $ - * * netmap support for: ixgbe (LINUX version) * * This file is meant to be a reference on how to implement @@ -47,6 +45,8 @@ #define NM_IXGBE_TDT(ring_nr) IXGBE_TDT(ring_nr) #define NM_IXGBE_TDH(ring_nr) IXGBE_TDH(ring_nr) #define NM_IXGBE_RDT(ring_nr) IXGBE_RDT(ring_nr) +#define NM_IXGBE_TDWBAH(ring_nr) IXGBE_TDWBAH(ring_nr) +#define NM_IXGBE_TDWBAL(ring_nr) IXGBE_TDWBAL(ring_nr) #define NM_IXGBE_ADAPTER ixgbe_adapter #define NM_IXGBE_RESETTING __IXGBE_RESETTING #define NM_IXGBE_DOWN(adapter) ixgbe_down(adapter) @@ -105,7 +105,7 @@ ixgbe_netmap_intr(struct netmap_adapter *na, int onoff) static void ixgbe_netmap_intr(struct netmap_adapter *na, int onoff) { - RD(5, "per-queue irq disable not supported"); + nm_prlim(1, "per-queue irq disable not supported"); } #endif /* NETMAP_LINUX_IXGBE_HAVE_DISABLE */ @@ -120,15 +120,13 @@ ixgbe_netmap_configure_srrctl(struct NM_IXGBE_ADAPTER *adapter, struct NM_IXGBE_ struct ixgbe_hw *hw = &adapter->hw; u32 srrctl; u8 reg_idx = rx_ring->reg_idx; + struct netmap_kring *kring = na->rx_rings[reg_idx]; if (hw->mac.type == ixgbe_mac_82598EB) { u16 mask = adapter->ring_feature[RING_F_RSS].mask; reg_idx &= mask; } - srrctl = IXGBE_RX_HDR_SIZE << 2; - srrctl |= NETMAP_BUF_SIZE(na) >> IXGBE_SRRCTL_BSIZEPKT_SHIFT; - D("bufsz: %d srrctl: %d", NETMAP_BUF_SIZE(na), - NETMAP_BUF_SIZE(na) >> IXGBE_SRRCTL_BSIZEPKT_SHIFT); + srrctl = kring->hwbuf_len >> IXGBE_SRRCTL_BSIZEPKT_SHIFT; /* * XXX * With Advanced RX descriptor, the address needs to be rewritten, @@ -137,20 +135,44 @@ ixgbe_netmap_configure_srrctl(struct NM_IXGBE_ADAPTER *adapter, struct NM_IXGBE_ * (ixgbe datasheet - Section 7.1.9) */ srrctl |= IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF; + nm_prdis("bufsz: %d srrctl: %x", kring->hwbuf_len, srrctl); IXGBE_WRITE_REG(hw, IXGBE_SRRCTL(reg_idx), srrctl); } +#ifdef NETMAP_LINUX_IXGBE_HAVE_NTA +#define NETMAP_LINUX_HAVE_NTA +#endif /* NETMAP_LINUX_IXGBE_HAVE_NTA */ + +#ifdef NETMAP_LINUX_HAVE_IXGBE_STATE_BITMAP +#define NM_IXGBE_STATE(adapter) (&(adapter)->state) +#else +#define NM_IXGBE_STATE(adapter) ((adapter)->state) +#endif /* NETMAP_LINUX_IXGBE_HAVE_STATE_BITMAP */ + #else /*********************************************************************** * ixgbevf * ***********************************************************************/ +#define NM_IXGBE_USE_TDH // TODO switch to head wb #define NM_IXGBE_TDT(ring_nr) IXGBE_VFTDT(ring_nr) #define NM_IXGBE_TDH(ring_nr) IXGBE_VFTDH(ring_nr) #define NM_IXGBE_RDT(ring_nr) IXGBE_VFRDT(ring_nr) +#define NM_IXGBE_TDWBAH(ring_nr) IXGBE_VFTDWBAH(ring_nr) +#define NM_IXGBE_TDWBAL(ring_nr) IXGBE_VFTDWBAL(ring_nr) +#ifdef NETMAP_LINUX_IXGBEVF_IXGBE_MACROS +#define NM_IXGBE_TX_DESC(_1, _2) IXGBE_TX_DESC_ADV(*(_1), _2) +#define NM_IXGBE_RX_DESC(_1, _2) IXGBE_RX_DESC_ADV(*(_1), _2) +#else #define NM_IXGBE_TX_DESC(_1, _2) IXGBEVF_TX_DESC(_1, _2) #define NM_IXGBE_RX_DESC(_1, _2) IXGBEVF_RX_DESC(_1, _2) +#endif /* NETMAP_LINUX_IXGBEVF_IXGBE_MACROS */ +#ifdef NETMAP_LINUX_IXGBEVF_PTR_ARRAY #define NM_IXGBE_TX_RING(a, r) ((a)->tx_ring[(r)]) #define NM_IXGBE_RX_RING(a, r) ((a)->rx_ring[(r)]) +#else +#define NM_IXGBE_TX_RING(a, r) (&(a)->tx_ring[(r)]) +#define NM_IXGBE_RX_RING(a, r) (&(a)->rx_ring[(r)]) +#endif /* NETMAP_LINUX_IXGBE_PTR_ARRAY */ #define NM_IXGBE_ADAPTER ixgbevf_adapter #define NM_IXGBE_RESETTING __IXGBEVF_RESETTING #define NM_IXGBE_DOWN(adapter) ixgbevf_down(adapter) @@ -164,18 +186,41 @@ static void ixgbe_netmap_intr(struct netmap_adapter *na, int onoff) { // TODO - RD(5, "per-queue irq disable not supported"); + nm_prlim(5, "per-queue irq disable not supported"); } static void ixgbe_netmap_configure_srrctl(struct NM_IXGBE_ADAPTER *adapter, struct NM_IXGBE_RING *rx_ring) { // TODO - D("not supported"); + nm_prerr("not supported"); } + +#ifdef NETMAP_LINUX_IXGBEVF_HAVE_NTA +#define NETMAP_LINUX_HAVE_NTA +#endif /* NETMAP_LINUX_IXGBEVF_HAVE_NTA */ + +#define NM_IXGBE_STATE(adapter) (&(adapter)->state) + #endif /* NM_IXGBE */ /**********************************************************************/ +struct netmap_ixgbe_head { +#ifndef NM_IXGBE_USE_TDH + dma_addr_t map; + u32* phead; +#endif /*! NM_IXGBE_USE_TDH */ +}; + +struct netmap_ixgbe_adapter { + struct netmap_hw_adapter up; +#ifndef NM_IXGBE_USE_TDH + struct dma_pool *pool; + struct netmap_ixgbe_head *heads; +#endif /*! NM_IXGBE_USE_TDH */ +}; + + /* * Register/unregister. We are already under netmap lock. * Only called on the first register or the last unregister. @@ -188,7 +233,7 @@ ixgbe_netmap_reg(struct netmap_adapter *na, int onoff) // adapter->netdev->trans_start = jiffies; // disable watchdog ? /* protect against other reinit */ - while (test_and_set_bit(NM_IXGBE_RESETTING, &adapter->state)) + while (test_and_set_bit(NM_IXGBE_RESETTING, NM_IXGBE_STATE(adapter))) usleep_range(1000, 2000); if (netif_running(adapter->netdev)) @@ -200,10 +245,10 @@ ixgbe_netmap_reg(struct netmap_adapter *na, int onoff) } else { nm_clear_native_flags(na); } - /* XXX SRIOV migth need another 2sec wait */ + /* XXX SRIOV might need another 2sec wait */ if (netif_running(adapter->netdev)) NM_IXGBE_UP(adapter); /* also enables intr */ - clear_bit(NM_IXGBE_RESETTING, &adapter->state); + clear_bit(NM_IXGBE_RESETTING, NM_IXGBE_STATE(adapter)); return (0); } @@ -229,12 +274,15 @@ ixgbe_netmap_txsync(struct netmap_kring *kring, int flags) struct netmap_adapter *na = kring->na; struct ifnet *ifp = na->ifp; struct netmap_ring *ring = kring->ring; +#ifndef NM_IXGBE_USE_TDH + struct netmap_ixgbe_adapter *ina = (struct netmap_ixgbe_adapter *)na; +#endif /* !NM_IXGBE_USE_TDH */ u_int ring_nr = kring->ring_id; u_int nm_i; /* index into the netmap ring */ u_int nic_i; /* index into the NIC ring */ - u_int n; u_int const lim = kring->nkr_num_slots - 1; u_int const head = kring->rhead; + u_int tosync; /* * interrupts on every tx packet are expensive so request * them every half ring, or where NS_REPORT is set @@ -244,7 +292,7 @@ ixgbe_netmap_txsync(struct netmap_kring *kring, int flags) /* device-specific */ struct NM_IXGBE_ADAPTER *adapter = netdev_priv(ifp); struct NM_IXGBE_RING *txr = NM_IXGBE_TX_RING(adapter, ring_nr); - int reclaim_tx; + int reclaim_tx, report; /* * First part: process new packets to send. @@ -270,10 +318,6 @@ ixgbe_netmap_txsync(struct netmap_kring *kring, int flags) * need to update the buffer's physical address in the NIC slot * even NS_BUF_CHANGED is not set (PNMB computes the addresses). * - * The netmap_reload_map() calls is especially expensive, - * even when (as in this case) the tag is 0, so do only - * when the buffer has actually changed. - * * If possible do not set the report/intr bit on all slots, * but only a few times per ring or when NS_REPORT is set. * @@ -288,34 +332,87 @@ ixgbe_netmap_txsync(struct netmap_kring *kring, int flags) nm_i = kring->nr_hwcur; if (nm_i != head) { /* we have new packets to send */ nic_i = netmap_idx_k2n(kring, nm_i); - for (n = 0; nm_i != head; n++) { + while (nm_i != head) { struct netmap_slot *slot = &ring->slot[nm_i]; u_int len = slot->len; uint64_t paddr; - void *addr = PNMB(na, slot, &paddr); + uint64_t offset = nm_get_offset(kring, slot); /* device-specific */ union ixgbe_adv_tx_desc *curr = NM_IXGBE_TX_DESC(txr, nic_i); - int flags = (slot->flags & NS_REPORT || - nic_i == 0 || nic_i == report_frequency) ? - IXGBE_TXD_CMD_RS : 0; - - NM_CHECK_ADDR_LEN(na, addr, len); - - if (slot->flags & NS_BUF_CHANGED) { - /* buffer has changed, reload map */ - // netmap_reload_map(pdev, DMA_TO_DEVICE, old_addr, addr); + unsigned int hw_flags = IXGBE_ADVTXD_DTYP_DATA | IXGBE_ADVTXD_DCMD_DEXT | + IXGBE_ADVTXD_DCMD_IFCS; + u_int totlen = len; + + PNMB(na, slot, &paddr); + NM_CHECK_ADDR_LEN_OFF(na, len, offset); + + report = slot->flags & NS_REPORT || + nic_i == 0 || + nic_i == report_frequency; + if (slot->flags & NS_MOREFRAG) { + /* There is some duplicated code here, but + * mixing everything up in the outer loop makes + * things less transparent, and it also adds + * unnecessary instructions in the fast path + */ + union ixgbe_adv_tx_desc *first = curr; + + first->read.buffer_addr = htole64(paddr + offset); + first->read.cmd_type_len = htole32(len | hw_flags); + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, len, NR_TX); + /* avoid setting the FCS flag in the + * descriptors after the first, for safety + */ + hw_flags &= ~IXGBE_ADVTXD_DCMD_IFCS; + for (;;) { + nm_i = nm_next(nm_i, lim); + nic_i = nm_next(nic_i, lim); + /* remember that we have to ask for a + * report each time we move past half a + * ring + */ + report |= nic_i == 0 || + nic_i == report_frequency; + if (nm_i == head) { + // XXX should we accept incomplete packets? + return EINVAL; + } + slot = &ring->slot[nm_i]; + len = slot->len; + PNMB(na, slot, &paddr); + offset = nm_get_offset(kring, slot); + NM_CHECK_ADDR_LEN_OFF(na, len, offset); + curr = NM_IXGBE_TX_DESC(txr, nic_i); + totlen += len; + if (!(slot->flags & NS_MOREFRAG)) + break; + curr->read.buffer_addr = htole64(paddr + offset); + curr->read.olinfo_status = 0; + curr->read.cmd_type_len = htole32(len | hw_flags); + + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, len, NR_TX); + } + first->read.olinfo_status = + htole32(totlen << IXGBE_ADVTXD_PAYLEN_SHIFT); + totlen = 0; } - if (!(slot->flags & NS_MOREFRAG)) - flags |= IXGBE_TXD_CMD_EOP; + + /* curr now always points to the last descriptor of a packet + * (which is also the first for single-slot packets) + * + * EOP and RS must be set only in this descriptor. + */ + hw_flags |= IXGBE_TXD_CMD_EOP | (report ? IXGBE_TXD_CMD_RS : 0); slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED | NS_MOREFRAG); /* Fill the slot in the NIC ring. */ - curr->read.buffer_addr = htole64(paddr); - curr->read.olinfo_status = htole32(len << IXGBE_ADVTXD_PAYLEN_SHIFT); - curr->read.cmd_type_len = htole32(len | flags | - IXGBE_ADVTXD_DTYP_DATA | IXGBE_ADVTXD_DCMD_DEXT | - IXGBE_ADVTXD_DCMD_IFCS); + curr->read.buffer_addr = htole64(paddr + offset); + curr->read.olinfo_status = htole32(totlen << IXGBE_ADVTXD_PAYLEN_SHIFT); + curr->read.cmd_type_len = htole32(len | hw_flags); + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, &paddr, len, NR_TX); nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); } @@ -328,6 +425,18 @@ ixgbe_netmap_txsync(struct netmap_kring *kring, int flags) /* * Second part: reclaim buffers for completed transmissions. + */ + tosync = nm_next(kring->nr_hwtail, lim); +#ifndef NM_IXGBE_USE_TDH + (void)reclaim_tx; + if ((flags & NAF_FORCE_RECLAIM) || nm_kr_txempty(kring)) { + nic_i = NM_ACCESS_ONCE(*ina->heads[ring_nr].phead); + nm_i = netmap_idx_n2k(kring, nic_i); + nm_prdis(5, "%s: h %d", kring->name, h); + kring->nr_hwtail = nm_prev(nm_i, lim); + } +#else /* NM_IXGBE_USE_TDH */ + /* * Because this is expensive (we read a NIC register etc.) * we only do it in specific cases (see below). */ @@ -353,7 +462,7 @@ ixgbe_netmap_txsync(struct netmap_kring *kring, int flags) nic_i = (nic_i < kring->nkr_num_slots / 4 || nic_i >= kring->nkr_num_slots*3/4) ? 0 : report_frequency; - reclaim_tx = txd[nic_i].wb.status & IXGBE_TXD_STAT_DD; // XXX cpu_to_le32 ? + reclaim_tx = le32toh(txd[nic_i].wb.status) & IXGBE_TXD_STAT_DD; } if (reclaim_tx) { /* @@ -367,12 +476,25 @@ ixgbe_netmap_txsync(struct netmap_kring *kring, int flags) * good way. */ nic_i = IXGBE_READ_REG(&adapter->hw, NM_IXGBE_TDH(ring_nr)); - if (nic_i >= kring->nkr_num_slots) { /* XXX can it happen ? */ - D("TDH wrap %d", nic_i); + if (unlikely(nic_i >= kring->nkr_num_slots)) { + nm_prerr("%s: TDH overflow (%d)", kring->name, nic_i); nic_i -= kring->nkr_num_slots; } + nm_i = netmap_idx_n2k(kring, nic_i); txr->next_to_clean = nic_i; - kring->nr_hwtail = nm_prev(netmap_idx_n2k(kring, nic_i), lim); + txr->next_to_use = txr->next_to_clean; + kring->nr_hwtail = nm_prev(nm_i, lim); + } +#endif /* NM_IXGBE_USE_TDH */ + /* sync all buffers that we are returning to userspace. + */ + for ( ; tosync != nm_i; tosync = nm_next(tosync, lim)) { + struct netmap_slot *slot = &ring->slot[tosync]; + uint64_t paddr; + (void)PNMB_O(kring, slot, &paddr); + + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, + &paddr, slot->len, NR_TX); } out: @@ -434,26 +556,50 @@ ixgbe_netmap_rxsync(struct netmap_kring *kring, int flags) * rxr->next_to_clean is set to 0 on a ring reinit */ if (netmap_no_pendintr || force_update) { - uint16_t slot_flags = kring->nkr_slot_flags; + u_int new_hwtail = (u_int)-1; nic_i = rxr->next_to_clean; nm_i = netmap_idx_n2k(kring, nic_i); for (n = 0; ; n++) { union ixgbe_adv_rx_desc *curr = NM_IXGBE_RX_DESC(rxr, nic_i); - uint32_t staterr = le32toh(curr->wb.upper.status_error); + uint32_t staterr; + u_int size = le16toh(curr->wb.upper.length); + uint64_t paddr; + struct netmap_slot *slot = &ring->slot[nm_i]; + int complete; - if ((staterr & IXGBE_RXD_STAT_DD) == 0) + if (!size) break; - ring->slot[nm_i].len = le16toh(curr->wb.upper.length); - ring->slot[nm_i].flags = (!(staterr & IXGBE_RXD_STAT_EOP) ? NS_MOREFRAG | - slot_flags:slot_flags); + + dma_rmb(); + + staterr = le32toh(curr->wb.upper.status_error); + + slot->len = size; + complete = staterr & IXGBE_RXD_STAT_EOP; + slot->flags = complete ? 0 : NS_MOREFRAG; + PNMB_O(kring, slot, &paddr); + netmap_sync_map_cpu(na, (bus_dma_tag_t) na->pdev, + &paddr, size, NR_RX); + nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); + + if (complete) + new_hwtail = nm_i; } if (n) { /* update the state variables */ rxr->next_to_clean = nic_i; - kring->nr_hwtail = nm_i; + rxr->next_to_use = rxr->next_to_clean; +#ifdef NETMAP_LINUX_HAVE_NTA + rxr->next_to_alloc = rxr->next_to_clean; +#endif /* NETMAP_LINUX_HAVE_NTA */ + if (new_hwtail != (u_int)-1) { + /* Update nr_hwtail only if we saw a complete + * packet in the previous loop. */ + kring->nr_hwtail = new_hwtail; + } } kring->nr_kflags &= ~NKR_PENDINTR; } @@ -473,33 +619,34 @@ ixgbe_netmap_rxsync(struct netmap_kring *kring, int flags) struct netmap_slot *slot = &ring->slot[nm_i]; uint64_t paddr; void *addr = PNMB(na, slot, &paddr); + uint64_t offset = nm_get_offset(kring, slot); union ixgbe_adv_rx_desc *curr = NM_IXGBE_RX_DESC(rxr, nic_i); if (addr == NETMAP_BUF_BASE(na)) /* bad buf */ goto ring_reset; if (slot->flags & NS_BUF_CHANGED) { - /* buffer has changed, reload map */ - // netmap_reload_map(pdev, DMA_TO_DEVICE, old_addr, addr); slot->flags &= ~NS_BUF_CHANGED; } + netmap_sync_map_dev(na, (bus_dma_tag_t) na->pdev, + &paddr, NETMAP_BUF_SIZE(na), NR_RX); + curr->wb.upper.length = 0; curr->wb.upper.status_error = 0; - curr->read.pkt_addr = htole64(paddr); + curr->read.pkt_addr = htole64(paddr + offset); nm_i = nm_next(nm_i, lim); nic_i = nm_next(nic_i, lim); } kring->nr_hwcur = head; - rxr->next_to_use = nic_i; // XXX not really used wmb(); /* * IMPORTANT: we must leave one free slot in the ring, * so move nic_i back by one unit */ nic_i = nm_prev(nic_i, lim); + rxr->next_to_use = nic_i; /* used for debug only */ IXGBE_WRITE_REG(&adapter->hw, NM_IXGBE_RDT(rxr->reg_idx), nic_i); } - return 0; ring_reset: @@ -511,16 +658,35 @@ ixgbe_netmap_rxsync(struct netmap_kring *kring, int flags) * if in netmap mode, attach the netmap buffers to the ring and return true. * Otherwise return false. */ -static int -ixgbe_netmap_configure_tx_ring(struct NM_IXGBE_ADAPTER *adapter, int ring_nr) +static u32 +ixgbe_netmap_configure_tx_ring(struct NM_IXGBE_ADAPTER *adapter, int ring_nr, u32 txdctl) { struct netmap_adapter *na = NA(adapter->netdev); struct netmap_slot *slot; - //int j; +#ifndef NM_IXGBE_USE_TDH + struct ixgbe_hw *hw = &adapter->hw; + struct netmap_ixgbe_adapter *ina = (struct netmap_ixgbe_adapter *)na; + u64 wba; + struct netmap_ixgbe_head *h; +#endif /* !NM_IXGBE_USE_TDH */ - slot = netmap_reset(na, NR_TX, ring_nr, 0); + slot = netmap_reset(na, NR_TX, ring_nr, 0); if (!slot) - return 0; // not in native netmap mode + return txdctl; // not in native netmap mode + +#ifndef NM_IXGBE_USE_TDH + /* we reset WTRESH (it must be 0 according to specs) */ + txdctl &= ~(0x7f << 16); + + wba = (u64)ina->heads[ring_nr].map; + IXGBE_WRITE_REG(hw, NM_IXGBE_TDWBAL(ring_nr), + (wba & DMA_BIT_MASK(32)) | IXGBE_TDWBAL_HEAD_WB_ENABLE); + IXGBE_WRITE_REG(hw, NM_IXGBE_TDWBAH(ring_nr), wba >> 32); + /* reset all heads */ + h = &ina->heads[ring_nr]; + *h->phead = 0; +#endif /* !NM_IXGBE_USE_TDH */ + #if 0 /* * on a generic card we should set the address in the slot. @@ -529,12 +695,14 @@ ixgbe_netmap_configure_tx_ring(struct NM_IXGBE_ADAPTER *adapter, int ring_nr) * loading the map. */ for (j = 0; j < na->num_tx_desc; j++) { - int sj = netmap_idx_n2k(&na->tx_rings[ring_nr], j); + int sj = netmap_idx_n2k(na->tx_rings[ring_nr], j); uint64_t paddr; void *addr = PNMB(na, slot + sj, &paddr); } #endif - return 1; + + /* the queue will be re-enabled by the caller */ + return txdctl; } static int @@ -560,34 +728,167 @@ ixgbe_netmap_configure_rx_ring(struct NM_IXGBE_ADAPTER *adapter, int ring_nr) struct netmap_slot *slot; int lim, i; struct NM_IXGBE_RING *ring = NM_IXGBE_RX_RING(adapter, ring_nr); + struct netmap_kring *kring; - slot = netmap_reset(na, NR_RX, ring_nr, 0); - /* same as in ixgbe_setup_transmit_ring() */ + slot = netmap_reset(na, NR_RX, ring_nr, 0); + /* same as in ixgbe_setup_transmit_ring() */ if (!slot) return 0; // not in native netmap mode - // XXX can we move it later ? + + kring = na->rx_rings[ring_nr]; + ixgbe_netmap_configure_srrctl(adapter, ring); - lim = na->num_rx_desc - 1 - nm_kr_rxspace(&na->rx_rings[ring_nr]); + lim = na->num_rx_desc - 1 - nm_kr_rxspace(na->rx_rings[ring_nr]); - for (i = 0; i < na->num_rx_desc; i++) { + for (i = 0; i <= lim; i++) { /* * Fill the map and set the buffer address in the NIC ring, * considering the offset between the netmap and NIC rings * (see comment in ixgbe_setup_transmit_ring() ). */ - int si = netmap_idx_n2k(&na->rx_rings[ring_nr], i); + int si = netmap_idx_n2k(kring, i); + union ixgbe_adv_rx_desc *curr = NM_IXGBE_RX_DESC(ring, i); uint64_t paddr; - PNMB(na, slot + si, &paddr); - // netmap_load_map(rxr->ptag, rxbuf->pmap, addr); + PNMB_O(kring, slot + si, &paddr); /* Update descriptor */ - NM_IXGBE_RX_DESC(ring, i)->read.pkt_addr = htole64(paddr); + curr->read.pkt_addr = htole64(paddr); + curr->wb.upper.length = 0; + curr->wb.upper.status_error = 0; } - IXGBE_WRITE_REG(&adapter->hw, NM_IXGBE_RDT(ring_nr), lim); + ring->next_to_use = lim; /* used for debug only */ + IXGBE_WRITE_REG(&adapter->hw, NM_IXGBE_RDT(ring->reg_idx), lim); return 1; } +#ifndef NM_IXGBE_USE_TDH +static void ixgbe_netmap_destroy_heads(struct netmap_adapter *); +static int +ixgbe_netmap_create_heads(struct netmap_adapter *na) +{ + struct netmap_ixgbe_adapter *ina = + (struct netmap_ixgbe_adapter *)na; + int i; + + // allocate head-writeback region + ina->pool = dma_pool_create("head-wb", + na->pdev, sizeof(u32), + L1_CACHE_BYTES, 0); + if (ina->pool == NULL) { + pr_err("netmap: failed to allocated head-wb pool"); + goto err; + } + + ina->heads = kmalloc(sizeof(struct netmap_ixgbe_head) * na->num_tx_rings, + GFP_KERNEL | __GFP_ZERO); + if (ina->heads == NULL) + goto err; + for (i = 0; i < na->num_tx_rings; i++) { + struct netmap_ixgbe_head *h = &ina->heads[i]; + + h->phead = dma_pool_alloc(ina->pool, GFP_KERNEL, &h->map); + if (h->phead == NULL) { + pr_err("netmap: failed to allocated head %d", i); + goto err; + } + *h->phead = 0; + nm_prdis("%s: phead %p *phead %x", na->tx_rings[i].name, h->phead, *h->phead); + } + return 0; + +err: + ixgbe_netmap_destroy_heads(na); + return ENOMEM; +} + +static void +ixgbe_netmap_destroy_heads(struct netmap_adapter *na) +{ + struct netmap_ixgbe_adapter *ina = + (struct netmap_ixgbe_adapter *)na; + + if (ina->heads != NULL) { + int i; + + for (i = 0; i < na->num_tx_rings; i++) { + struct netmap_ixgbe_head *h = &ina->heads[i]; + if (h->phead != NULL) + dma_pool_free(ina->pool, h->phead, h->map); + h->phead = NULL; + } + kfree(ina->heads); + ina->heads = NULL; + } + if (ina->pool != NULL) { + dma_pool_destroy(ina->pool); + ina->pool = NULL; + } +} +#endif /* !NM_IXGBE_USE_TDH */ + +static void +ixgbe_netmap_krings_delete(struct netmap_adapter *na) +{ +#ifndef NM_IXGBE_USE_TDH + ixgbe_netmap_destroy_heads(na); +#endif /*! NM_IXGBE_USE_TDH */ + netmap_hw_krings_delete(na); +} + +static int +ixgbe_netmap_krings_create(struct netmap_adapter *na) +{ + int ret; + + ret = netmap_hw_krings_create(na); + if (ret) + return ret; + +#ifndef NM_IXGBE_USE_TDH + ret = ixgbe_netmap_create_heads(na); + if (ret) { + netmap_hw_krings_delete(na); + return ret; + } +#endif /*! NM_IXGBE_USE_TDH */ + return 0; +} + +static int +ixgbe_netmap_config(struct netmap_adapter *na, struct nm_config_info *info) +{ + int ret = netmap_rings_config_get(na, info); + + if (ret) { + return ret; + } + + info->rx_buf_maxsize = NETMAP_BUF_SIZE(na); + + return 0; +} + +static int +ixgbe_netmap_bufcfg(struct netmap_kring *kring, uint64_t target) +{ + kring->buf_align = 0; + if (kring->tx == NR_TX) { + kring->hwbuf_len = target; + return 0; + } + + target >>= 10; + if (target < 1 || target > 16) + return EINVAL; + + kring->hwbuf_len = target << 10; + + return 0; +} + + +static void ixgbe_netmap_detach(struct NM_IXGBE_ADAPTER *adapter); /* * The attach routine, called near the end of ixgbe_attach(), * fills the parameters for netmap_attach() and calls it. @@ -604,15 +905,38 @@ ixgbe_netmap_attach(struct NM_IXGBE_ADAPTER *adapter) na.ifp = adapter->netdev; na.pdev = &adapter->pdev->dev; + na.na_flags = NAF_MOREFRAG | NAF_OFFSETS; na.num_tx_desc = NM_IXGBE_TX_RING(adapter, 0)->count; na.num_rx_desc = NM_IXGBE_RX_RING(adapter, 0)->count; + na.num_tx_rings = adapter->num_tx_queues; + na.num_rx_rings = adapter->num_rx_queues; + na.rx_buf_maxsize = 1500; /* will be overwritten by nm_config */ na.nm_txsync = ixgbe_netmap_txsync; na.nm_rxsync = ixgbe_netmap_rxsync; na.nm_register = ixgbe_netmap_reg; - na.num_tx_rings = adapter->num_tx_queues; - na.num_rx_rings = adapter->num_rx_queues; + na.nm_krings_create = ixgbe_netmap_krings_create; + na.nm_krings_delete = ixgbe_netmap_krings_delete; na.nm_intr = ixgbe_netmap_intr; - netmap_attach(&na); + na.nm_config = ixgbe_netmap_config; + na.nm_bufcfg = ixgbe_netmap_bufcfg; + + if (netmap_attach_ext(&na, sizeof(struct netmap_ixgbe_adapter), 1)) { + pr_err("netmap: failed to attach netmap adapter"); + return; + } +} + +static void +ixgbe_netmap_detach(struct NM_IXGBE_ADAPTER *adapter) +{ +#ifndef NM_IXGBE_USE_TDH + if (!NM_NA_VALID(adapter->netdev)) + return; + + ixgbe_netmap_destroy_heads(NA(adapter->netdev)); +#endif /*! NM_IXGBE_USE_TDH */ + + netmap_detach(adapter->netdev); } /* end of file */ diff --git a/LINUX/mlx5-config.sh_ b/LINUX/mlx5-config.sh_ new file mode 100755 index 000000000..94d1e095e --- /dev/null +++ b/LINUX/mlx5-config.sh_ @@ -0,0 +1,10 @@ +#!/bin/sh + +if [ -e mlx5/configure.mk.kernel ]; then + exit 0 +fi + +LINVER=$(@SRCDIR@/scripts/vers @LIN_VER@ -C) + +cd mlx5 +./configure --mlnx_en --kernel-version=$LINVER --kernel-sources=@KSRC@ -j$(nproc) diff --git a/LINUX/mlx5-prepare.sh b/LINUX/mlx5-prepare.sh new file mode 100644 index 000000000..96365d8ae --- /dev/null +++ b/LINUX/mlx5-prepare.sh @@ -0,0 +1,10 @@ +#!/bin/sh -x + +KSRC=$1 + +if [ -e mlx5/config.mk ]; then + exit 0 +fi + +cd mlx5 +scripts/mlnx_en_patch.sh -s $KSRC -j$(grep -c processor /proc/cpuinfo) diff --git a/LINUX/mlx5_netmap_linux.h b/LINUX/mlx5_netmap_linux.h new file mode 100644 index 000000000..415163531 --- /dev/null +++ b/LINUX/mlx5_netmap_linux.h @@ -0,0 +1,733 @@ +/* + * netmap support for Mellanox mlx5 Ethernet driver on Linux + * + * Copyright (C) 2015-2018 British Broadcasting Corporation. All rights reserved. + * + * Author: Stuart Grace, BBC Research & Development + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Some portions are: + * + * Copyright (C) 2012-2014 Matteo Landi, Luigi Rizzo. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Some portions are: + * + * Copyright (c) 2013-2015, Mellanox Technologies, Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __MLX5_NETMAP_LINUX_H__ +#define __MLX5_NETMAP_LINUX_H__ + +#include +#include +#include + +#ifdef NETMAP_MLX5_MAIN + +#define NM_MLX5E_ADAPTER mlx5e_priv + +/* This function is in en_rx.c but needed here to + * deal with compressed CQEs + */ +u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, + struct mlx5e_cq *cq, + int budget_rem); + +int mlx5e_netmap_reg(struct netmap_adapter *na, int onoff); +int mlx5e_netmap_txsync(struct netmap_kring *kring, int flags); +int mlx5e_netmap_rxsync(struct netmap_kring *kring, int flags); +int mlx5e_netmap_tx_flush(struct mlx5e_txqsq *sq); +int mlx5e_netmap_rx_flush(struct mlx5e_rq *rq); +int mlx5e_netmap_configure_tx_ring(struct NM_MLX5E_ADAPTER *adapter, int ring_nr); +int mlx5e_netmap_configure_rx_ring(struct mlx5e_rq *rq, int ring_nr); +int mlx5e_netmap_config(struct netmap_adapter *na, struct nm_config_info *info); +void mlx5e_netmap_attach(struct NM_MLX5E_ADAPTER *adapter); + +/* + * Register/unregister. We are already under netmap lock. + * Only called on the first register or the last unregister. + */ +int mlx5e_netmap_reg(struct netmap_adapter *na, int onoff) { + struct ifnet *ifp = na->ifp; + struct NM_MLX5E_ADAPTER *adapter = netdev_priv(ifp); + int err = 0; + int was_opened; + + nm_prinf("mlx5e switching %s native netmap mode", onoff ? "into" : "out of"); + + /* Should we check and wait for any reset in progress to complete? */ + mutex_lock(&adapter->state_lock); + was_opened = test_bit(MLX5E_STATE_OPENED, &adapter->state); + + if (was_opened) { + mlx5e_close_locked(adapter->netdev); + } + + /* enable or disable flags and callbacks in na and ifp */ + if (onoff) { + nm_set_native_flags(na); + } else { + nm_clear_native_flags(na); + } + + if (was_opened) + err = mlx5e_open_locked(adapter->netdev); + + if (err) + netdev_err(adapter->netdev, + "mlx5e_netmap_reg: mlx5e_open_locked returned err code %d\n", + err); + + mutex_unlock(&adapter->state_lock); + + return err; +} + +#define MLX5E_SQ_NOPS_ROOM MLX5_SEND_WQE_MAX_WQEBBS +#define MLX5E_SQ_STOP_ROOM (MLX5_SEND_WQE_MAX_WQEBBS +\ + MLX5E_SQ_NOPS_ROOM) + +/* + * Reconcile kernel and user view of the transmit ring. + * + * Userspace wants to send packets up to the one before ring->head, + * kernel knows kring->nr_hwcur is the first unsent packet. + * + * Here we push packets out (as many as possible), and possibly + * reclaim buffers from previously completed transmission. + * + * ring->tail is updated on return. + * ring->head is never used here. + * + * The caller (netmap) guarantees that there is only one instance + * running at any time. Any interference with other driver + * methods should be handled by the individual drivers. + */ +int mlx5e_netmap_txsync(struct netmap_kring *kring, int flags) { + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *ring = kring->ring; + u32 ring_nr = kring->ring_id; + u32 nm_i; /* index into the netmap ring */ + u32 n; + u32 const lim = kring->nkr_num_slots - 1; + u32 const head = kring->rhead; + + /* device-specific */ + struct NM_MLX5E_ADAPTER *priv = netdev_priv(ifp); + + struct mlx5e_txqsq *sq = priv->txq2sq[ring_nr]; + struct mlx5_wq_cyc *wq = &sq->wq; + struct mlx5e_cq *cq = &(sq->cq); + struct mlx5e_tx_wqe *wqe = NULL; + struct mlx5_cqe64 *cqe = NULL; + struct mlx5_wqe_ctrl_seg *cseg; + struct mlx5_wqe_eth_seg *eseg; + struct mlx5_wqe_data_seg *dseg; + u16 sqcc; + int cqe_found = 0; + + /* + * If we have packets to send (kring->nr_hwcur != ring->cur) + * iterate over the netmap ring, fetch buffer address and length + * and create a suitable WQE for each packet to send. + * + * Only the last WQE requests a CQE is created to report + * completion. + */ + + if (!netif_carrier_ok(ifp)) { + goto out; + } + + nm_i = kring->nr_hwcur; + + if (nm_i != head) { /* we have new packets to send */ + + /* nm_prinf("TX ring %u sending slots %u to %u", + * ring_nr, nm_i, nm_prev(head, lim)); + */ + + for (n = 0; nm_i != head; n++) { + if (unlikely(!mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, MLX5E_SQ_STOP_ROOM))) { + break; + } + + struct netmap_slot *slot = &ring->slot[nm_i]; + u_int len = slot->len; + uint64_t paddr; /* physical address for DMA */ + void *addr = PNMB(na, slot, &paddr); + + /* Code below based on mlx5e_sq_xmit() in en_tx.c */ + + u16 pi = sq->pc & wq->fbc.sz_m1; /* producer index */ + + u8 opcode = MLX5_OPCODE_SEND; + u16 ds_cnt; + u16 ihs; /* inline hdr size */ + u8 num_wqebbs = 0; + + wqe = mlx5_wq_cyc_get_wqe(wq, pi); + cseg = &wqe->ctrl; /* ctrl seg */ + eseg = &wqe->eth; /* ethernet seg */ + ds_cnt = sizeof(*wqe) / MLX5_SEND_WQE_DS; + + NM_CHECK_ADDR_LEN(na, addr, len); /* limit len to buf size */ + + slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED); + + memset(wqe, 0, sizeof(*wqe)); + + /* request checksum generation in hw */ + eseg->cs_flags = MLX5_ETH_WQE_L3_CSUM | MLX5_ETH_WQE_L4_CSUM; + + /* Use minimum inline header to minimise data copying */ + ihs = ETH_HLEN; + + if (unlikely(ihs > len)) + ihs = len; /* whole packet fits inline */ + + memcpy(eseg->inline_hdr.start, addr, ihs); + eseg->inline_hdr.sz = cpu_to_be16(ihs); + + ds_cnt += + DIV_ROUND_UP(ihs - sizeof(eseg->inline_hdr.start), MLX5_SEND_WQE_DS); + + dseg = (struct mlx5_wqe_data_seg *)cseg + ds_cnt; + + /* Put all rest of packet into a single data segment */ + /* excluding bytes in the inline header */ + if (likely(len > ihs)) { + + dseg->addr = cpu_to_be64(paddr + ihs); /* phys addr */ + dseg->lkey = sq->mkey_be; + dseg->byte_count = cpu_to_be32(len - ihs); + ds_cnt++; + } + + cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | opcode); + cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt); + + num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS); + sq->pc += num_wqebbs; + + /* Instead of storing pointer to a skb in sq->skb[pi], we use + * it to store info we will need at completion: + * - number of wqebbs in this wqe (shifted up by 24 bits) + * - slot number in the netmap kring that this wqe is sending + * (in bottom 24 bits) + */ + sq->db.wqe_info[pi].skb = (void *)(uintptr_t)(nm_i & 0x00FFFFFF); + sq->db.wqe_info[pi].num_wqebbs = num_wqebbs; + + mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, cseg); + + sq->stats->packets++; + + /* next netmap slot */ + nm_i = nm_next(nm_i, lim); + } /* next packet */ + + kring->nr_hwcur = nm_i; + } + + /* + * Second part: reclaim buffers from completed transmissions. + * We find these by looking for CQEs in the CQ. + * + * Code below based on mlx5e_poll_tx_cq() in en_tx.c + */ + + /* sq->cc must be updated only after mlx5_cqwq_update_db_record(), + * otherwise a cq overrun may occur */ + sqcc = sq->cc; + + cqe = mlx5_cqwq_get_cqe(&cq->wq); + + while (cqe) { + u16 wqe_counter; + bool last_wqe; + + cqe_found = 1; + mlx5_cqwq_pop(&cq->wq); + + /* this cqe could relate to many wqes */ + wqe_counter = be16_to_cpu(cqe->wqe_counter); + + do { + u16 ci = sqcc & sq->wq.fbc.sz_m1; + void *skb = sq->db.wqe_info[ci].skb; + u8 num_wqebbs = sq->db.wqe_info[ci].num_wqebbs; + u32 nm_i_done; + + last_wqe = (sqcc == wqe_counter); + + if (unlikely(!skb)) { /* nop */ + sq->stats->nop++; + sqcc++; + continue; + } + + /* unpack slot number from skb pointer */ + nm_i_done = (u32)((uintptr_t)skb & 0x00FFFFFF); + + sqcc += num_wqebbs; + kring->nr_hwtail = nm_prev(nm_i_done, lim); + + } while (!last_wqe); + + cqe = mlx5_cqwq_get_cqe(&cq->wq); + } + + if (cqe_found) { + + mlx5_cqwq_update_db_record(&cq->wq); + + /* ensure cq space is freed before enabling more cqes */ + wmb(); + sq->cc = sqcc; + } + + mlx5e_cq_arm(cq); /* allow interrupts from this CQ */ + +out: + return 0; +} + +/* + * Reconcile kernel and user view of the receive ring. + * Same as for the txsync, this routine must be efficient. + * The caller guarantees a single invocations, but races against + * the rest of the driver should be handled here. + * + * When called, userspace has released buffers up to ring->head + * (last one excluded). + * + * If (flags & NAF_FORCE_READ) also check for incoming packets irrespective + * of whether or not we received an interrupt. + */ +int mlx5e_netmap_rxsync(struct netmap_kring *kring, int flags) { + struct netmap_adapter *na = kring->na; + struct ifnet *ifp = na->ifp; + struct netmap_ring *ring = kring->ring; + u_int ring_nr = kring->ring_id; + u_int nm_i = 0; /* index into the netmap ring */ + u_int const lim = kring->nkr_num_slots - 1; + u_int const head = kring->rhead; + u_int const stop_i = nm_prev(head, lim); /* stop reclaiming here */ + uint16_t slot_flags = 0; + + /* device-specific */ + struct NM_MLX5E_ADAPTER *priv = netdev_priv(ifp); + struct mlx5e_rq *rq = &(priv->channels.c[ring_nr]->rq); + struct mlx5e_cq *cq = &(rq->cq); + struct mlx5_cqe64 *cqe = NULL; + int cqe_found = 0; +/* + if (unlikely(rq->rq_type == RQ_TYPE_STRIDE)) { + netdev_err(ifp, + "RQ type is STRIDING - this is not supported in netmap mode\n"); + return 0; + } +*/ + if (!netif_carrier_ok(ifp)) + return 0; + + if (unlikely(head > lim)) { + return netmap_ring_reinit(kring); + } + + rmb(); + + /* + * first part: reclaim buffers that userspace has released: + * (from kring->nr_hwcur to second last [*] slot before ring->head) + * and make the buffers available for reception. + * As usual nm_i is the index in the netmap ring. + * [*] IMPORTANT: we must leave one free slot in the ring + * to avoid ring empty/full confusion in userspace. + */ + nm_i = kring->nr_hwcur; + + if (nm_i != stop_i) { + struct mlx5_wq_cyc *wq = &rq->wqe.wq; + struct mlx5e_rx_wqe_cyc *wqe = mlx5_wq_cyc_get_wqe(wq, mlx5_wq_cyc_get_head(wq)); + struct netmap_slot *slot; + uint64_t paddr; + void *addr; + + while (nm_i != stop_i && !mlx5_wq_cyc_is_full(wq)) { + + slot = &ring->slot[nm_i]; + addr = PNMB(na, slot, &paddr); /* find phys address */ + + if (unlikely(addr == NETMAP_BUF_BASE(na))) { /* bad buf */ + netdev_warn(ifp, "Resetting RX ring %u in mlx5e_netmap_rxsync\n", + ring_nr); + goto ring_reset; + } + + if (slot->flags & NS_BUF_CHANGED) { + slot->flags &= ~NS_BUF_CHANGED; + } + + wqe = mlx5_wq_cyc_get_wqe(wq, mlx5_wq_cyc_get_head(wq)); + wqe->data->addr = cpu_to_be64(paddr); + + mlx5_wq_cyc_push(wq); + + nm_i = nm_next(nm_i, lim); + } + + kring->nr_hwcur = nm_i; + + /* ensure wqes are visible to device before updating doorbell record */ + wmb(); + mlx5_wq_cyc_update_db_record(wq); + } + + /* + * Second part: import newly received packets. + * We are told about received packets by CQEs in the CQ. + * + * nm_i is the index of the next free slot in the netmap ring: + */ + nm_i = kring->nr_hwtail; + + cqe = mlx5_cqwq_get_cqe(&cq->wq); + + while (cqe) { + struct mlx5e_rx_wqe_cyc *wqe; + u16 bytes_recv = 0; + __be16 wqe_id_be; + u16 wqe_counter; + + cqe_found = 1; + if (mlx5_get_cqe_format(cqe) == MLX5_COMPRESSED) + mlx5e_decompress_cqes_start(rq, &rq->cq, 1024); + + mlx5_cqwq_pop(&cq->wq); + + wqe_id_be = cqe->wqe_counter; + wqe_counter = be16_to_cpu(wqe_id_be); + wqe = mlx5_wq_cyc_get_wqe( &rq->wqe.wq, wqe_counter); + bytes_recv = be32_to_cpu(cqe->byte_cnt); + + if (unlikely((cqe->op_own >> 4) != MLX5_CQE_RESP_SEND)) { + rq->stats->wqe_err++; + netdev_warn(ifp, "Bad response found in CQE for RQ %u\n", ring_nr); + goto wq_cyc_pop; + } + + rq->stats->packets++; + if (cqe->hds_ip_ext & CQE_L4_OK) + rq->stats->csum_unnecessary++; + + /* could analyse checksums more thoroughly using flags in + * l4_hdr_type_etc that us which checksums are applicable + */ + /* Following is useful during debugging: + * printk(KERN_ERR "** Received %u bytes for ring %u slot %u with + * L2CSUM %s, L3CSUM %s, L4CSUM %s\n", + * be32_to_cpu(cqe->byte_cnt), ring_nr, nm_i, + * (cqe->hds_ip_ext & CQE_L2_OK)? "good" : "*BAD*", + * (cqe->hds_ip_ext & CQE_L3_OK)? "good" : "bad or not IP", + * (cqe->hds_ip_ext & CQE_L4_OK)? "good" : "bad or not TCP/UDP"); + */ + + ring->slot[nm_i].len = bytes_recv; + ring->slot[nm_i].flags = slot_flags; + nm_i = nm_next(nm_i, lim); + + wq_cyc_pop: + cqe = mlx5_cqwq_get_cqe(&cq->wq); + mlx5_wq_cyc_pop(&rq->wqe.wq); + } + + if (cqe_found) { + kring->nr_hwtail = nm_i; + mlx5_cqwq_update_db_record(&cq->wq); + + /* ensure cq space is freed before enabling more cqes */ + wmb(); + + /* update the kring state */ + kring->nr_kflags &= ~NKR_PENDINTR; + } + + mlx5e_cq_arm(cq); /* allow interrupts from this CQ */ + + return 0; + +ring_reset: + return netmap_ring_reinit(kring); +} + +/* + * Acknowledge and clear all CQEs when TX queue is closing down + */ +int mlx5e_netmap_tx_flush(struct mlx5e_txqsq *sq) { + struct mlx5e_cq *cq = &(sq->cq); + struct mlx5_cqe64 *cqe; + u16 sqcc; + + rmb(); + + /* sq->cc must be updated only after mlx5_cqwq_update_db_record(), + * otherwise a cq overrun may occur */ + sqcc = sq->cc; + + /* Any completed jobs in the CQ? */ + cqe = mlx5_cqwq_get_cqe(&cq->wq); + + while (cqe) { + u16 wqe_counter; + bool last_wqe; + + mlx5_cqwq_pop(&cq->wq); + + /* this cqe could relate to many wqes */ + wqe_counter = be16_to_cpu(cqe->wqe_counter); + + do { + u16 ci = sqcc & sq->wq.fbc.sz_m1; + void *skb = sq->db.wqe_info[ci].skb; + u8 num_wqebbs = sq->db.wqe_info[ci].num_wqebbs; + + last_wqe = (sqcc == wqe_counter); + + if (unlikely(!skb)) { /* nop */ + sq->stats->nop++; + sqcc++; + continue; + } + + sqcc += num_wqebbs; + + } while (!last_wqe); + + cqe = mlx5_cqwq_get_cqe(&cq->wq); + } + + mlx5_cqwq_update_db_record(&cq->wq); + + /* ensure cq space is freed before enabling more cqes */ + wmb(); + sq->cc = sqcc; + + return 0; +} + +/* + * Acknowledge and clear all CQEs when RX queue is closing down + */ +int mlx5e_netmap_rx_flush(struct mlx5e_rq *rq) { + struct mlx5e_cq *cq = &(rq->cq); + struct mlx5_cqe64 *cqe; + + rmb(); + + cqe = mlx5_cqwq_get_cqe(&cq->wq); + + while (cqe) { + struct mlx5e_rx_wqe_cyc *wqe; + __be16 wqe_id_be; + u16 wqe_counter; + + if (mlx5_get_cqe_format(cqe) == MLX5_COMPRESSED) + mlx5e_decompress_cqes_start(rq, &rq->cq, 1024); + + mlx5_cqwq_pop(&cq->wq); + + wqe_id_be = cqe->wqe_counter; + wqe_counter = be16_to_cpu(wqe_id_be); + wqe = mlx5_wq_cyc_get_wqe(&rq->wqe.wq, wqe_counter); + + cqe = mlx5_cqwq_get_cqe(&cq->wq); + mlx5_wq_cyc_pop(&rq->wqe.wq); + } + + mlx5_cqwq_update_db_record(&cq->wq); + + /* ensure cq space is freed before enabling more cqes */ + wmb(); + + mlx5e_cq_arm(cq); /* allow interrupts from this CQ */ + + return 0; +} + +/* + * if in netmap mode, attach the netmap buffers to the ring and return true. + * Otherwise return false. + */ +int mlx5e_netmap_configure_tx_ring(struct NM_MLX5E_ADAPTER *adapter, + int ring_nr) { + struct netmap_adapter *na = NA(adapter->netdev); + struct netmap_slot *slot; + + slot = netmap_reset(na, NR_TX, ring_nr, 0); + if (!slot) + return 0; /* not in native netmap mode */ + + /* + * On some cards we would set up the slot addresses now. + * But on mlx5e, the address will be written to the WQ when + * each packet arrives in mlx5e_netmap_txsync + */ + + return 1; +} + +int mlx5e_netmap_configure_rx_ring(struct mlx5e_rq *rq, int ring_nr) { + /* + * In netmap mode, we must preserve the buffers made + * available to userspace before the if_init() + * (this is true by default on the TX side, because + * init makes all buffers available to userspace). + */ + struct netmap_adapter *na = NA(rq->netdev); + struct netmap_slot *slot; + int lim; /* number of WQEs to prepare */ + int count = 0; + + struct mlx5_wq_cyc *wq = &rq->wqe.wq; + + slot = netmap_reset(na, NR_RX, ring_nr, 0); + if (!slot) + return 0; /* not in native netmap mode */ + + lim = na->num_rx_desc - 1 - nm_kr_rxspace(na->rx_rings[ring_nr]); + + while (!mlx5_wq_cyc_is_full(wq) && (count < lim)) { + + struct mlx5e_rx_wqe_cyc *wqe = mlx5_wq_cyc_get_wqe(wq, mlx5_wq_cyc_get_head(wq)); + + uint64_t paddr; + PNMB(na, slot + count, &paddr); + + wqe->data->addr = cpu_to_be64(paddr); + + mlx5_wq_cyc_push(wq); + count++; + } + + nm_prinf("populated %d WQEs in ring %d", count, ring_nr); + + /* tell netmap how many buffers we have prepared */ + na->rx_rings[ring_nr]->nr_hwcur = count; + + /* ensure wqes are visible to device before updating doorbell record */ + wmb(); + mlx5_wq_cyc_update_db_record(wq); + + return 1; +} + +int mlx5e_netmap_config(struct netmap_adapter *na, struct nm_config_info *info) { + int ret = netmap_rings_config_get(na, info); + + if (ret) { + return ret; + } + + info->rx_buf_maxsize = NETMAP_BUF_SIZE(na); + + return 0; +} + +/* + * The attach routine, called at the end of mlx5e_create_netdev(), + * fills the parameters for netmap_attach() and calls it. + * It cannot fail, in the worst case (such as no memory) + * netmap mode will be disabled and the driver will only + * operate in standard mode. + */ +void mlx5e_netmap_attach(struct NM_MLX5E_ADAPTER *adapter) { + struct netmap_adapter na; + bzero(&na, sizeof(na)); + + na.ifp = adapter->netdev; + na.pdev = &adapter->mdev->pdev->dev; + na.num_tx_desc = (1 << adapter->channels.params.log_sq_size); + na.num_rx_desc = (1 << adapter->channels.params.log_rq_mtu_frames); + na.nm_txsync = mlx5e_netmap_txsync; + na.nm_rxsync = mlx5e_netmap_rxsync; + na.nm_register = mlx5e_netmap_reg; + na.nm_config = mlx5e_netmap_config; + + /* each channel has 1 rx ring and a tx for each tc */ + na.num_tx_rings = adapter->channels.params.num_channels * adapter->channels.params.mqprio.num_tc; + na.num_rx_rings = adapter->channels.params.num_channels; + na.rx_buf_maxsize = 1500; /* will be overwritten by nm_config */ + netmap_attach(&na); +} + +#endif /* NETMAP_MLX5_MAIN */ + +#endif /* __MLX5_NETMAP_LINUX_H__ */ + +/* end of file */ diff --git a/LINUX/netmap.mak.in b/LINUX/netmap.mak.in index d8f31013a..136927074 100644 --- a/LINUX/netmap.mak.in +++ b/LINUX/netmap.mak.in @@ -9,14 +9,18 @@ MODNAME:=@MODNAME@ SUBSYS:=@SUBSYS@ SRCDIR:=@SRCDIR@ BUILDDIR:=@BUILDDIR@ +DEBUG:=@DEBUG@ # The following commands are needed to build the modules as out-of-tree, # in fact the kernel sources path must be specified. +# subsystem flags +SUBSYS_FLAGS = $(foreach s,$(SUBSYS),-DCONFIG_NETMAP_$(shell echo $s|tr a-z- A-Z_)) # Additional compile flags (e.g. header location) EXTRA_CFLAGS := -I$(BUILDDIR) -I$(SRCDIR) -I$(SRCDIR)/../sys -I$(SRCDIR)/../sys/dev -DCONFIG_NETMAP -EXTRA_CFLAGS += -Wno-unused-but-set-variable -EXTRA_CFLAGS += $(foreach s,$(SUBSYS),-DCONFIG_NETMAP_$(shell echo $s|tr a-z- A-Z_)) +EXTRA_CFLAGS += $(addprefix -Wno-,@REC_DISABLED_WARNINGS@) +EXTRA_CFLAGS += $(if $(DEBUG),-g) +EXTRA_CFLAGS += $(SUBSYS_FLAGS) # We use KSRC for the kernel configuration and sources. @@ -27,10 +31,10 @@ KOPTS = @KOPTS@ MODPATH = @MODPATH@ LIN_VER = @LIN_VER@ MOD_LIST := @MOD_LIST@ -PATCHES = @PATCHES@ S_DRIVERS = @S_DRIVERS@ E_DRIVERS = @E_DRIVERS@ DRVSUFFIX = @DRVSUFFIX@ +UTILS = @UTILS@ ifeq (,$(DRVSUFFIX)) else @@ -42,13 +46,14 @@ DRIVERS = $(shell [ -n "$(S_DRIVERS)" ] && ls -dAp $(S_DRIVERS) 2> /dev/null) # external drivers after copy and patch DRIVERS_EXT = $(shell [ -n "$(E_DRIVERS)" ] && ls -dAp $(E_DRIVERS) 2> /dev/null) -COMMON_OPTS=-C $(KSRC) M=$(BUILDDIR) EXTRA_CFLAGS='$(EXTRA_CFLAGS)' $(KOPTS) modules +COMMON_OPTS=-C $(KSRC) M=$(BUILDDIR) ccflags-y='$(EXTRA_CFLAGS)' $(KOPTS) modules define common_driver get-$(1): $($(1)@fetch) $($(1)@src) - $(foreach p,$($(1)@patch),patch --posix --quiet --force -p1 < $(p);) + $(foreach p,$($(1)@patch),patch --quiet --force -p1 < $(p);) + $($(1)@prepare) $(if $($(1)@build),,$(if $(filter-out %.c,$(1)),mv $(1)/Makefile $(1)/orig.mak || mv $(1)/Kbuild $(1)/orig.mak; cp drv-subdir.mak $(1)/Makefile,)) touch get-$(1) distclean-$(1): @@ -58,8 +63,7 @@ endef define external_driver build-$(1): get-$(1) netmap.ko - if [ -d $(1) ] && [ -e Module.symvers ]; then cp Module.symvers $(1); fi - +$($(1)@build) + +KBUILD_EXTRA_SYMBOLS=@BUILDDIR@/Module.symvers; export KBUILD_EXTRA_SYMBOLS; $($(1)@build) clean-$(1): -$($(1)@clean) install-$(1): install-netmap @@ -69,11 +73,12 @@ endef .PHONY: $(foreach d,$(E_DRIVERS),build-$(d) clean-$(1) install-$(1)) netmap.ko -all: $(S_DRIVERS:%=get-%) netmap.ko $(E_DRIVERS:%=build-%) apps +all: $(S_DRIVERS:%=get-%) netmap.ko $(E_DRIVERS:%=build-%) libnetmap apps utils netmap.ko: $(MAKE) $(COMMON_OPTS) CONFIG_NETMAP=m $(MOD_LIST) O_DRIVERS="$(patsubst %.c,%.o,$(filter-out $(DRIVERS_EXT),$(DRIVERS)))" NETMAP_DRIVER_SUFFIX=$(DRVSUFFIX) +-include extdrv-versions.mak -include default-config.mak -include config.mak -include drivers.mak @@ -81,8 +86,8 @@ netmap.ko: $(foreach d,$(S_DRIVERS),$(eval $(call common_driver,$(d)))) $(foreach d,$(E_DRIVERS),$(eval $(call external_driver,$(d)))) -.PHONY: install install-netmap install-apps install-headers install-docs -install: install-netmap $(E_DRIVERS:%=install-%) install-apps install-headers install-docs +.PHONY: install install-netmap install-apps install-headers install-docs libnetmap +install: install-netmap $(E_DRIVERS:%=install-%) install-apps install-headers install-docs install-libnetmap install-netmap: $(MAKE) -C $(KSRC) M=$(BUILDDIR) CONFIG_NETMAP=m $(MOD_LIST) \ @@ -92,7 +97,7 @@ install-netmap: $(if $(MODPATH),INSTALL_MOD_PATH=$(MODPATH)) \ modules_install -clean: $(E_DRIVERS:%=clean-%) clean-apps +clean: $(E_DRIVERS:%=clean-%) clean-apps clean-utils clean-libnetmap -@ $(MAKE) -C $(KSRC) M=$(BUILDDIR) clean 2> /dev/null APPS_LIST=@APPS_LIST@ @@ -110,19 +115,51 @@ clean-apps: $(APPS_LIST:%=clean-app-%) .PHONY: apps $(APPS_BUILD) install-apps $(APPS_INSTALL) define apps_actions -build-app-$(1): - +$(MAKE) -C build-apps/$(1) SRCDIR=$(SRCDIR)/.. CC="$(APPS_CC)" LD="$(APPS_LD)" +build-app-$(1): libnetmap + +$(MAKE) -C build-apps/$(1) SRCDIR=$(SRCDIR)/.. BUILDDIR=$(BUILDDIR) CC="$(APPS_CC)" LD="$(APPS_LD)" install-app-$(1): - $(MAKE) -C build-apps/$(1) install SRCDIR=$(SRCDIR)/.. DESTDIR="$(abspath $(DESTDIR))" PREFIX="$(PREFIX)" + $(MAKE) -C build-apps/$(1) install SRCDIR=$(SRCDIR)/.. BUILDDIR=$(BUILDDIR) DESTDIR="$(abspath $(DESTDIR))" PREFIX="$(PREFIX)" clean-app-$(1): - $(MAKE) -C build-apps/$(1) clean SRCDIR=$(SRCDIR)/.. + $(MAKE) -C build-apps/$(1) clean SRCDIR=$(SRCDIR)/.. BUILDDIR=$(BUILDDIR) endef $(foreach a,$(APPS_LIST),$(eval $(call apps_actions,$(a)))) +%: - @echo $($*) + @echo '$($*)' + +libnetmap: + +$(MAKE) -C build-libnetmap SRCDIR=$(SRCDIR)/.. BUILDDIR=$(BUILDDIR) CC="$(APPS_CC)" LD="$(APPS_LD)" + +clean-libnetmap: + +$(MAKE) -C build-libnetmap clean SRCDIR=$(SRCDIR)/.. BUILDDIR=$(BUILDDIR) + +install-libnetmap: + +$(MAKE) -C build-libnetmap install SRCDIR=$(SRCDIR)/.. BUILDDIR=$(BUILDDIR) DESTDIR="$(abspath $(DESTDIR))" PREFIX="$(PREFIX)" + +ifeq (,$(UTILS)) +utils: +install-utils: +clean-utils: +intest: +unitest: +else +.PHONY: utils +utils: libnetmap + +$(MAKE) -C build-utils SRCDIR=$(SRCDIR)/.. BUILDDIR=$(BUILDDIR) CC="$(APPS_CC)" LD="$(APPS_LD)" SUBSYS_FLAGS="$(SUBSYS_FLAGS)" + +install-utils: + $(MAKE) -C build-utils install SRCDIR=$(SRCDIR)/.. BUILDDIR=$(BUILDDIR) DESTDIR="$(abspath $(DESTDIR))" PREFIX="$(PREFIX)" + +clean-utils: + $(MAKE) -C build-utils clean SRCDIR=$(SRCDIR)/.. BUILDDIR=$(BUILDDIR) + +intest: utils + PATH=$(BUILDDIR)/build-utils:$$PATH $(SRCDIR)/../utils/randomized_tests +unitest: utils + build-utils/ctrl-api-test +endif INCLUDE_PREFIX := $(if $(filter-out /,$(PREFIX)),$(PREFIX),/usr) @@ -130,18 +167,29 @@ install-headers: install -m 0644 -D $(SRCDIR)/../sys/net/netmap.h $(DESTDIR)/$(INCLUDE_PREFIX)/include/net/netmap.h install -m 0644 -D $(SRCDIR)/../sys/net/netmap_user.h $(DESTDIR)/$(INCLUDE_PREFIX)/include/net/netmap_user.h install -m 0644 -D $(SRCDIR)/../sys/net/netmap_virt.h $(DESTDIR)/$(INCLUDE_PREFIX)/include/net/netmap_virt.h + install -m 0644 -D $(SRCDIR)/../sys/net/netmap_legacy.h $(DESTDIR)/$(INCLUDE_PREFIX)/include/net/netmap_legacy.h + install -m 0644 -D $(SRCDIR)/../libnetmap/libnetmap.h $(DESTDIR)/$(INCLUDE_PREFIX)/include/libnetmap.h MAN_PREFIX := $(INCLUDE_PREFIX) install-docs: install -D -m 644 $(SRCDIR)/../share/man/man4/netmap.4 $(DESTDIR)/$(MAN_PREFIX)/share/man/man4/netmap.4 + install -D -m 644 $(SRCDIR)/../share/man/man4/vale.4 $(DESTDIR)/$(MAN_PREFIX)/share/man/man4/vale.4 + install -D -m 644 $(SRCDIR)/../share/man/man4/ptnet.4 $(DESTDIR)/$(MAN_PREFIX)/share/man/man4/ptnet.4 distclean: clean $(S_DRIVERS:%=distclean-%) rm -f config.status config.log netmap_linux_config.h \ - patches drivers.mak Kbuild netmap.mak default-config.mak + patches drivers.mak Kbuild netmap.mak default-config.mak \ + extdrv-versions.mak intel-fix.sh rm -rf netmap-tmpdir rm -f *.orig *.rej if [ -L GNUmakefile ]; then rm GNUmakefile; fi if [ -L drv-subdir.mak ]; then rm drv-subdir.mak; fi if [ -L read-vars.mak ]; then rm read-vars.mak; fi + if [ -L tests ]; then rm tests; fi rm -rf build-apps + rm -rf build-utils + rm -rf build-libnetmap + +format: + clang-format -i -style=file $(shell git ls-files "utils/*.[ch]" "apps/*.[ch]" "extra/*.[ch]" "LINUX/*.[ch]" "WINDOWS/*.[ch]" "sys/*.[ch]") diff --git a/LINUX/netmap_linux.c b/LINUX/netmap_linux.c index f737e483b..a5ed20633 100644 --- a/LINUX/netmap_linux.c +++ b/LINUX/netmap_linux.c @@ -26,29 +26,83 @@ #include "bsd_glue.h" #include /* fget(int fd) */ +#include #include #include #include #include +#include +#include #include #include +#include #include #include +#include +#ifdef NETMAP_LINUX_HAVE_SCHED_MM +#include +#endif /* NETMAP_LINUX_HAVE_SCHED_MM */ #include "netmap_linux_config.h" +#ifdef NETMAP_LINUX_HAVE_SKB_VLAN_UNTAG +#ifndef NETMAP_LINUX_HAVE_ETH_TYPE_VLAN +static inline bool eth_type_vlan(__be16 ethertype) +{ + return ethertype == htons(ETH_P_8021Q) || + ethertype == htons(ETH_P_8021AD); +} +#endif /* NETMAP_LINUX_HAVE_ETH_TYPE_VLAN */ + +#ifndef NETMAP_LINUX_HAVE_SKB_VLAN_TAG_PRESENT +#define skb_vlan_tag_present(__skb) vlan_tx_tag_present(__skb) +#endif /* NETMAP_LINUX_HAVE_SKB_VLAN_TAG_PRESENT */ + +#ifndef NETMAP_LINUX_HAVE_VLAN_HWACCEL_PUSH_INSIDE +static inline struct sk_buff *__vlan_hwaccel_push_inside(struct sk_buff *skb) +{ + skb = __vlan_put_tag(skb, skb->vlan_proto, + vlan_tx_tag_get(skb)); + if (likely(skb)) + skb->vlan_tci = 0; + return skb; +} +#endif /* NETMAP_LINUX_HAVE_VLAN_HWACCESS_PUSH_INSIDE */ +#endif /* NETMAP_LINUX_HAVE_SKB_VLAN_UNTAG */ + +#ifdef NETMAP_LINUX_HAVE_SOCKADDR_UNSIZED +#define NM_SOCKADDR sockaddr_unsized +#else /* !NETMAP_LINUX_HAVE_SOCKADDR_UNSIZED */ +#define NM_SOCKADDR sockaddr +#endif /* NETMAP_LINUX_HAVE_SOCKADDR_UNSIZED */ + void * nm_os_malloc(size_t size) { - return kmalloc(size, GFP_ATOMIC | __GFP_ZERO); + void *rv = kmalloc(size, GFP_ATOMIC | __GFP_ZERO); + if (IS_ERR(rv)) + return NULL; + return rv; +} + +void * +nm_os_vmalloc(size_t size) +{ + void *rv = vmalloc(size); + if (IS_ERR(rv)) + return NULL; + return rv; } void * nm_os_realloc(void *addr, size_t new_size, size_t old_size) { + void *rv; (void)old_size; - - return krealloc(addr, new_size, GFP_ATOMIC | __GFP_ZERO); + rv = krealloc(addr, new_size, GFP_ATOMIC | __GFP_ZERO); + if (IS_ERR(rv)) + return NULL; + return rv; } void @@ -57,9 +111,15 @@ nm_os_free(void *addr){ } void -nm_os_selinfo_init(NM_SELINFO_T *si) +nm_os_vfree(void *addr){ + vfree(addr); +} + +int +nm_os_selinfo_init(NM_SELINFO_T *si, const char *name) { init_waitqueue_head(si); + return 0; } void @@ -143,6 +203,171 @@ nm_os_ifnet_fini(void) } } +unsigned +nm_os_ifnet_mtu(struct ifnet *ifp) +{ + return ifp->mtu; +} + +#ifdef WITH_EXTMEM +struct nm_os_extmem { + struct page **pages; + int nr_pages; + int mapped; +}; + +void +nm_os_extmem_delete(struct nm_os_extmem *e) +{ + int i; + for (i = 0; i < e->nr_pages; i++) { + if (i < e->mapped) + kunmap(e->pages[i]); +#ifdef NETMAP_LINUX_HAVE_PIN_PAGES + unpin_user_page(e->pages[i]); +#else + put_page(e->pages[i]); +#endif + } + if (e->pages) + nm_os_vfree(e->pages); + nm_os_free(e); +} + +char * +nm_os_extmem_nextpage(struct nm_os_extmem *e) +{ + if (e->mapped >= e->nr_pages) + return NULL; + return kmap(e->pages[e->mapped++]); +} + +int +nm_os_extmem_isequal(struct nm_os_extmem *e1, struct nm_os_extmem *e2) +{ + int i; + + if (e1->nr_pages != e2->nr_pages) + return 0; + + for (i = 0; i < e1->nr_pages; i++) + if (e1->pages[i] != e2->pages[i]) + return 0; + + return 1; +} + +int +nm_os_extmem_nr_pages(struct nm_os_extmem *e) +{ + return e->nr_pages; +} + + +struct nm_os_extmem * +nm_os_extmem_create(unsigned long p, struct nmreq_pools_info *pi, int *perror) +{ +#ifndef FOLL_POPULATE +#define FOLL_POPULATE 0 +#endif /* FOLL_POPULATE */ +#ifndef FOLL_LONGTERM +#define FOLL_LONGTERM 0 +#endif /* FOLL_POPULATE */ + unsigned long end, start; + int nr_pages, res; + struct nm_os_extmem *e = NULL; + int err; + struct page **pages; + + end = (p + pi->nr_memsize + PAGE_SIZE - 1) >> PAGE_SHIFT; + start = p >> PAGE_SHIFT; + nr_pages = end - start; + + e = nm_os_malloc(sizeof(*e)); + if (e == NULL) { + nm_prerr("failed to allocate os_extmem"); + err = ENOMEM; + goto out; + } + + pages = nm_os_vmalloc(nr_pages * sizeof(*pages)); + if (pages == NULL) { + nm_prerr("failed to allocate pages array (nr_pages %d)", nr_pages); + err = ENOMEM; + goto out; + } + + e->pages = pages; + +#ifdef NETMAP_LINUX_HAVE_PIN_PAGES + res = pin_user_pages_unlocked( + p, + nr_pages, + pages, + FOLL_WRITE | FOLL_POPULATE | FOLL_LONGTERM); +#elif defined(NETMAP_LINUX_HAVE_GUP_4ARGS) + res = get_user_pages_unlocked( + p, + nr_pages, + pages, + FOLL_WRITE | FOLL_GET | FOLL_SPLIT | FOLL_POPULATE); // XXX check other flags +#elif defined(NETMAP_LINUX_HAVE_GUP_5ARGS) + res = get_user_pages_unlocked( + p, + nr_pages, + 1, /* write */ + 0, /* don't force */ + pages); +#elif defined(NETMAP_LINUX_HAVE_GUP_6ARGS) + res = get_user_pages_unlocked( + current, + current->mm, + p, + nr_pages, + pages, + FOLL_WRITE | FOLL_GET | FOLL_SPLIT | FOLL_POPULATE); // XXX check other flags +#elif defined(NETMAP_LINUX_HAVE_GUP_7ARGS) + res = get_user_pages_unlocked( + current, + current->mm, + p, + nr_pages, + 1, /* write */ + 0, /* don't force */ + pages); +#else + down_read(¤t->mm->mmap_sem); + res = get_user_pages( + current, + current->mm, + p, + nr_pages, + 1, /* write */ + 0, /* don't force */ + pages, + NULL); + up_read(¤t->mm->mmap_sem); +#endif /* NETMAP_LINUX_GUP */ + + e->nr_pages = res; + + if (res < nr_pages) { + nm_prerr("failed to get user pages: res %d nr_pages %d", res, nr_pages); + err = (res < 0) ? -res : EFAULT; + goto out; + } + + return e; + +out: + if (e) + nm_os_extmem_delete(e); + if (perror) + *perror = err; + return NULL; +} +#endif /* WITH_EXTMEM */ + #ifdef NETMAP_LINUX_HAVE_IOMMU #include @@ -156,13 +381,16 @@ int nm_iommu_group_id(struct device *dev) int id; if (!dev) - return 0; + return -1; grp = iommu_group_get(dev); if (!grp) - return 0; + return -1; id = iommu_group_id(grp); + + iommu_group_put(grp); + return id; } #else /* ! HAVE_IOMMU */ @@ -195,7 +423,7 @@ nm_os_csum_ipv4(struct nm_iphdr *iph) } /* Compute and insert a TCP/UDP checksum over IPv4: 'iph' points to the IPv4 - * header, 'data' points to the TCP/UDP header, 'datalen' is the lenght of + * header, 'data' points to the TCP/UDP header, 'datalen' is the length of * TCP/UDP header + payload. */ void @@ -208,7 +436,7 @@ nm_os_csum_tcpudp_ipv4(struct nm_iphdr *iph, void *data, } /* Compute and insert a TCP/UDP checksum over IPv6: 'ip6h' points to the IPv6 - * header, 'data' points to the TCP/UDP header, 'datalen' is the lenght of + * header, 'data' points to the TCP/UDP header, 'datalen' is the length of * TCP/UDP header + payload. */ void @@ -233,14 +461,25 @@ nm_os_send_up(struct ifnet *ifp, struct mbuf *m, struct mbuf *prev) (void)ifp; (void)prev; m->priority = NM_MAGIC_PRIORITY_RX; /* do not reinject to netmap */ +#ifdef NETMAP_LINUX_HAVE_NETIF_RX_NI + netif_rx_ni(m); +#else netif_rx(m); +#endif + return NULL; } int -nm_os_mbuf_has_offld(struct mbuf *m) +nm_os_mbuf_has_csum_offld(struct mbuf *m) +{ + return m->ip_summed == CHECKSUM_PARTIAL; +} + +int +nm_os_mbuf_has_seg_offld(struct mbuf *m) { - return m->ip_summed == CHECKSUM_PARTIAL || skb_is_gso(m); + return skb_is_gso(m); } #ifdef WITH_GENERIC @@ -260,64 +499,64 @@ nm_os_mbuf_has_offld(struct mbuf *m) static NETMAP_LINUX_TIMER_RTYPE generic_timer_handler(struct hrtimer *t) { - struct nm_generic_mit *mit = - container_of(t, struct nm_generic_mit, mit_timer); - u_int work_done; + struct nm_generic_mit *mit = + container_of(t, struct nm_generic_mit, mit_timer); + u_int work_done; - if (!mit->mit_pending) { - return HRTIMER_NORESTART; - } + if (!mit->mit_pending) { + return HRTIMER_NORESTART; + } - /* Some work arrived while the timer was counting down: - * Reset the pending work flag, restart the timer and send - * a notification. - */ - mit->mit_pending = 0; - /* below is a variation of netmap_generic_irq XXX revise */ - if (nm_netmap_on(mit->mit_na)) { - netmap_common_irq(mit->mit_na, mit->mit_ring_idx, &work_done); - generic_rate(0, 0, 0, 0, 0, 1); - } - nm_os_mitigation_restart(mit); + /* Some work arrived while the timer was counting down: + * Reset the pending work flag, restart the timer and send + * a notification. + */ + mit->mit_pending = 0; + /* below is a variation of netmap_generic_irq XXX revise */ + if (nm_netmap_on(mit->mit_na)) { + netmap_common_irq(mit->mit_na, mit->mit_ring_idx, &work_done); + generic_rate(0, 0, 0, 0, 0, 1); + } + nm_os_mitigation_restart(mit); - return HRTIMER_RESTART; + return HRTIMER_RESTART; } void nm_os_mitigation_init(struct nm_generic_mit *mit, int idx, - struct netmap_adapter *na) + struct netmap_adapter *na) { - hrtimer_init(&mit->mit_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - mit->mit_timer.function = &generic_timer_handler; - mit->mit_pending = 0; - mit->mit_ring_idx = idx; - mit->mit_na = na; + nm_hrtimer_setup(&mit->mit_timer, &generic_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + mit->mit_pending = 0; + mit->mit_ring_idx = idx; + mit->mit_na = na; } void nm_os_mitigation_start(struct nm_generic_mit *mit) { - hrtimer_start(&mit->mit_timer, ktime_set(0, netmap_generic_mit), HRTIMER_MODE_REL); + hrtimer_start(&mit->mit_timer, ktime_set(0, netmap_generic_mit), HRTIMER_MODE_REL); } void nm_os_mitigation_restart(struct nm_generic_mit *mit) { - hrtimer_forward_now(&mit->mit_timer, ktime_set(0, netmap_generic_mit)); + hrtimer_forward_now(&mit->mit_timer, ktime_set(0, netmap_generic_mit)); } int nm_os_mitigation_active(struct nm_generic_mit *mit) { - return hrtimer_active(&mit->mit_timer); + return hrtimer_active(&mit->mit_timer); } void nm_os_mitigation_cleanup(struct nm_generic_mit *mit) { - hrtimer_cancel(&mit->mit_timer); + hrtimer_cancel(&mit->mit_timer); } @@ -358,6 +597,13 @@ linux_generic_rx_handler_common(struct mbuf *m) can see it. */ skb_push(m, ETH_HLEN); +#ifdef NETMAP_LINUX_HAVE_SKB_VLAN_UNTAG + /* First VLAN tag has been already popped to skb metadata. */ + if (skb_vlan_tag_present(m)) { + m = __vlan_hwaccel_push_inside(m); + } +#endif /* NETMAP_LINUX_HAVE_SKB_VLAN_UNTAG */ + /* Possibly steal the mbuf and notify the pollers for a new RX * packet. */ stolen = generic_rx_handler(m->dev, m); @@ -365,6 +611,13 @@ linux_generic_rx_handler_common(struct mbuf *m) return NM_RX_HANDLER_STOLEN; } +#ifdef NETMAP_LINUX_HAVE_SKB_VLAN_UNTAG + /* Untag once again if not stolen */ + if (eth_type_vlan(m->protocol)) { + m = skb_vlan_untag(m); + } +#endif /* NETMAP_LINUX_HAVE_SKB_VLAN_UNTAG */ + skb_pull(m, ETH_HLEN); return NM_RX_HANDLER_PASS; @@ -398,33 +651,45 @@ nm_os_catch_rx(struct netmap_generic_adapter *gna, int intercept) { #ifndef NETMAP_LINUX_HAVE_RX_REGISTER #warning "Packet reception with emulated (generic) mode not supported for this kernel version" - return 0; + return 0; #else /* HAVE_RX_REGISTER */ - struct netmap_adapter *na = &gna->up.up; - struct ifnet *ifp = netmap_generic_getifp(gna); - - if (intercept) { - return -netdev_rx_handler_register(ifp, - &linux_generic_rx_handler, na); - } else { - netdev_rx_handler_unregister(ifp); - return 0; - } + struct netmap_adapter *na = &gna->up.up; + struct ifnet *ifp = netmap_generic_getifp(gna); + int ret = 0; + + if (!ifp) { + nm_prerr("Failed to get ifp"); + return -EBUSY; + } + + nm_os_ifnet_lock(); + if (intercept) { + ret = -netdev_rx_handler_register(ifp, + &linux_generic_rx_handler, na); + } else { + netdev_rx_handler_unregister(ifp); + } + nm_os_ifnet_unlock(); + return ret; #endif /* HAVE_RX_REGISTER */ } +#ifndef NETMAP_LINUX_SELECT_QUEUE_PARM3 +#define NETMAP_LINUX_SELECT_QUEUE_PARM3 void* +#endif /*! NETMAP_LINUX_SELECT_QUEUE_PARM3 */ + #ifdef NETMAP_LINUX_SELECT_QUEUE static u16 generic_ndo_select_queue(struct ifnet *ifp, struct mbuf *m #if NETMAP_LINUX_SELECT_QUEUE >= 3 - , void *accel_priv + , NETMAP_LINUX_SELECT_QUEUE_PARM3 accel_priv #if NETMAP_LINUX_SELECT_QUEUE >= 4 , select_queue_fallback_t fallback #endif /* >= 4 */ #endif /* >= 3 */ ) { - return skb_get_queue_mapping(m); // actually 0 on 2.6.23 and before + return skb_get_queue_mapping(m); // actually 0 on 2.6.23 and before } #endif /* SELECT_QUEUE */ @@ -443,10 +708,21 @@ generic_ndo_start_xmit(struct mbuf *m, struct ifnet *ifp) (struct netmap_generic_adapter *)NA(ifp); if (likely(m->priority == NM_MAGIC_PRIORITY_TX)) { + netdev_tx_t ret; + /* Reset priority, so that generic_netmap_tx_clean() * knows that it can reclaim this mbuf. */ m->priority = 0; - return gna->save_start_xmit(m, ifp); /* To the driver. */ + ret = gna->save_start_xmit(m, ifp); /* To the driver. */ + if (unlikely(ret == NETDEV_TX_BUSY)) { + /* The driver is busy, so the packet has not + * been consumed and will be resubmitted + * later. Set the priority again to our + * magic value, so that it hits again + * this code path. */ + m->priority = NM_MAGIC_PRIORITY_TX; + } + return ret; } /* To a netmap RX ring. */ @@ -454,12 +730,15 @@ generic_ndo_start_xmit(struct mbuf *m, struct ifnet *ifp) } struct nm_generic_qdisc { - unsigned int qidx; unsigned int limit; }; static int -generic_qdisc_init(struct Qdisc *qdisc, struct nlattr *opt) +generic_qdisc_init(struct Qdisc *qdisc, struct nlattr *opt +#ifdef NETMAP_LINUX_HAVE_QDISC_EXTACK + , struct netlink_ext_ack *extack +#endif /* NETMAP_LINUX_HAVE_QDISC_EXTACK */ + ) { struct nm_generic_qdisc *priv = NULL; @@ -467,21 +746,20 @@ generic_qdisc_init(struct Qdisc *qdisc, struct nlattr *opt) * always use our priv->limit, for simplicity. */ priv = qdisc_priv(qdisc); - priv->qidx = 0; priv->limit = 1024; /* This is going to be overridden. */ if (opt) { - struct nm_generic_qdisc *qdiscopt = nla_data(opt); + uint32_t *limit = nla_data(opt); - if (nla_len(opt) < sizeof(*qdiscopt)) { - D("Invalid netlink attribute"); - return EINVAL; + if (nla_len(opt) < sizeof(*limit) || *limit <= 0) { +#ifdef NETMAP_LINUX_HAVE_QDISC_EXTACK + NL_SET_ERR_MSG(extack, "Invalid netlink attribute"); +#else + nm_prerr("Invalid netlink attribute"); +#endif /* NETMAP_LINUX_HAVE_QDISC_EXTACK */ + return -EINVAL; } - - priv->qidx = qdiscopt->qidx; - priv->limit = qdiscopt->limit; - D("Qdisc #%d initialized with max_len = %u", priv->qidx, - priv->limit); + priv->limit = *limit; } /* Qdisc bypassing is not an option for now. @@ -500,7 +778,7 @@ generic_qdisc_enqueue(struct mbuf *m, struct Qdisc *qdisc struct nm_generic_qdisc *priv = qdisc_priv(qdisc); if (unlikely(qdisc_qlen(qdisc) >= priv->limit)) { - RD(5, "dropping mbuf"); + nm_prlim(5, "dropping mbuf"); return qdisc_drop(m, qdisc #ifdef NETMAP_LINUX_HAVE_QDISC_ENQUEUE_TOFREE @@ -510,7 +788,7 @@ generic_qdisc_enqueue(struct mbuf *m, struct Qdisc *qdisc /* or qdisc_reshape_fail() ? */ } - ND(5, "Enqueuing mbuf, len %u", qdisc_qlen(qdisc)); + nm_prdis(5, "Enqueuing mbuf, len %u", qdisc_qlen(qdisc)); return qdisc_enqueue_tail(m, qdisc); } @@ -524,135 +802,171 @@ generic_qdisc_dequeue(struct Qdisc *qdisc) return NULL; } - if (unlikely(m->priority == NM_MAGIC_PRIORITY_TXQE)) { - /* nm_os_generic_xmit_frame() asked us an event on this mbuf. - * We have to set the priority to the normal TX token, so that - * generic_ndo_start_xmit can pass it to the driver. */ - m->priority = NM_MAGIC_PRIORITY_TX; - ND(5, "Event met, notify %p", m); - netmap_generic_irq(NA(qdisc_dev(qdisc)), - skb_get_queue_mapping(m), NULL); - } + if (unlikely(m->priority == NM_MAGIC_PRIORITY_TXQE)) { + /* nm_os_generic_xmit_frame() asked us an event on this mbuf. + * We have to set the priority to the normal TX token, so that + * generic_ndo_start_xmit can pass it to the driver. */ + m->priority = NM_MAGIC_PRIORITY_TX; + nm_prdis(5, "Event met, notify %p", m); + netmap_generic_irq(NA(qdisc_dev(qdisc)), + skb_get_queue_mapping(m), NULL); + } - ND(5, "Dequeuing mbuf, len %u", qdisc_qlen(qdisc)); + nm_prdis(5, "Dequeuing mbuf, len %u", qdisc_qlen(qdisc)); return m; } +static struct mbuf * +generic_qdisc_peek(struct Qdisc *qdisc) +{ + return qdisc_peek_head(qdisc); +} + static struct Qdisc_ops generic_qdisc_ops __read_mostly = { - .id = "netmap_generic", + .id = "netmapemu", .priv_size = sizeof(struct nm_generic_qdisc), + .enqueue = generic_qdisc_enqueue, + .dequeue = generic_qdisc_dequeue, + .peek = generic_qdisc_peek, .init = generic_qdisc_init, .reset = qdisc_reset_queue, .change = generic_qdisc_init, - .enqueue = generic_qdisc_enqueue, - .dequeue = generic_qdisc_dequeue, .dump = NULL, .owner = THIS_MODULE, }; static int -nm_os_catch_qdisc(struct netmap_generic_adapter *gna, int intercept) +tc_configure(struct ifnet *ifp, const char *qdisc_name, + uint32_t parent, uint32_t handle, uint32_t limit) { - struct netmap_adapter *na = &gna->up.up; - struct ifnet *ifp = netmap_generic_getifp(gna); - struct nm_generic_qdisc *qdiscopt = NULL; - struct Qdisc *fqdisc = NULL; - struct nlattr *nla = NULL; - struct netdev_queue *txq; - unsigned int i; - - if (!gna->txqdisc) { - return 0; + struct sockaddr_nl saddr = { + .nl_family = AF_NETLINK, + .nl_groups = 0, + .nl_pid = 0, + }; + struct msghdr msg = { + .msg_name = (struct sockaddr *)&saddr, + .msg_namelen = sizeof(saddr), + .msg_flags = /* MSG_DONTWAIT */0, + }; + struct { + struct nlmsghdr hdr; + struct tcmsg tcmsg; + char buf[100]; + } nlreq = { + .hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)), + .hdr.nlmsg_type = RTM_NEWQDISC, + .hdr.nlmsg_flags = NLM_F_REQUEST|/*NLM_F_ACK|*/NLM_F_REPLACE|NLM_F_CREATE, + .hdr.nlmsg_seq = 1, + .hdr.nlmsg_pid = 0, + .tcmsg.tcm_family = AF_UNSPEC, + .tcmsg.tcm_ifindex = ifp->ifindex, + .tcmsg.tcm_handle = handle, + .tcmsg.tcm_parent = parent, + .tcmsg.tcm_info = 0, + }; + struct socket *sock = NULL; + struct nlattr *attr_kind; + struct nlattr *attr_opt; + struct iovec iov; + int ret; + + ret = sock_create_kern( +#ifdef NETMAP_LINUX_SOCK_CREATE_KERN_NETNS + current->nsproxy ? + current->nsproxy->net_ns : &init_net, +#endif /* NETMAP_LINUX_SOCK_CREATE_KERN_NETNS */ + AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, &sock); + if (ret) { + nm_prerr("Failed to create netlink socket (err=%d)", ret); + return -ret; } - if (intercept) { - nla = kmalloc(nla_attr_size(sizeof(*qdiscopt)), - GFP_KERNEL); - if (!nla) { - D("Failed to allocate netlink attribute"); - return ENOMEM; - } - nla->nla_type = RTM_NEWQDISC; - nla->nla_len = nla_attr_size(sizeof(*qdiscopt)); - qdiscopt = (struct nm_generic_qdisc *)nla_data(nla); - memset(qdiscopt, 0, sizeof(*qdiscopt)); - qdiscopt->limit = na->num_tx_desc; - } - - if (ifp->flags & IFF_UP) { - dev_deactivate(ifp); - } - - /* Replace the current qdiscs with our own. */ - for (i = 0; i < ifp->real_num_tx_queues; i++) { - struct Qdisc *nqdisc = NULL; - struct Qdisc *oqdisc; - int err; - - txq = netdev_get_tx_queue(ifp, i); - - if (intercept) { - /* This takes a refcount to netmap module, alloc the - * qdisc and calls the init() op with NULL netlink - * attribute. */ - nqdisc = qdisc_create_dflt( -#ifndef NETMAP_LINUX_QDISC_CREATE_DFLT_3ARGS - ifp, -#endif /* NETMAP_LINUX_QDISC_CREATE_DFLT_3ARGS */ - txq, &generic_qdisc_ops, - TC_H_UNSPEC); - if (!nqdisc) { - D("Failed to create qdisc"); - goto qdisc_create; - } - fqdisc = fqdisc ?: nqdisc; - - /* Call the change() op passing a valid netlink - * attribute. This is used to set the queue idx. */ - qdiscopt->qidx = i; - err = nqdisc->ops->change(nqdisc, nla); - if (err) { - D("Failed to init qdisc"); - goto qdisc_create; - } - } - oqdisc = dev_graft_qdisc(txq, nqdisc); - /* We can call this also with - * odisc == &noop_qdisc, since the noop - * qdisc has the TCQ_F_BUILTIN flag set, - * and so qdisc_destroy will skip it. */ - qdisc_destroy(oqdisc); + ret = kernel_bind(sock, (struct NM_SOCKADDR *)&saddr, sizeof(saddr)); + if (ret) { + nm_prerr("Failed to bind() netlink socket (err=%d)", ret); + goto release; } - kfree(nla); - - if (ifp->qdisc) { - qdisc_destroy(ifp->qdisc); - } - if (intercept) { - atomic_inc(&fqdisc->refcnt); - ifp->qdisc = fqdisc; - } else { - ifp->qdisc = &noop_qdisc; + /* Push TCA_KIND attr. */ + attr_kind = (struct nlattr *)(((void *)&nlreq) + + NLMSG_ALIGN(nlreq.hdr.nlmsg_len)); + attr_kind->nla_len = NLA_HDRLEN + strlen(qdisc_name) + 1; + attr_kind->nla_type = TCA_KIND; + strcpy(((void *)attr_kind) + NLA_HDRLEN, qdisc_name); + nlreq.hdr.nlmsg_len = NLMSG_ALIGN(nlreq.hdr.nlmsg_len) + + NLA_ALIGN(attr_kind->nla_len); + + if (limit > 0) { + /* Push TCA_OPTIONS attr. */ + attr_opt = (struct nlattr *)(((void *)&nlreq) + + NLMSG_ALIGN(nlreq.hdr.nlmsg_len)); + attr_opt->nla_len = NLA_HDRLEN + sizeof(uint32_t); + attr_opt->nla_type = TCA_OPTIONS; + *((uint32_t *)(((void *)attr_opt) + NLA_HDRLEN)) = limit; + nlreq.hdr.nlmsg_len = NLMSG_ALIGN(nlreq.hdr.nlmsg_len) + + NLA_ALIGN(attr_opt->nla_len); } - if (ifp->flags & IFF_UP) { - dev_activate(ifp); + iov.iov_base = (void *)&nlreq; + iov.iov_len = nlreq.hdr.nlmsg_len; + ret = kernel_sendmsg(sock, &msg, (struct kvec *)&iov, 1, + iov.iov_len); + if (ret != nlreq.hdr.nlmsg_len) { + nm_prerr("Failed to sendmsg to netlink socket (err=%d)", ret); + ret = -EINVAL; + goto release; } + ret = 0; - return 0; + nm_prinf("ifp %s qdisc %s parent %u handle %u", ifp->name, qdisc_name, parent, handle); -qdisc_create: - if (nla) { - kfree(nla); - } +release: + sock_release(sock); + + return ret; +} + +static int +nm_os_catch_qdisc(struct netmap_generic_adapter *gna, int intercept) +{ + struct ifnet *ifp = netmap_generic_getifp(gna); + struct netmap_adapter *na = &gna->up.up; + bool multiqueue = (na->num_tx_rings > 1); + static uint32_t root_handle_cnt = 18; + uint32_t root_handle = multiqueue ? root_handle_cnt++ : 0; + uint32_t limit = (!multiqueue && intercept) ? na->num_tx_desc : 0; + const char *qdisc_name; + int ret = 0; - nm_os_catch_qdisc(gna, 0); + if (!gna->txqdisc) { + return 0; + } - return -1; + qdisc_name = multiqueue ? "mq" : + (intercept ? generic_qdisc_ops.id : "pfifo"); + /* Configure root qdisc. + * sudo tc qdisc replace dev ifp->name root handle @root_handle: qdisc_name */ + ret = tc_configure(ifp, qdisc_name, /*parent=*/TC_H_ROOT, + /*handle=*/root_handle << 16, limit); + if (ret) { + return -ret; + } + if (intercept && multiqueue) { + /* Configure per-queue qdisc. */ + int i; + qdisc_name = (intercept ? generic_qdisc_ops.id : "pfifo"); + limit = na->num_tx_desc; + for (i = 0; i < na->num_tx_rings; i++) { + tc_configure(ifp, qdisc_name, + /*parent=*/(root_handle << 16) | (i+1), + /*handle=*/0, limit); + } + } + return 0; } /* Must be called under rtnl. */ @@ -663,11 +977,18 @@ nm_os_catch_tx(struct netmap_generic_adapter *gna, int intercept) struct ifnet *ifp = netmap_generic_getifp(gna); int err; + if (!ifp) { + nm_prerr("Failed to get ifp"); + return -1; + } + err = nm_os_catch_qdisc(gna, intercept); if (err) { return err; } + nm_os_ifnet_lock(); + if (intercept) { /* * Save the old pointer to the netdev_ops, @@ -679,24 +1000,28 @@ nm_os_catch_tx(struct netmap_generic_adapter *gna, int intercept) /* Save a redundant copy of ndo_start_xmit(). */ gna->save_start_xmit = ifp->netdev_ops->ndo_start_xmit; - gna->generic_ndo = *ifp->netdev_ops; /* Copy all */ - gna->generic_ndo.ndo_start_xmit = &generic_ndo_start_xmit; + gna->up.nm_ndo = *ifp->netdev_ops; /* copy all, replace some */ + gna->up.nm_ndo.ndo_start_xmit = &generic_ndo_start_xmit; #ifndef NETMAP_LINUX_SELECT_QUEUE - D("No packet steering support"); + nm_prerr("No packet steering support"); #else - gna->generic_ndo.ndo_select_queue = &generic_ndo_select_queue; + gna->up.nm_ndo.ndo_select_queue = &generic_ndo_select_queue; #endif - - ifp->netdev_ops = &gna->generic_ndo; + ifp->netdev_ops = &gna->up.nm_ndo; } else { /* Restore the original netdev_ops. */ ifp->netdev_ops = (void *)na->if_transmit; } + nm_os_ifnet_unlock(); + return 0; } +/* Used to cover cases where ETH_P_802_3_MIN is undefined */ +#define NM_ETH_P_802_3_MIN 0x0600 + /* Transmit routine used by generic_netmap_txsync(). Returns 0 on success and -1 on error (which may be packet drops or other errors). */ int @@ -706,8 +1031,9 @@ nm_os_generic_xmit_frame(struct nm_os_gen_arg *a) struct ifnet *ifp = a->ifp; u_int len = a->len; netdev_tx_t ret; + uint16_t ethertype; - /* We know that the driver needs to prepend ifp->needed_headroom bytes + /* We know that the driver needs to prepend LL_RESERVED_SPACE(ifp) bytes * to each packet to be transmitted. We then reset the mbuf pointers * to the correct initial state: * ___________________________________________ @@ -717,29 +1043,38 @@ nm_os_generic_xmit_frame(struct nm_os_gen_arg *a) * tail * * which correspond to an empty buffer with exactly - * ifp->needed_headroom bytes between head and data. + * LL_RESERVED_SPACE(ifp) bytes between head and data. */ m->len = 0; - m->data = m->head + ifp->needed_headroom; + m->data = m->head + LL_RESERVED_SPACE(ifp); skb_reset_tail_pointer(m); skb_reset_mac_header(m); - /* Initialize the header pointers assuming this is an IPv4 packet. - * This is useful to make netmap interact well with TC when - * netmap_generic_txqdisc == 0. */ - skb_set_network_header(m, 14); - skb_set_transport_header(m, 34); - m->protocol = htons(ETH_P_IP); - m->pkt_type = PACKET_HOST; - /* Copy a netmap buffer into the mbuf. * TODO Support the slot flags (NS_MOREFRAG, NS_INDIRECT). */ skb_copy_to_linear_data(m, a->addr, len); // skb_store_bits(m, 0, addr, len); skb_put(m, len); + /* Initialize the header pointers assuming this is an IP packet. + * This is useful to make netmap interact well with TC when + * netmap_generic_txqdisc == 0. */ + skb_set_network_header(m, ETH_HLEN); + ethertype = *((uint16_t*)(m->data + ETH_ALEN * 2)); + m->protocol = ntohs(ethertype) >= NM_ETH_P_802_3_MIN ? ethertype : htons(ETH_P_802_3); + m->pkt_type = PACKET_HOST; + m->ip_summed = CHECKSUM_NONE; + + if (m->protocol == htons(ETH_P_IPV6)) { + skb_set_transport_header(m, ETH_HLEN + sizeof(struct nm_ipv6hdr)); + } else if (m->protocol == htons(ETH_P_IP)) { + skb_set_transport_header(m, ETH_HLEN + sizeof(struct nm_iphdr)); + } else { + skb_reset_transport_header(m); + } + /* Hold a reference on this, we are going to recycle mbufs as * much as possible. */ - NM_ATOMIC_INC(&m->users); + skb_get(m); /* On linux m->dev is not reliable, since it can be changed by the * ndo_start_xmit() callback. This happens, for instance, with veth @@ -749,10 +1084,36 @@ nm_os_generic_xmit_frame(struct nm_os_gen_arg *a) m->dev = ifp; skb_shinfo(m)->destructor_arg = m->dev; + /* Tell the NIC to compute checksums for outgoing TCP and UDP packets */ + if (netmap_generic_hwcsum) { + uint8_t transport_proto = IPPROTO_IP; + + if (m->protocol == htons(ETH_P_IPV6)) { + transport_proto = ((struct nm_ipv6hdr*)ip_hdr(m))->nexthdr; + } else if (m->protocol == htons(ETH_P_IP)) { + transport_proto = ((struct nm_iphdr*)ip_hdr(m))->protocol; + } + + if (transport_proto == IPPROTO_TCP) { + m->ip_summed = CHECKSUM_PARTIAL; + m->csum_start = m->transport_header; + m->csum_offset = 16; /* offset to TCP checksum within TCP header */ + } else if (transport_proto == IPPROTO_UDP) { + m->ip_summed = CHECKSUM_PARTIAL; + m->csum_start = m->transport_header; + m->csum_offset = 6; /* offset to UDP checksum within UDP header */ + } + } + /* Tell generic_ndo_start_xmit() to pass this mbuf to the driver. */ skb_set_queue_mapping(m, a->ring_nr); m->priority = a->qevent ? NM_MAGIC_PRIORITY_TXQE : NM_MAGIC_PRIORITY_TX; + if (unlikely(m->next)) { + nm_prlim(1, "Warning: resetting skb->next as it is not NULL\n"); + m->next = NULL; + } + ret = dev_queue_xmit(m); if (unlikely(ret != NET_XMIT_SUCCESS)) { @@ -771,7 +1132,7 @@ nm_os_generic_xmit_frame(struct nm_os_gen_arg *a) * field), and so the temporary noop qdisc enqueue * method will drop the packet and return NET_XMIT_CN. */ - RD(3, "Warning: dev_queue_xmit() is dropping [%d]", ret); + nm_prlim(3, "Warning: dev_queue_xmit() is dropping [%d]", ret); return -1; } @@ -786,26 +1147,37 @@ nm_os_generic_set_features(struct netmap_generic_adapter *gna) } #endif /* WITH_GENERIC */ -/* Use ethtool to find the current NIC rings lengths, so that the netmap - rings can have the same lengths. */ +/* + * Use ethtool to find the current NIC rings lengths, so that the netmap + * rings can have the same lengths. If no ethtool command is available, + * or something fails, do not update the output arguments. + * The caller should initialize the output arguments with sane defaults. + */ int nm_os_generic_find_num_desc(struct ifnet *ifp, unsigned int *tx, unsigned int *rx) { int error = EOPNOTSUPP; #ifdef NETMAP_LINUX_HAVE_GET_RINGPARAM struct ethtool_ringparam rp; +#if NETMAP_LINUX_HAVE_GET_RINGPARAM == 2 + struct kernel_ethtool_ringparam ker; + struct netlink_ext_ack extack; +#endif + unsigned int ntx, nrx; if (ifp->ethtool_ops && ifp->ethtool_ops->get_ringparam) { - ifp->ethtool_ops->get_ringparam(ifp, &rp); - *tx = rp.tx_pending ? rp.tx_pending : rp.tx_max_pending; - *rx = rp.rx_pending ? rp.rx_pending : rp.rx_max_pending; - if (*rx < 3) { - D("Invalid RX ring size %u, using default", *rx); - *rx = netmap_generic_ringsize; + ifp->ethtool_ops->get_ringparam(ifp, &rp +#if NETMAP_LINUX_HAVE_GET_RINGPARAM == 2 + , &ker, &extack +#endif + ); + ntx = rp.tx_pending ? rp.tx_pending : rp.tx_max_pending; + nrx = rp.rx_pending ? rp.rx_pending : rp.rx_max_pending; + if (nrx >= 3) { + *rx = nrx; } - if (*tx < 3) { - D("Invalid TX ring size %u, using default", *tx); - *tx = netmap_generic_ringsize; + if (ntx >= 3) { + *tx = ntx; } error = 0; } @@ -832,13 +1204,14 @@ nm_os_generic_find_num_queues(struct ifnet *ifp, u_int *txq, u_int *rxq) *rxq = ifp->real_num_rx_queues; #else *rxq = 1; + nm_prinf("WARNING: netmap will use only the first " + "RX queue of %s", ifp->name); #endif /* HAVE_REAL_NUM_RX_QUEUES */ } } int -netmap_linux_config(struct netmap_adapter *na, - u_int *txr, u_int *txd, u_int *rxr, u_int *rxd) +netmap_rings_config_get(struct netmap_adapter *na, struct nm_config_info *info) { struct ifnet *ifp = na->ifp; int error = 0; @@ -846,21 +1219,44 @@ netmap_linux_config(struct netmap_adapter *na, rtnl_lock(); if (ifp == NULL) { - D("zombie adapter"); + nm_prerr("zombie adapter"); error = ENXIO; goto out; } - error = nm_os_generic_find_num_desc(ifp, txd, rxd); - if (error) - goto out; - nm_os_generic_find_num_queues(ifp, txr, rxr); + nm_os_generic_find_num_queues(ifp, &info->num_tx_rings, + &info->num_rx_rings); + /* + * Start from what we already know and check for config + * updates. + */ + info->num_tx_descs = na->num_tx_desc; + info->num_rx_descs = na->num_rx_desc; + nm_os_generic_find_num_desc(ifp, &info->num_tx_descs, + &info->num_rx_descs); out: rtnl_unlock(); return error; } +EXPORT_SYMBOL(netmap_rings_config_get); + +/* Default nm_config implementation for netmap_hw_adapter on Linux. */ +int +nm_os_config(struct netmap_adapter *na, struct nm_config_info *info) +{ + int ret = netmap_rings_config_get(na, info); + + if (ret) { + return ret; + } + /* Take whatever we had at init time. */ + info->rx_buf_maxsize = na->rx_buf_maxsize; + + return 0; +} +EXPORT_SYMBOL(nm_os_config); /* ######################## FILE OPERATIONS ####################### */ @@ -903,13 +1299,13 @@ struct nm_linux_selrecord_t { * - file is passed as 'td'; */ static u_int -linux_netmap_poll(struct file * file, struct poll_table_struct *pwait) +linux_netmap_poll(struct file *file, struct poll_table_struct *pwait) { #ifdef NETMAP_LINUX_PWAIT_KEY int events = pwait ? pwait->NETMAP_LINUX_PWAIT_KEY : \ POLLIN | POLLOUT | POLLERR; #else - int events = POLLIN | POLLOUT; /* XXX maybe... */ + int events = POLLIN | POLLOUT | POLLERR; #endif /* PWAIT_KEY */ struct nm_linux_selrecord_t sr = { .file = file, @@ -919,23 +1315,37 @@ linux_netmap_poll(struct file * file, struct poll_table_struct *pwait) return netmap_poll(priv, events, &sr); } +#ifndef NETMAP_LINUX_HAVE_SETPAGESWAPBACKED +#define SetPageSwapBacked(p_) +#endif /* NETMAP_LINUX_HAVE_SETPAGESWAPBACKED */ +#ifdef NETMAP_LINUX_HAVE_VMFAULT_T +static vm_fault_t +#else static int +#endif /* NETMAP_LINUX_HAVE_VMFAULT_T */ +#ifdef NETMAP_LINUX_HAVE_FAULT_VMA_ARG linux_netmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { +#else +linux_netmap_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; +#endif /* NETMAP_LINUX_HAVE_FAULT_VMA_ARG */ struct netmap_priv_d *priv = vma->vm_private_data; struct netmap_adapter *na = priv->np_na; struct page *page; - unsigned long off = (vma->vm_pgoff + vmf->pgoff) << PAGE_SHIFT; + unsigned long off = vmf->pgoff << PAGE_SHIFT; unsigned long pa, pfn; pa = netmap_mem_ofstophys(na->nm_mem, off); - ND("fault off %lx -> phys addr %lx", off, pa); + nm_prdis("fault off %lx -> phys addr %lx", off, pa); if (pa == 0) return VM_FAULT_SIGBUS; pfn = pa >> PAGE_SHIFT; if (!pfn_valid(pfn)) return VM_FAULT_SIGBUS; page = pfn_to_page(pfn); + SetPageSwapBacked(page); get_page(page); vmf->page = page; return 0; @@ -949,8 +1359,9 @@ static int linux_netmap_mmap(struct file *f, struct vm_area_struct *vma) { int error = 0; - unsigned long off; - u_int memsize, memflags; + uint64_t off; + unsigned int memflags; + uint64_t memsize; struct netmap_priv_d *priv = f->private_data; struct netmap_adapter *na = priv->np_na; /* @@ -966,11 +1377,11 @@ linux_netmap_mmap(struct file *f, struct vm_area_struct *vma) /* check that [off, off + vsize) is within our memory */ error = netmap_mem_get_info(na->nm_mem, &memsize, &memflags, NULL); - ND("get_info returned %d", error); + nm_prdis("get_info returned %d", error); if (error) return -error; off = vma->vm_pgoff << PAGE_SHIFT; - ND("off %lx size %lx memsize %x", off, + nm_prdis("off %lx size %lx memsize %x", off, (vma->vm_end - vma->vm_start), memsize); if (off + (vma->vm_end - vma->vm_start) > memsize) return -EINVAL; @@ -1006,14 +1417,52 @@ linux_netmap_start_xmit(struct sk_buff *skb, struct net_device *dev) return (NETDEV_TX_OK); } +#define native_change_mtu(na, dev, mtu) \ + (((struct net_device_ops *)(na)->if_transmit)->NETMAP_LINUX_CHANGE_MTU(dev, mtu)) + +int +linux_netmap_change_mtu(struct net_device *dev, int new_mtu) +{ + struct netmap_adapter *na = NA(dev); + + if (netmap_buf_size_validate(na, new_mtu)) + return -EINVAL; + return native_change_mtu(na, dev, new_mtu); +} + + /* while in netmap mode, we cannot tolerate any change in the * number of rx/tx rings and descriptors + * + * Linux calls this while holding the rtnl_lock(). */ +#ifdef NETMAP_LINUX_HAVE_AX25PTR +static +#endif int linux_netmap_set_ringparam(struct net_device *dev, - struct ethtool_ringparam *e) -{ + struct ethtool_ringparam *e +#ifdef NETMAP_LINUX_HAVE_SETRNGPRM_4ARGS + , struct kernel_ethtool_ringparam *k + , struct netlink_ext_ack *a +#endif /* NETMAP_LINUX_HAVE_SETRNGPRM_4ARGS */ + ) +{ +#ifdef NETMAP_LINUX_HAVE_AX25PTR return -EBUSY; +#else /* !NETMAP_LINUX_HAVE_AX25PTR */ + struct netmap_adapter *na = NA(dev); + + if (nm_netmap_on(na)) + return -EBUSY; + if (na->magic.save_eto->set_ringparam) + return na->magic.save_eto->set_ringparam(dev, e +#ifdef NETMAP_LINUX_HAVE_SETRNGPRM_4ARGS + , k, a +#endif /* NETMAP_LINUX_HAVE_SETRNGPRM_4ARGS */ + ); + return -EOPNOTSUPP; +#endif /* NETMAP_LINUX_HAVE_AX25PTR */ } #ifdef NETMAP_LINUX_HAVE_SET_CHANNELS @@ -1021,11 +1470,20 @@ int linux_netmap_set_channels(struct net_device *dev, struct ethtool_channels *e) { +#ifdef NETMAP_LINUX_HAVE_AX25PTR return -EBUSY; +#else /* !NETMAP_LINUX_HAVE_AX25PTR */ + struct netmap_adapter *na = NA(dev); + + if (nm_netmap_on(na)) + return -EBUSY; + if (na->magic.save_eto->set_channels) + return na->magic.save_eto->set_channels(dev, e); + return -EOPNOTSUPP; +#endif /* NETMAP_LINUX_HAVE_AX25PTR */ } #endif - #ifndef NETMAP_LINUX_HAVE_UNLOCKED_IOCTL #define LIN_IOCTL_NAME .ioctl static int @@ -1041,6 +1499,7 @@ linux_netmap_ioctl(struct file *file, u_int cmd, u_long data /* arg */) union { struct nm_ifreq ifr; struct nmreq nmr; + struct nmreq_header hdr; } arg; size_t argsize = 0; @@ -1051,9 +1510,14 @@ linux_netmap_ioctl(struct file *file, u_int cmd, u_long data /* arg */) case NIOCCONFIG: argsize = sizeof(arg.ifr); break; - default: + case NIOCREGIF: + case NIOCGINFO: argsize = sizeof(arg.nmr); break; + case NIOCCTRL: { + argsize = sizeof(arg.hdr); + break; + } } if (argsize) { if (!data) @@ -1062,7 +1526,8 @@ linux_netmap_ioctl(struct file *file, u_int cmd, u_long data /* arg */) if (copy_from_user(&arg, (void *)data, argsize) != 0) return -EFAULT; } - ret = netmap_ioctl(priv, cmd, (caddr_t)&arg, NULL); + ret = netmap_ioctl(priv, cmd, (caddr_t)&arg, NULL, + /*nr_body_is_user=*/1); if (data && copy_to_user((void*)data, &arg, argsize) != 0) return -EFAULT; return -ret; @@ -1073,9 +1538,9 @@ linux_netmap_ioctl(struct file *file, u_int cmd, u_long data /* arg */) static long linux_netmap_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) + unsigned long arg) { - return linux_netmap_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); + return linux_netmap_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); } #endif @@ -1102,6 +1567,7 @@ linux_netmap_open(struct inode *inode, struct file *file) error = -ENOMEM; goto out; } + priv->np_filp = file; file->private_data = priv; out: NMG_UNLOCK(); @@ -1111,19 +1577,18 @@ linux_netmap_open(struct inode *inode, struct file *file) static struct file_operations netmap_fops = { - .owner = THIS_MODULE, - .open = linux_netmap_open, - .mmap = linux_netmap_mmap, - LIN_IOCTL_NAME = linux_netmap_ioctl, + .owner = THIS_MODULE, + .open = linux_netmap_open, + .mmap = linux_netmap_mmap, + LIN_IOCTL_NAME = linux_netmap_ioctl, #ifdef CONFIG_COMPAT - .compat_ioctl = linux_netmap_compat_ioctl, + .compat_ioctl = linux_netmap_compat_ioctl, #endif - .poll = linux_netmap_poll, - .release = linux_netmap_release, + .poll = linux_netmap_poll, + .release = linux_netmap_release, }; -#ifdef WITH_VALE #ifdef CONFIG_NET_NS #include @@ -1202,7 +1667,7 @@ netmap_pernet_init(struct net *net) return error; ns->net = net; - ns->num_bridges = NM_BRIDGES; + ns->num_bridges = vale_max_bridges; ns->bridges = netmap_init_bridges2(ns->num_bridges); if (ns->bridges == NULL) { nm_bns_destroy(net, ns); @@ -1232,20 +1697,26 @@ static struct pernet_operations netmap_pernet_ops = { #endif }; +static int netmap_bns_registered = 0; int netmap_bns_register(void) { + int rv; #ifdef NETMAP_LINUX_HAVE_PERNET_OPS_ID - return -register_pernet_subsys(&netmap_pernet_ops); + rv = register_pernet_subsys(&netmap_pernet_ops); #else - return -register_pernet_gen_subsys(&netmap_bns_id, + rv = register_pernet_gen_subsys(&netmap_bns_id, &netmap_pernet_ops); #endif + netmap_bns_registered = !rv; + return -rv; } void netmap_bns_unregister(void) { + if (!netmap_bns_registered) + return; #ifdef NETMAP_LINUX_HAVE_PERNET_OPS_ID unregister_pernet_subsys(&netmap_pernet_ops); #else @@ -1254,7 +1725,6 @@ netmap_bns_unregister(void) #endif } #endif /* CONFIG_NET_NS */ -#endif /* WITH_VALE */ /* ##################### kthread wrapper ##################### */ #include @@ -1270,362 +1740,153 @@ nm_os_ncpus(void) return nr_cpu_ids; } -/* kthread context */ -struct nm_kthread_ctx { - /* files to exchange notifications */ - struct file *ioevent_file; /* notification from guest */ - struct file *irq_file; /* notification to guest (interrupt) */ - struct eventfd_ctx *irq_ctx; +struct nm_kctx { + struct mm_struct *mm; /* to access guest memory */ + struct task_struct *worker; /* the kernel thread */ + int attach_user; /* kthread attached to user_process */ + int affinity; - /* poll ioeventfd to receive notification from the guest */ - poll_table poll_table; - wait_queue_head_t *waitq_head; - wait_queue_t waitq; + /* worker function and parameter */ + nm_kctx_worker_fn_t worker_fn; + void *worker_private; - /* worker function and parameter */ - nm_kthread_worker_fn_t worker_fn; - void *worker_private; - - /* integer to manage multiple worker contexts */ - long type; -}; - -struct nm_kthread { - struct mm_struct *mm; - struct task_struct *worker; - - atomic_t scheduled; /* pending wake_up request */ - int attach_user; /* kthread attached to user_process */ - - struct nm_kthread_ctx worker_ctx; - int affinity; + /* integer to manage multiple worker contexts */ + long type; }; -void inline -nm_os_kthread_wakeup_worker(struct nm_kthread *nmk) -{ - /* - * There may be a race between FE and BE, - * which call both this function, and worker kthread, - * that reads ptk->scheduled. - * - * For us it is not important the counter value, - * but simply that it has changed since the last - * time the kthread saw it. - */ - atomic_inc(&nmk->scheduled); - wake_up_process(nmk->worker); -} - - -static void -nm_kthread_poll_fn(struct file *file, wait_queue_head_t *wq_head, poll_table *pt) -{ - struct nm_kthread_ctx *ctx; - - ctx = container_of(pt, struct nm_kthread_ctx, poll_table); - ctx->waitq_head = wq_head; - add_wait_queue(wq_head, &ctx->waitq); -} - -static int -nm_kthread_poll_wakeup(wait_queue_t *wq, unsigned mode, int sync, void *key) -{ - struct nm_kthread_ctx *ctx; - struct nm_kthread *nmk; - - ctx = container_of(wq, struct nm_kthread_ctx, waitq); - nmk = container_of(ctx, struct nm_kthread, worker_ctx); - nm_os_kthread_wakeup_worker(nmk); - - return 0; -} - -static void inline -nm_kthread_worker_fn(struct nm_kthread_ctx *ctx) -{ - __set_current_state(TASK_RUNNING); - ctx->worker_fn(ctx->worker_private); /* run payload */ - if (need_resched()) - schedule(); -} - static int -nm_kthread_worker(void *data) -{ - struct nm_kthread *nmk = data; - struct nm_kthread_ctx *ctx = &nmk->worker_ctx; - int old_scheduled = atomic_read(&nmk->scheduled); - int new_scheduled = old_scheduled; - mm_segment_t oldfs = get_fs(); - - if (nmk->mm) { - set_fs(USER_DS); - use_mm(nmk->mm); - } - - while (!kthread_should_stop()) { - if (!ctx->ioevent_file) { - /* - * if ioevent_file is not defined, we don't have notification - * mechanism and we continually execute worker_fn() - */ - nm_kthread_worker_fn(ctx); - - } else { - /* - * Set INTERRUPTIBLE state before to check if there is work. - * if wake_up() is called, although we have not seen the new - * counter value, the kthread state is set to RUNNING and - * after schedule() it is not moved off run queue. - */ - set_current_state(TASK_INTERRUPTIBLE); - - new_scheduled = atomic_read(&nmk->scheduled); - - /* check if there is a pending notification */ - if (likely(new_scheduled != old_scheduled)) { - old_scheduled = new_scheduled; - nm_kthread_worker_fn(ctx); - } else { - schedule(); - } - } - } - - __set_current_state(TASK_RUNNING); - - if (nmk->mm) { - unuse_mm(nmk->mm); - } - - set_fs(oldfs); - return 0; -} - -void inline -nm_os_kthread_send_irq(struct nm_kthread *nmk) +nm_kctx_worker(void *data) { - if (nmk->worker_ctx.irq_ctx) - eventfd_signal(nmk->worker_ctx.irq_ctx, 1); -} + struct nm_kctx *nmk = data; +#ifndef NETMAP_LINUX_HAVE_KTHREAD_USE_MM + mm_segment_t oldfs = get_fs(); +#endif /* NETMAP_LINUX_HAVE_KTHREAD_USE_MM */ -static void -nm_kthread_close_files(struct nm_kthread *nmk) -{ - struct nm_kthread_ctx *wctx = &nmk->worker_ctx; - - if (wctx->ioevent_file) { - fput(wctx->ioevent_file); - wctx->ioevent_file = NULL; - } - - if (wctx->irq_file) { - fput(wctx->irq_file); - wctx->irq_file = NULL; - eventfd_ctx_put(wctx->irq_ctx); - wctx->irq_ctx = NULL; - } -} + if (nmk->mm) { +#ifndef NETMAP_LINUX_HAVE_KTHREAD_USE_MM + set_fs(USER_DS); + use_mm(nmk->mm); +#else + kthread_use_mm(nmk->mm); +#endif /* NETMAP_LINUX_HAVE_KTHREAD_USE_MM */ + } -static int -nm_kthread_open_files(struct nm_kthread *nmk, void *opaque) -{ - struct file *file; - struct nm_kthread_ctx *wctx = &nmk->worker_ctx; - struct ptnetmap_cfgentry_qemu *ring_cfg = opaque; + while (!kthread_should_stop()) { + nmk->worker_fn(nmk->worker_private); /* work */ + if (need_resched()) + schedule(); + } - wctx->ioevent_file = NULL; - wctx->irq_file = NULL; + if (nmk->mm) { +#ifndef NETMAP_LINUX_HAVE_KTHREAD_USE_MM + unuse_mm(nmk->mm); +#else + kthread_unuse_mm(nmk->mm); +#endif /* NETMAP_LINUX_HAVE_KTHREAD_USE_MM */ + } - if (!opaque) { +#ifndef NETMAP_LINUX_HAVE_KTHREAD_USE_MM + set_fs(oldfs); +#endif /* NETMAP_LINUX_HAVE_KTHREAD_USE_MM */ return 0; - } - - if (ring_cfg->ioeventfd) { - file = eventfd_fget(ring_cfg->ioeventfd); - if (IS_ERR(file)) - goto err; - wctx->ioevent_file = file; - } - - if (ring_cfg->irqfd) { - file = eventfd_fget(ring_cfg->irqfd); - if (IS_ERR(file)) - goto err; - wctx->irq_file = file; - wctx->irq_ctx = eventfd_ctx_fileget(file); - } - - return 0; - -err: - nm_kthread_close_files(nmk); - return -PTR_ERR(file); } -static void -nm_kthread_init_poll(struct nm_kthread *nmk, struct nm_kthread_ctx *ctx) +void +nm_os_kctx_worker_setaff(struct nm_kctx *nmk, int affinity) { - init_waitqueue_func_entry(&ctx->waitq, nm_kthread_poll_wakeup); - init_poll_funcptr(&ctx->poll_table, nm_kthread_poll_fn); + nmk->affinity = affinity; } -static int -nm_kthread_start_poll(struct nm_kthread_ctx *ctx, struct file *file) +struct nm_kctx * +nm_os_kctx_create(struct nm_kctx_cfg *cfg, void *opaque) { - unsigned long mask; - int ret = 0; + struct nm_kctx *nmk = NULL; - if (ctx->waitq_head) - return 0; - mask = file->f_op->poll(file, &ctx->poll_table); - if (mask) - nm_kthread_poll_wakeup(&ctx->waitq, 0, 0, (void *)mask); - if (mask & POLLERR) { - if (ctx->waitq_head) - remove_wait_queue(ctx->waitq_head, &ctx->waitq); - ret = EINVAL; - } - return ret; -} + nmk = kzalloc(sizeof *nmk, GFP_KERNEL); + if (!nmk) + return NULL; -static void -nm_kthread_stop_poll(struct nm_kthread_ctx *ctx) -{ - if (ctx->waitq_head) { - remove_wait_queue(ctx->waitq_head, &ctx->waitq); - ctx->waitq_head = NULL; - } -} + nmk->worker_fn = cfg->worker_fn; + nmk->worker_private = cfg->worker_private; + nmk->type = cfg->type; + nmk->attach_user = cfg->attach_user; + nmk->affinity = -1; /* unspecified */ -void -nm_os_kthread_set_affinity(struct nm_kthread *nmk, int affinity) -{ - nmk->affinity = affinity; + return nmk; } -struct nm_kthread * -nm_os_kthread_create(struct nm_kthread_cfg *cfg, unsigned int cfgtype, - void *opaque) +int +nm_os_kctx_worker_start(struct nm_kctx *nmk) { - struct nm_kthread *nmk = NULL; - int error; - - if (cfgtype != PTNETMAP_CFGTYPE_QEMU) { - D("Unsupported cfgtype %u", cfgtype); - return NULL; - } + char name[16]; + int error = 0; - nmk = kzalloc(sizeof *nmk, GFP_KERNEL); - if (!nmk) - return NULL; + if (nmk->worker) { + return EBUSY; + } - nmk->worker_ctx.worker_fn = cfg->worker_fn; - nmk->worker_ctx.worker_private = cfg->worker_private; - nmk->worker_ctx.type = cfg->type; - atomic_set(&nmk->scheduled, 0); + /* Get caller's memory mapping if needed. */ + if (nmk->attach_user) { + nmk->mm = get_task_mm(current); + } - /* attach kthread to user process (ptnetmap) */ - nmk->attach_user = cfg->attach_user; + /* Run the context in a kernel thread. */ + snprintf(name, sizeof(name), "nmkth:%d:%ld", current->pid, + nmk->type); + nmk->worker = kthread_create(nm_kctx_worker, nmk, name); + if (IS_ERR(nmk->worker)) { + error = -PTR_ERR(nmk->worker); + goto err; + } - /* open event fds */ - error = nm_kthread_open_files(nmk, opaque); - if (error) - goto err; + if (nmk->affinity >= 0) { + kthread_bind(nmk->worker, nmk->affinity); + } + wake_up_process(nmk->worker); - nm_kthread_init_poll(nmk, &nmk->worker_ctx); + return 0; - return nmk; err: - //XXX: set errno? - kfree(nmk); - return NULL; -} - -int -nm_os_kthread_start(struct nm_kthread *nmk) -{ - int error = 0; - char name[16]; - - if (nmk->worker) { - return EBUSY; - } - - /* check if we want to attach kthread to user process */ - if (nmk->attach_user) { - nmk->mm = get_task_mm(current); - } - - /* ToDo Make this able to pass arbitrary string (e.g., for 'nm_') from nmk */ - snprintf(name, sizeof(name), "nmkth:%d:%ld", current->pid, - nmk->worker_ctx.type); - nmk->worker = kthread_create(nm_kthread_worker, nmk, name); - if (IS_ERR(nmk->worker)) { - error = -PTR_ERR(nmk->worker); - goto err; - } - - kthread_bind(nmk->worker, nmk->affinity); - wake_up_process(nmk->worker); - - if (nmk->worker_ctx.ioevent_file) { - error = nm_kthread_start_poll(&nmk->worker_ctx, - nmk->worker_ctx.ioevent_file); - if (error) { - goto err_kstop; + if (nmk->worker) { + kthread_stop(nmk->worker); + nmk->worker = NULL; } - } - - return 0; -err_kstop: - kthread_stop(nmk->worker); -err: - nmk->worker = NULL; - if (nmk->mm) - mmput(nmk->mm); - nmk->mm = NULL; - return error; + if (nmk->mm) { + mmput(nmk->mm); + nmk->mm = NULL; + } + return error; } void -nm_os_kthread_stop(struct nm_kthread *nmk) +nm_os_kctx_worker_stop(struct nm_kctx *nmk) { - if (!nmk->worker) { - return; - } - - nm_kthread_stop_poll(&nmk->worker_ctx); - - if (nmk->worker) { - kthread_stop(nmk->worker); - nmk->worker = NULL; - } + if (nmk->worker) { + kthread_stop(nmk->worker); + nmk->worker = NULL; + } - if (nmk->mm) { - mmput(nmk->mm); - nmk->mm = NULL; - } + if (nmk->mm) { + mmput(nmk->mm); + nmk->mm = NULL; + } } void -nm_os_kthread_delete(struct nm_kthread *nmk) +nm_os_kctx_destroy(struct nm_kctx *nmk) { - if (!nmk) - return; - - if (nmk->worker) { - nm_os_kthread_stop(nmk); - } + if (!nmk) + return; - nm_kthread_close_files(nmk); + if (nmk->worker) { + nm_os_kctx_worker_stop(nmk); + } - kfree(nmk); + kfree(nmk); } -/* ##################### PTNETMAP SUPPORT ##################### */ -#ifdef WITH_PTNETMAP_GUEST +/* ################## PTNETMAP GUEST SUPPORT ################## */ +#ifdef WITH_PTNETMAP /* * ptnetmap memory device (memdev) for linux guest * Used to expose host memory to the guest through PCI-BAR @@ -1637,6 +1898,7 @@ nm_os_kthread_delete(struct nm_kthread *nmk) int ptnet_probe(struct pci_dev *pdev, const struct pci_device_id *id); void ptnet_remove(struct pci_dev *pdev); +void ptnet_shutdown(struct pci_dev *pdev); /* * PCI Device ID Table @@ -1655,11 +1917,11 @@ MODULE_DEVICE_TABLE(pci, ptnetmap_guest_device_table); */ struct ptnetmap_memdev { - struct pci_dev *pdev; - void __iomem *pci_io; - void __iomem *pci_mem; - struct netmap_mem_d *nm_mem; - int bars; + struct pci_dev *pdev; + void __iomem *pci_io; + void __iomem *pci_mem; + struct netmap_mem_d *nm_mem; + int bars; }; /* @@ -1670,31 +1932,31 @@ struct ptnetmap_memdev */ int nm_os_pt_memdev_iomap(struct ptnetmap_memdev *ptn_dev, vm_paddr_t *nm_paddr, - void **nm_addr, uint64_t *mem_size) -{ - struct pci_dev *pdev = ptn_dev->pdev; - phys_addr_t mem_paddr; - int err = 0; - - *mem_size = ioread32(ptn_dev->pci_io + PTNET_MDEV_IO_MEMSIZE_HI); - *mem_size = ioread32(ptn_dev->pci_io + PTNET_MDEV_IO_MEMSIZE_LO) | - (*mem_size << 32); - - D("=== BAR %d start %llx len %llx mem_size %lx ===", - PTNETMAP_MEM_PCI_BAR, - pci_resource_start(pdev, PTNETMAP_MEM_PCI_BAR), - pci_resource_len(pdev, PTNETMAP_MEM_PCI_BAR), - (unsigned long)(*mem_size)); - - /* map memory allocator */ - mem_paddr = pci_resource_start(pdev, PTNETMAP_MEM_PCI_BAR); - ptn_dev->pci_mem = *nm_addr = ioremap_cache(mem_paddr, *mem_size); - if (ptn_dev->pci_mem == NULL) { - err = -ENOMEM; - } - *nm_paddr = mem_paddr; + void **nm_addr, uint64_t *mem_size) +{ + struct pci_dev *pdev = ptn_dev->pdev; + phys_addr_t mem_paddr; + int err = 0; + + *mem_size = ioread32(ptn_dev->pci_io + PTNET_MDEV_IO_MEMSIZE_HI); + *mem_size = ioread32(ptn_dev->pci_io + PTNET_MDEV_IO_MEMSIZE_LO) | + (*mem_size << 32); + + nm_prinf("=== BAR %d start %llx len %llx mem_size %lx ===", + PTNETMAP_MEM_PCI_BAR, + (unsigned long long)pci_resource_start(pdev, PTNETMAP_MEM_PCI_BAR), + (unsigned long long)pci_resource_len(pdev, PTNETMAP_MEM_PCI_BAR), + (unsigned long)(*mem_size)); + + /* map memory allocator */ + mem_paddr = pci_resource_start(pdev, PTNETMAP_MEM_PCI_BAR); + ptn_dev->pci_mem = *nm_addr = ioremap_cache(mem_paddr, *mem_size); + if (ptn_dev->pci_mem == NULL) { + err = -ENOMEM; + } + *nm_paddr = mem_paddr; - return err; + return err; } uint32_t @@ -1709,114 +1971,130 @@ nm_os_pt_memdev_ioread(struct ptnetmap_memdev *ptn_dev, unsigned int reg) void nm_os_pt_memdev_iounmap(struct ptnetmap_memdev *ptn_dev) { - if (ptn_dev->pci_mem) { - iounmap(ptn_dev->pci_mem); - ptn_dev->pci_mem = NULL; - } + if (ptn_dev->pci_mem) { + iounmap(ptn_dev->pci_mem); + ptn_dev->pci_mem = NULL; + } } /* - * Device Initialization Routine + * Device initialization routine * * Returns 0 on success, negative on failure */ static int ptnetmap_guest_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct ptnetmap_memdev *ptn_dev; - int bars, err; - uint16_t mem_id; - - if (id->device == PTNETMAP_PCI_NETIF_ID) { - /* Probe the ptnet device. */ - return ptnet_probe(pdev, id); - } - - /* Probe the memdev device. */ - - ptn_dev = kzalloc(sizeof(*ptn_dev), GFP_KERNEL); - if (ptn_dev == NULL) - return -ENOMEM; - - ptn_dev->pdev = pdev; - bars = pci_select_bars(pdev, IORESOURCE_MEM | IORESOURCE_IO); - /* enable the device */ - err = pci_enable_device(pdev); /* XXX-ste: device_mem() */ - if (err) - goto err; - - err = pci_request_selected_regions(pdev, bars, PTNETMAP_MEMDEV_NAME); - if (err) - goto err_pci_reg; - - ptn_dev->pci_io = pci_iomap(pdev, PTNETMAP_IO_PCI_BAR, 0); - if (ptn_dev->pci_io == NULL) { - err = -ENOMEM; - goto err_iomap; - } - pci_set_drvdata(pdev, ptn_dev); - pci_set_master(pdev); /* XXX-ste: is needed??? */ - - ptn_dev->bars = bars; - mem_id = ioread32(ptn_dev->pci_io + PTNET_MDEV_IO_MEMID); - - /* create guest allocator */ - ptn_dev->nm_mem = netmap_mem_pt_guest_attach(ptn_dev, mem_id); - if (ptn_dev->nm_mem == NULL) { - err = -ENOMEM; - goto err_nmd_attach; - } - netmap_mem_get(ptn_dev->nm_mem); - - return 0; + struct ptnetmap_memdev *ptn_dev; + int bars, err; + uint16_t mem_id; + + if (id->device == PTNETMAP_PCI_NETIF_ID) { + /* Probe the ptnet device. */ + return ptnet_probe(pdev, id); + } + + /* Probe the memdev device. */ + ptn_dev = kzalloc(sizeof(*ptn_dev), GFP_KERNEL); + if (ptn_dev == NULL) + return -ENOMEM; + + ptn_dev->pdev = pdev; + bars = pci_select_bars(pdev, IORESOURCE_MEM | IORESOURCE_IO); + /* enable the device */ + err = pci_enable_device(pdev); + if (err) + goto err; + + err = pci_request_selected_regions(pdev, bars, PTNETMAP_MEMDEV_NAME); + if (err) + goto err_pci_reg; + + ptn_dev->pci_io = pci_iomap(pdev, PTNETMAP_IO_PCI_BAR, 0); + if (ptn_dev->pci_io == NULL) { + err = -ENOMEM; + goto err_iomap; + } + pci_set_drvdata(pdev, ptn_dev); + pci_set_master(pdev); /* XXX probably not needed */ + + ptn_dev->bars = bars; + mem_id = ioread32(ptn_dev->pci_io + PTNET_MDEV_IO_MEMID); + + /* create guest allocator */ + ptn_dev->nm_mem = netmap_mem_pt_guest_attach(ptn_dev, mem_id); + if (ptn_dev->nm_mem == NULL) { + err = -ENOMEM; + goto err_nmd_attach; + } + netmap_mem_get(ptn_dev->nm_mem); + + return 0; err_nmd_attach: - pci_set_drvdata(pdev, NULL); - iounmap(ptn_dev->pci_io); + pci_set_drvdata(pdev, NULL); + iounmap(ptn_dev->pci_io); err_iomap: - pci_release_selected_regions(pdev, bars); + pci_release_selected_regions(pdev, bars); err_pci_reg: - pci_disable_device(pdev); + pci_disable_device(pdev); err: - kfree(ptn_dev); - return err; + kfree(ptn_dev); + return err; } /* - * Device Removal Routine + * Device removal routine. */ static void ptnetmap_guest_remove(struct pci_dev *pdev) { - struct ptnetmap_memdev *ptn_dev = pci_get_drvdata(pdev); + struct ptnetmap_memdev *ptn_dev = pci_get_drvdata(pdev); - if (pdev->device == PTNETMAP_PCI_NETIF_ID) { - /* Remove the ptnet device. */ - return ptnet_remove(pdev); - } + if (pdev->device == PTNETMAP_PCI_NETIF_ID) { + /* Remove the ptnet device. */ + return ptnet_remove(pdev); + } - /* Remove the memdev device. */ + /* Remove the memdev device. */ - if (ptn_dev->nm_mem) { - netmap_mem_put(ptn_dev->nm_mem); - ptn_dev->nm_mem = NULL; - } - nm_os_pt_memdev_iounmap(ptn_dev); - pci_set_drvdata(pdev, NULL); - iounmap(ptn_dev->pci_io); - pci_release_selected_regions(pdev, ptn_dev->bars); - pci_disable_device(pdev); - kfree(ptn_dev); + if (ptn_dev->nm_mem) { + netmap_mem_put(ptn_dev->nm_mem); + ptn_dev->nm_mem = NULL; + } + nm_os_pt_memdev_iounmap(ptn_dev); + pci_set_drvdata(pdev, NULL); + iounmap(ptn_dev->pci_io); + pci_release_selected_regions(pdev, ptn_dev->bars); + pci_disable_device(pdev); + kfree(ptn_dev); +} + +/* + * Device shutdown routine, called when the system is going to power + * off or reboot. + */ +static void +ptnetmap_guest_shutdown(struct pci_dev *pdev) +{ + if (pdev->device == PTNETMAP_PCI_NETIF_ID) { + /* Shutdown the ptnet device. */ + ptnet_shutdown(pdev); + } else if (pdev->device == PTNETMAP_PCI_DEVICE_ID) { + /* Shutdown the memdev device. */ + pci_disable_device(pdev); + } } /* * pci driver information */ static struct pci_driver ptnetmap_guest_drivers = { - .name = "ptnetmap-guest-drivers", - .id_table = ptnetmap_guest_device_table, - .probe = ptnetmap_guest_probe, - .remove = ptnetmap_guest_remove, + .name = "ptnetmap-guest-drivers", + .id_table = ptnetmap_guest_device_table, + .probe = ptnetmap_guest_probe, + .remove = ptnetmap_guest_remove, + .shutdown = ptnetmap_guest_shutdown, }; /* @@ -1827,31 +2105,32 @@ static struct pci_driver ptnetmap_guest_drivers = { static int ptnetmap_guest_init(void) { - int ret; + int ret; - /* register pci driver */ - ret = pci_register_driver(&ptnetmap_guest_drivers); - if (ret < 0) { - D("Failed to register drivers"); - return ret; - } - return 0; + /* register pci driver */ + ret = pci_register_driver(&ptnetmap_guest_drivers); + if (ret < 0) { + nm_prerr("Failed to register drivers"); + return ret; + } + + return 0; } /* * Driver Exit Cleanup Routine */ -void +static void ptnetmap_guest_fini(void) { - /* unregister pci driver */ - pci_unregister_driver(&ptnetmap_guest_drivers); + /* unregister pci driver */ + pci_unregister_driver(&ptnetmap_guest_drivers); } -#else /* !WITH_PTNETMAP_GUEST */ +#else /* !WITH_PTNETMAP */ #define ptnetmap_guest_init() 0 #define ptnetmap_guest_fini() -#endif /* WITH_PTNETMAP_GUEST */ +#endif /* WITH_PTNETMAP */ #ifdef WITH_SINK @@ -1871,8 +2150,16 @@ ptnetmap_guest_fini(void) */ static int sink_delay_ns = 100; module_param(sink_delay_ns, int, 0644); -static struct net_device *nm_sink_netdev = NULL; /* global sink netdev */ -s64 nm_sink_next_link_idle; /* for link emulation */ +#define NM_MAX_SINKS 100 +static int sink_num = 1; +static int sink_actual_num = 0; +module_param(sink_num, int, 0644); +static struct net_device **nm_sink_netdev = NULL; /* global sink netdev */ + +struct netmap_sink_adapter { + struct netmap_hw_adapter up; + s64 next_link_idle; +}; #define NM_SINK_SLOTS 1024 #define NM_SINK_DELAY_NS \ @@ -1881,36 +2168,41 @@ s64 nm_sink_next_link_idle; /* for link emulation */ static int nm_sink_register(struct netmap_adapter *na, int onoff) { + struct netmap_sink_adapter *sa = + (struct netmap_sink_adapter *)na; + if (onoff) nm_set_native_flags(na); else nm_clear_native_flags(na); - nm_sink_next_link_idle = ktime_get_ns(); + sa->next_link_idle = ktime_get_ns(); return 0; } static inline void -nm_sink_emu(unsigned int n) +nm_sink_emu(struct netmap_adapter *na, unsigned int n) { - u64 wait_until = nm_sink_next_link_idle; + struct netmap_sink_adapter *sa = + (struct netmap_sink_adapter *)na; + u64 wait_until = sa->next_link_idle; u64 now = ktime_get_ns(); - if (sink_delay_ns < 0 || nm_sink_next_link_idle < now) { + if (sink_delay_ns < 0 || sa->next_link_idle < now) { /* If we are emulating packet consumer mode or the link went * idle some time ago, we need to update the link emulation * variable, because we don't want the caller to accumulate * credit. */ - nm_sink_next_link_idle = now; + sa->next_link_idle = now; } /* Schedule new transmissions. */ - nm_sink_next_link_idle += n * NM_SINK_DELAY_NS; + sa->next_link_idle += n * NM_SINK_DELAY_NS; if (sink_delay_ns < 0) { /* In packet consumer mode we emulate synchronous * transmission, so we have to wait right now for the link * to become idle. */ - wait_until = nm_sink_next_link_idle; + wait_until = sa->next_link_idle; } while (ktime_get_ns() < wait_until) ; } @@ -1929,7 +2221,7 @@ nm_sink_txsync(struct netmap_kring *kring, int flags) kring->nr_hwcur = head; kring->nr_hwtail = nm_prev(kring->nr_hwcur, lim); - nm_sink_emu(n); + nm_sink_emu(kring->na, n); return 0; } @@ -1953,7 +2245,7 @@ static netdev_tx_t nm_sink_start_xmit(struct sk_buff *skb, struct net_device *netdev) { kfree_skb(skb); - nm_sink_emu(1); + nm_sink_emu(NA(netdev), 1); return NETDEV_TX_OK; } @@ -1963,51 +2255,78 @@ static const struct net_device_ops nm_sink_netdev_ops = { .ndo_start_xmit = nm_sink_start_xmit, }; -int +static int netmap_sink_init(void) { struct netmap_adapter na; struct net_device *netdev; - int err; + int i, err = 0; + + sink_actual_num = sink_num; - netdev = alloc_etherdev(0); - if (!netdev) { - return ENOMEM; + if (sink_actual_num > NM_MAX_SINKS) { + pr_err("too many netmap sink devices (max %d)", NM_MAX_SINKS); + return -EINVAL; } - netdev->netdev_ops = &nm_sink_netdev_ops ; - strncpy(netdev->name, "nmsink", sizeof(netdev->name) - 1); - netdev->features = NETIF_F_HIGHDMA; - strcpy(netdev->name, "nmsink%d"); - err = register_netdev(netdev); - if (err) { - free_netdev(netdev); + + nm_sink_netdev = nm_os_malloc(sizeof(struct netdev *) * sink_actual_num); + if (!nm_sink_netdev) { + return -ENOMEM; } - bzero(&na, sizeof(na)); - na.ifp = netdev; - na.num_tx_desc = NM_SINK_SLOTS; - na.num_rx_desc = NM_SINK_SLOTS; - na.nm_register = nm_sink_register; - na.nm_txsync = nm_sink_txsync; - na.nm_rxsync = nm_sink_rxsync; - na.num_tx_rings = na.num_rx_rings = 1; - netmap_attach(&na); + for (i = 0; i < sink_actual_num; i++) { + netdev = alloc_etherdev(0); + if (!netdev) { + err = -ENOMEM; + break; + } + netdev->netdev_ops = &nm_sink_netdev_ops ; + strlcpy(netdev->name, "nmsink%d", sizeof(netdev->name)); + netdev->features = NETIF_F_HIGHDMA; + err = register_netdev(netdev); + if (err) { + free_netdev(netdev); + break; + } - netif_carrier_on(netdev); - nm_sink_netdev = netdev; + bzero(&na, sizeof(na)); + na.ifp = netdev; + na.num_tx_desc = NM_SINK_SLOTS; + na.num_rx_desc = NM_SINK_SLOTS; + na.nm_register = nm_sink_register; + na.nm_txsync = nm_sink_txsync; + na.nm_rxsync = nm_sink_rxsync; + na.num_tx_rings = na.num_rx_rings = 1; + na.na_flags = NAF_OFFSETS | NAF_MOREFRAG; + if (netmap_attach_ext(&na, sizeof(struct netmap_sink_adapter), 1)) { + dev_err(&netdev->dev, "failed to attach netmap adapter"); + unregister_netdev(netdev); + free_netdev(netdev); + break; + } - return 0; + netif_carrier_on(netdev); + nm_sink_netdev[i] = netdev; + } + sink_actual_num = i; + + return err; } -void +static void netmap_sink_fini(void) { - struct net_device *netdev = nm_sink_netdev; + struct net_device *netdev; + int i; + for (i = 0; i < sink_actual_num; i++) { + netdev = nm_sink_netdev[i]; + unregister_netdev(netdev); + netmap_detach(netdev); + free_netdev(netdev); + } + kfree(nm_sink_netdev); nm_sink_netdev = NULL; - unregister_netdev(netdev); - netmap_detach(netdev); - free_netdev(netdev); } #endif /* WITH_SINK */ @@ -2032,25 +2351,48 @@ static int linux_netmap_init(void) err = ptnetmap_guest_init(); if (err) { - return err; + goto netmap_fini; } #ifdef WITH_SINK err = netmap_sink_init(); if (err) { - D("Warning: could not init netmap sink interface"); + nm_prerr("Error: could not init netmap sink interface"); + goto ptnetmap_fini; } #endif /* WITH_SINK */ +#ifdef WITH_GENERIC + err = register_qdisc(&generic_qdisc_ops); + if (err) { + nm_prerr("Error: failed to register qdisc for emulated netmap (err=%d)", err); + goto sink_fini; + } +#endif /* WITH_GENERIC */ return 0; + +#ifdef WITH_GENERIC +sink_fini: +#endif /* WITH_GENERIC */ +#ifdef WITH_SINK + netmap_sink_fini(); +ptnetmap_fini: +#endif /* WITH_SINK */ + ptnetmap_guest_fini(); +netmap_fini: + netmap_fini(); + return err; } static void linux_netmap_fini(void) { +#ifdef WITH_GENERIC + unregister_qdisc(&generic_qdisc_ops); +#endif /* WITH_GENERIC */ #ifdef WITH_SINK netmap_sink_fini(); #endif /* WITH_SINK */ - ptnetmap_guest_fini(); - netmap_fini(); + ptnetmap_guest_fini(); + netmap_fini(); } #ifndef NETMAP_LINUX_HAVE_LIVE_ADDR_CHANGE @@ -2082,33 +2424,46 @@ static int linux_nm_vi_xmit(struct sk_buff *skb, struct net_device *netdev) } #ifdef NETMAP_LINUX_HAVE_GET_STATS64 -static struct rtnl_link_stats64 *linux_nm_vi_get_stats( - struct net_device *netdev, - struct rtnl_link_stats64 *stats) +static +#ifdef NETMAP_LINUX_HAVE_NONVOID_GET_STATS64 +struct rtnl_link_stats64 * +#else /* !VOID */ +void +#endif /* NETMAP_LINUX_HAVE_NONVOID_GET_STATS64 */ +linux_nm_vi_get_stats(struct net_device *netdev, struct rtnl_link_stats64 *stats) { +#ifdef NETMAP_LINUX_HAVE_NONVOID_GET_STATS64 return stats; +#endif /* !NETMAP_LINUX_HAVE_VOID_GET_STATS64 */ } -#endif +#endif /* NETMAP_LINUX_HAVE_GET_STATS64 */ static int linux_nm_vi_change_mtu(struct net_device *netdev, int new_mtu) { return 0; } +#ifdef NETMAP_LINUX_HAVE_NETDEV_DTOR static void linux_nm_vi_destructor(struct net_device *netdev) { // netmap_detach(netdev); free_netdev(netdev); } +#endif static const struct net_device_ops nm_vi_ops = { .ndo_open = linux_nm_vi_open, .ndo_stop = linux_nm_vi_stop, .ndo_start_xmit = linux_nm_vi_xmit, .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = linux_nm_vi_change_mtu, + .NETMAP_LINUX_CHANGE_MTU = linux_nm_vi_change_mtu, #ifdef NETMAP_LINUX_HAVE_GET_STATS64 .ndo_get_stats64 = linux_nm_vi_get_stats, #endif }; +#ifdef NETMAP_LINUX_HAVE_NETIF_F_LLTX +#define linux_nm_set_lltx(dev_) do { (dev_)->features |= NETIF_F_LLTX; } while (0) +#else /* !NETMAP_LINUX_HAVE_NETIF_F_LLTX */ +#define linux_nm_set_lltx(dev_) do { (dev_)->lltx = true; } while (0) +#endif /* NETMAP_LINUX_HAVE_NETIF_F_LLTX */ /* dev->name is not initialized yet */ static void linux_nm_vi_setup(struct ifnet *dev) @@ -2117,13 +2472,18 @@ linux_nm_vi_setup(struct ifnet *dev) dev->netdev_ops = &nm_vi_ops; dev->priv_flags &= ~IFF_TX_SKB_SHARING; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; +#ifdef NETMAP_LINUX_HAVE_NETDEV_DTOR dev->destructor = linux_nm_vi_destructor; +#else + dev->needs_free_netdev = 1; +#endif dev->tx_queue_len = 0; /* XXX */ - dev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST | + dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_TSO; + linux_nm_set_lltx(dev); #ifdef NETMAP_LINUX_HAVE_HW_FEATURES - dev->hw_features = dev->features & ~NETIF_F_LLTX; + dev->hw_features = dev->features; #endif #ifdef NETMAP_LINUX_HAVE_ADDR_RANDOM eth_hw_addr_random(dev); @@ -2147,12 +2507,13 @@ nm_os_vi_persist(const char *name, struct ifnet **ret) error = ENOMEM; goto err_put; } - dev_net_set(ifp, &init_net); - ifp->features |= NETIF_F_NETNS_LOCAL; /* just for safety */ +#ifdef CONFIG_NET_NS + dev_net_set(ifp, current->nsproxy->net_ns); +#endif ifp->dev.driver = &linux_dummy_drv; error = register_netdev(ifp); if (error < 0) { - D("error %d", error); + nm_prerr("error %d", error); error = -error; goto err_free; } @@ -2189,38 +2550,125 @@ nm_os_selrecord(NM_SELRECORD_T *sr, NM_SELINFO_T *si) poll_wait(sr->file, si, sr->pwait); } +void +nm_os_onattach(struct ifnet *ifp) +{ + struct netmap_adapter *na = NA(ifp); + struct netmap_hw_adapter *hwna = (struct netmap_hw_adapter *)na; + +#ifdef NETMAP_LINUX_HAVE_NETDEV_OPS + if (ifp->netdev_ops) { + /* prepare a clone of the netdev ops */ + hwna->nm_ndo = *ifp->netdev_ops; + } +#endif /* NETMAP_LINUX_HAVE_NETDEV_OPS */ + hwna->nm_ndo.ndo_start_xmit = linux_netmap_start_xmit; + hwna->nm_ndo.NETMAP_LINUX_CHANGE_MTU = linux_netmap_change_mtu; +#ifdef NETMAP_LINUX_HAVE_AX25PTR + if (ifp->ethtool_ops) { + hwna->nm_eto = *ifp->ethtool_ops; + } + hwna->nm_eto.set_ringparam = linux_netmap_set_ringparam; +#ifdef NETMAP_LINUX_HAVE_SET_CHANNELS + hwna->nm_eto.set_channels = linux_netmap_set_channels; +#endif /* NETMAP_LINUX_HAVE_SET_CHANNELS */ +#else /* !NETMAP_LINUX_HAVE_AX25PTR */ +#ifdef NETMAP_LINUX_HAVE_SET_CHANNELS + na->magic.eto.set_channels = linux_netmap_set_channels; +#endif /* NETMAP_LINUX_HAVE_SET_CHANNELS */ +#endif /* NETMAP_LINUX_HAVE_AX25PTR */ + if (na->nm_config == NULL) { + hwna->up.nm_config = nm_os_config; + } +} + +void +nm_os_onenter(struct ifnet *ifp) +{ + struct netmap_adapter *na = NA(ifp); + struct netmap_hw_adapter *hwna = (struct netmap_hw_adapter *)na; + + na->if_transmit = (void *)ifp->netdev_ops; + ifp->netdev_ops = &hwna->nm_ndo; +#ifdef NETMAP_LINUX_HAVE_AX25PTR + hwna->save_ethtool = ifp->ethtool_ops; + ifp->ethtool_ops = &hwna->nm_eto; +#else /* NETMAP_LINUX_HAVE_AX25PTR */ + (void)hwna; +#endif /* NETMAP_LINUX_HAVE_AX25PTR */ +} + +void +nm_os_onexit(struct ifnet *ifp) +{ + struct netmap_adapter *na = NA(ifp); + struct netmap_hw_adapter *hwna = (struct netmap_hw_adapter *)na; + + ifp->netdev_ops = (void *)na->if_transmit; +#ifdef NETMAP_LINUX_HAVE_AX25PTR + ifp->ethtool_ops = hwna->save_ethtool; +#else /* NETMAP_LINUX_HAVE_AX25PTR */ + (void)hwna; +#endif /* NETMAP_LINUX_HAVE_AX25PTR */ +} + module_init(linux_netmap_init); module_exit(linux_netmap_fini); /* export certain symbols to other modules */ EXPORT_SYMBOL(netmap_attach); /* driver attach routines */ -#ifdef WITH_PTNETMAP_GUEST +EXPORT_SYMBOL(netmap_attach_ext); +#ifdef NM_DEBUG_PUTGET +EXPORT_SYMBOL(__netmap_adapter_get); +EXPORT_SYMBOL(__netmap_adapter_put); +#else +EXPORT_SYMBOL(netmap_adapter_get); +EXPORT_SYMBOL(netmap_adapter_put); +#endif /* NM_DEBUG_PUTGET */ +#ifdef WITH_PTNETMAP EXPORT_SYMBOL(netmap_pt_guest_attach); /* ptnetmap driver attach routine */ EXPORT_SYMBOL(netmap_pt_guest_rxsync); /* ptnetmap generic rxsync */ EXPORT_SYMBOL(netmap_pt_guest_txsync); /* ptnetmap generic txsync */ EXPORT_SYMBOL(netmap_mem_pt_guest_ifp_del); /* unlink passthrough interface */ -#endif /* WITH_PTNETMAP_GUEST */ +#endif /* WITH_PTNETMAP */ EXPORT_SYMBOL(netmap_detach); /* driver detach routines */ EXPORT_SYMBOL(netmap_ring_reinit); /* ring init on error */ EXPORT_SYMBOL(netmap_reset); /* ring init routines */ EXPORT_SYMBOL(netmap_rx_irq); /* default irq handler */ EXPORT_SYMBOL(netmap_no_pendintr); /* XXX mitigation - should go away */ +EXPORT_SYMBOL(netmap_krings_mode_commit); #ifdef WITH_VALE -EXPORT_SYMBOL(netmap_bdg_ctl); /* bridge configuration routine */ -EXPORT_SYMBOL(netmap_bdg_learning); /* the default lookup function */ +EXPORT_SYMBOL(netmap_bdg_regops); /* bridge configuration routine */ EXPORT_SYMBOL(netmap_bdg_name); /* the bridge the vp is attached to */ +EXPORT_SYMBOL(netmap_bdg_update_private_data); +EXPORT_SYMBOL(netmap_vale_create); +EXPORT_SYMBOL(netmap_vale_destroy); +EXPORT_SYMBOL(netmap_bdg_attach); +EXPORT_SYMBOL(netmap_bdg_detach); +EXPORT_SYMBOL(nm_vi_create); +EXPORT_SYMBOL(nm_vi_destroy); #endif /* WITH_VALE */ EXPORT_SYMBOL(netmap_disable_all_rings); EXPORT_SYMBOL(netmap_enable_all_rings); EXPORT_SYMBOL(netmap_krings_create); EXPORT_SYMBOL(netmap_krings_delete); /* used by veth module */ +EXPORT_SYMBOL(netmap_hw_krings_create); +EXPORT_SYMBOL(netmap_hw_krings_delete); EXPORT_SYMBOL(netmap_mem_rings_create); /* used by veth module */ EXPORT_SYMBOL(netmap_mem_rings_delete); /* used by veth module */ #ifdef WITH_PIPES EXPORT_SYMBOL(netmap_pipe_txsync); /* used by veth module */ EXPORT_SYMBOL(netmap_pipe_rxsync); /* used by veth module */ +EXPORT_SYMBOL(netmap_pipe_krings_create_both); +EXPORT_SYMBOL(netmap_pipe_krings_delete_both); +EXPORT_SYMBOL(netmap_pipe_reg_both); #endif /* WITH_PIPES */ EXPORT_SYMBOL(netmap_verbose); +EXPORT_SYMBOL(nm_set_native_flags); +EXPORT_SYMBOL(nm_clear_native_flags); +#ifndef NETMAP_LINUX_HAVE_AX25PTR +EXPORT_SYMBOL(linux_netmap_set_ringparam); +#endif /* NETMAP_LINUX_HAVE_AX25PTR */ MODULE_AUTHOR("http://info.iet.unipi.it/~luigi/netmap/"); MODULE_DESCRIPTION("The netmap packet I/O framework"); diff --git a/LINUX/netmap_ptnet.c b/LINUX/netmap_ptnet.c index 792361813..ac4ffef6e 100644 --- a/LINUX/netmap_ptnet.c +++ b/LINUX/netmap_ptnet.c @@ -41,13 +41,17 @@ extern int ptnet_vnet_hdr; static bool ptnet_gso = true; module_param(ptnet_gso, bool, 0644); +int ptnet_probe(struct pci_dev *pdev, const struct pci_device_id *id); +void ptnet_remove(struct pci_dev *pdev); +void ptnet_shutdown(struct pci_dev *pdev); + /* Enable to debug RX-side hangs */ //#define HANGCTRL #if 0 /* Switch to 1 to enable per-packet logs. */ -#define DBG D +#define DBG nm_prinf #else -#define DBG ND +#define DBG nm_prdis #endif #define PTNET_DRV_NAME "ptnet" @@ -57,13 +61,13 @@ struct ptnet_info; /* Per-ring data structure. */ struct ptnet_queue { struct ptnet_info *pi; - struct ptnet_ring *ptring; + struct nm_csb_atok *atok; + struct nm_csb_ktoa *ktoa; int kring_id; u8* __iomem kick; /* MSI-X interrupt data structures. */ char msix_name[64]; - cpumask_var_t msix_affinity_mask; }; struct ptnet_rx_queue { @@ -94,7 +98,9 @@ struct ptnet_info { #endif /* !PTNET_CSB_ALLOC */ /* MSI-X interrupt data structures. */ +#ifdef NETMAP_LINUX_HAVE_PCI_ENABLE_MSIX struct msix_entry *msix_entries; +#endif int num_rings; int num_tx_rings; @@ -103,7 +109,9 @@ struct ptnet_info { /* CSB memory to be used for producer/consumer state * synchronization. */ - struct ptnet_csb *csb; + struct page *csb_pages; + struct nm_csb_atok *csb_gh; + struct nm_csb_ktoa *csb_hg; int min_tx_slots; @@ -125,9 +133,10 @@ hang_tmr_callback(unsigned long arg) struct netmap_kring *kring = na->rx_rings + prq->q.kring_id; struct netmap_ring *ring = kring->ring; - pr_info("HANG RX#%d: hwc %u h %u c %u hwt %u t %u rx.guest_need_kick %u\n", + pr_info("PTNET HANG RX#%d: hwc %u h %u c %u hwt %u t %u" + " rx.appl_need_kick %u\n", kring->ring_id, kring->nr_hwcur, ring->head, ring->cur, - kring->nr_hwtail, ring->tail, prq->q.ptring->guest_need_kick); + kring->nr_hwtail, ring->tail, prq->q.atok->appl_need_kick); if (mod_timer(&prq->hang_timer, jiffies + msecs_to_jiffies(HANG_INTVAL_MS))) { @@ -136,18 +145,6 @@ hang_tmr_callback(unsigned long arg) } #endif -static inline void -ptnet_sync_tail(struct ptnet_ring *ptring, struct netmap_kring *kring) -{ - struct netmap_ring *ring = kring->ring; - - /* Update hwcur and hwtail as known by the host. */ - ptnetmap_guest_read_kring_csb(ptring, kring); - - /* nm_sync_finalize */ - ring->tail = kring->rtail = kring->nr_hwtail; -} - static inline int ptnet_tx_slots(struct netmap_ring *ring) { @@ -210,13 +207,14 @@ ptnet_start_xmit(struct sk_buff *skb, struct net_device *netdev) int nfrags = skb_shinfo(skb)->nr_frags; int queue_idx = skb_get_queue_mapping(skb); struct ptnet_queue *pq = pi->queues[queue_idx]; - struct ptnet_ring *ptring = pq->ptring; + struct nm_csb_atok *atok = pq->atok; + struct nm_csb_ktoa *ktoa = pq->ktoa; struct netmap_kring *kring; struct xmit_copy_args a; int f; a.na = &pi->ptna->dr.up; - kring = &a.na->tx_rings[queue_idx]; + kring = a.na->tx_rings[queue_idx]; a.ring = kring->ring; a.lim = kring->nkr_num_slots - 1; @@ -224,10 +222,10 @@ ptnet_start_xmit(struct sk_buff *skb, struct net_device *netdev) /* Update hwcur and hwtail (completed TX slots) as known by the host, * by reading from CSB. */ - ptnet_sync_tail(ptring, kring); + ptnet_sync_tail(ktoa, kring); if (unlikely(ptnet_tx_slots(a.ring) < pi->min_tx_slots)) { - ND(1, "TX ring unexpected overflow, requeuing"); + nm_prdis(1, "TX ring unexpected overflow, requeuing"); return NETDEV_TX_BUSY; } @@ -257,8 +255,10 @@ ptnet_start_xmit(struct sk_buff *skb, struct net_device *netdev) vh->hdr.gso_size = skb_shinfo(skb)->gso_size; if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { vh->hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV4; +#ifdef NETMAP_LINUX_HAVE_UFO } else if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP) { vh->hdr.gso_type = VIRTIO_NET_HDR_GSO_UDP; +#endif /* NETMAP_LINUX_HAVE_UFO */ } else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) { vh->hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV6; } @@ -274,7 +274,7 @@ ptnet_start_xmit(struct sk_buff *skb, struct net_device *netdev) vh->num_buffers = 0; /* unused */ - ND(1, "%s: vnet hdr: flags %x csum_start %u csum_ofs %u hdr_len = " + nm_prdis(1, "%s: vnet hdr: flags %x csum_start %u csum_ofs %u hdr_len = " "%u gso_size %u gso_type %x", __func__, vh->hdr.flags, vh->hdr.csum_start, vh->hdr.csum_offset, vh->hdr.hdr_len, vh->hdr.gso_size, vh->hdr.gso_type); @@ -288,7 +288,7 @@ ptnet_start_xmit(struct sk_buff *skb, struct net_device *netdev) /* Third step: Copy in the sk_buffs frags. */ for (f = 0; f < nfrags; f++) { - const struct skb_frag_struct *frag; + const skb_frag_t *frag; frag = &skb_shinfo(skb)->frags[f]; ptnet_copy_to_ring(&a, skb_frag_address(frag), @@ -301,7 +301,7 @@ ptnet_start_xmit(struct sk_buff *skb, struct net_device *netdev) a.ring->head = a.ring->cur = nm_next(a.head, a.lim); if (skb_shinfo(skb)->nr_frags) { - ND(1, "TX frags #%u lfsz %u tsz %d gso_segs %d gso_size %d", skb_shinfo(skb)->nr_frags, + nm_prdis(1, "TX frags #%u lfsz %u tsz %d gso_segs %d gso_size %d", skb_shinfo(skb)->nr_frags, skb_frag_size(&skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags-1]), (int)skb->len, skb_shinfo(skb)->gso_segs, skb_shinfo(skb)->gso_size); } @@ -315,28 +315,30 @@ ptnet_start_xmit(struct sk_buff *skb, struct net_device *netdev) if (!XMIT_MORE(skb)) { /* Tell the host to process the new packets, updating cur and * head in the CSB. */ - ptnetmap_guest_write_kring_csb(ptring, kring->rcur, - kring->rhead); + nm_sync_kloop_appl_write(atok, kring->rcur, kring->rhead); } - /* Ask for a kick from a guest to the host if needed. */ - if (NM_ACCESS_ONCE(ptring->host_need_kick)) { - ptring->sync_flags = NAF_FORCE_RECLAIM; + /* Ask for a kick from a guest to the host if needed. */ + if (NM_ACCESS_ONCE(ktoa->kern_need_kick)) { + atok->sync_flags = NAF_FORCE_RECLAIM; iowrite32(0, pq->kick); } - /* No more TX slots for further transmissions. We have to stop the + /* No more TX slots for further transmissions. We have to stop the * qdisc layer and enable notifications. */ if (ptnet_tx_slots(a.ring) < pi->min_tx_slots) { netif_stop_subqueue(netdev, pq->kring_id); - ptring->guest_need_kick = 1; + atok->appl_need_kick = 1; - /* Double check. */ - ptnet_sync_tail(ptring, kring); + /* Double check. We need a full barrier to prevent the store + * to atok->appl_need_kick to be reordered with the load from + * ktoa->hwcur and ktoa->hwtail (store-load barrier). */ + nm_stld_barrier(); + ptnet_sync_tail(ktoa, kring); if (unlikely(ptnet_tx_slots(a.ring) >= pi->min_tx_slots)) { /* More TX space came in the meanwhile. */ netif_start_subqueue(netdev, pq->kring_id); - ptring->guest_need_kick = 0; + atok->appl_need_kick = 0; } } @@ -368,8 +370,8 @@ ptnet_get_stats(struct net_device *netdev) static int ptnet_change_mtu(struct net_device *netdev, int new_mtu) { - pr_info("%s changing MTU from %d to %d\n", - netdev->name, netdev->mtu, new_mtu); + pr_info("%s: %s changing MTU from %d to %d\n", + __func__, netdev->name, netdev->mtu, new_mtu); netdev->mtu = new_mtu; return 0; @@ -404,13 +406,13 @@ ptnet_napi_schedule(struct ptnet_queue *pq) /* Disable RX interrupts and schedule NAPI. */ if (likely(napi_schedule_prep(&prq->napi))) { - /* It's good thing to reset rx.guest_need_kick as soon as + /* It's good thing to reset rx.appl_need_kick as soon as * possible. */ - pq->ptring->guest_need_kick = 0; + pq->atok->appl_need_kick = 0; __napi_schedule(&prq->napi); } else { /* NAPI is already scheduled and we are ok with it. */ - pq->ptring->guest_need_kick = 1; + pq->atok->appl_need_kick = 1; } } @@ -467,10 +469,11 @@ ptnet_rx_poll(struct napi_struct *napi, int budget) struct ptnet_rx_queue *prq = container_of(napi, struct ptnet_rx_queue, napi); struct ptnet_queue *pq = (struct ptnet_queue *)prq; - struct ptnet_ring *ptring = pq->ptring; + struct nm_csb_atok *atok = pq->atok; + struct nm_csb_ktoa *ktoa = pq->ktoa; struct ptnet_info *pi = pq->pi; struct netmap_adapter *na = &pi->ptna->dr.up; - struct netmap_kring *kring = &na->rx_rings[pq->kring_id]; + struct netmap_kring *kring = na->rx_rings[pq->kring_id]; struct netmap_ring *ring = kring->ring; unsigned int const lim = kring->nkr_num_slots - 1; bool have_vnet_hdr = pi->vnet_hdr_len; @@ -494,7 +497,7 @@ ptnet_rx_poll(struct napi_struct *napi, int budget) /* Update hwtail, rtail, tail and hwcur to what is known from the host, * reading from CSB. */ - ptnet_sync_tail(ptring, kring); + ptnet_sync_tail(ktoa, kring); kring->nr_kflags &= ~NKR_PENDINTR; @@ -518,7 +521,7 @@ ptnet_rx_poll(struct napi_struct *napi, int budget) vh = nmbuf; if (likely(have_vnet_hdr)) { - ND(1, "%s: vnet hdr: flags %x csum_start %u " + nm_prdis(1, "%s: vnet hdr: flags %x csum_start %u " "csum_ofs %u hdr_len = %u gso_size %u " "gso_type %x", __func__, vh->hdr.flags, vh->hdr.csum_start, vh->hdr.csum_offset, @@ -537,7 +540,7 @@ ptnet_rx_poll(struct napi_struct *napi, int budget) #endif if (unlikely(!skb)) { pr_err("%s: skb allocation failed\n", - __func__); + __func__); break; } @@ -547,7 +550,7 @@ ptnet_rx_poll(struct napi_struct *napi, int budget) head = nm_next(head, lim); nns++; if (unlikely(head == ring->tail)) { - ND(1, "Warning: truncated packet, retrying"); + nm_prdis(1, "Warning: truncated packet, retrying"); dev_kfree_skb_any(skb); work_done ++; pi->netdev->stats.rx_frame_errors ++; @@ -563,7 +566,7 @@ ptnet_rx_poll(struct napi_struct *napi, int budget) do { if (!skbdata_avail) { if (skbpage) { - ND(1, "add f #%u fsz %lu tsz %d", skb_shinfo(skb)->nr_frags, + nm_prdis(1, "add f #%u fsz %lu tsz %d", skb_shinfo(skb)->nr_frags, PAGE_SIZE - skbdata_avail, (int)skb->len); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, skbpage, 0, PAGE_SIZE - skbdata_avail @@ -576,7 +579,7 @@ ptnet_rx_poll(struct napi_struct *napi, int budget) skbpage = ptnet_alloc_page(prq); if (unlikely(!skbpage)) { pr_err("%s: pntet_alloc_page() failed\n", - __func__); + __func__); break; } skbdata = page_address(skbpage); @@ -600,7 +603,7 @@ ptnet_rx_poll(struct napi_struct *napi, int budget) , PAGE_SIZE #endif ); - ND(1, "RX frags #%u lfsz %lu tsz %d nns %d", + nm_prdis(1, "RX frags #%u lfsz %lu tsz %d nns %d", skb_shinfo(skb)->nr_frags, PAGE_SIZE - skbdata_avail, (int)skb->len, nns); } @@ -633,10 +636,11 @@ ptnet_rx_poll(struct napi_struct *napi, int budget) case VIRTIO_NET_HDR_GSO_TCPV4: skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; break; - +#ifdef NETMAP_LINUX_HAVE_UFO case VIRTIO_NET_HDR_GSO_UDP: skb_shinfo(skb)->gso_type = SKB_GSO_UDP; break; +#endif /* NETMAP_LINUX_HAVE_UFO */ case VIRTIO_NET_HDR_GSO_TCPV6: skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; @@ -690,20 +694,24 @@ ptnet_rx_poll(struct napi_struct *napi, int budget) /* Budget was not fully consumed, since we have no more * completed RX slots. We can enable notifications and * exit polling mode. */ - ptring->guest_need_kick = 1; + atok->appl_need_kick = 1; #ifdef NETMAP_LINUX_HAVE_NAPI_COMPLETE_DONE napi_complete_done(napi, work_done); #else napi_complete(napi); #endif - /* Double check for more completed RX slots. */ - ptnet_sync_tail(ptring, kring); + /* Double check for more completed RX slots. + * We need a full barrier to prevent the store to + * atok->appl_need_kick to be reordered with the load from + * ktoa->hwcur and ktoa->hwtail (store-load barrier). */ + nm_stld_barrier(); + ptnet_sync_tail(ktoa, kring); if (head != ring->tail) { /* If there is more work to do, disable notifications * and reschedule. */ ptnet_napi_schedule(pq); - } + } #ifdef HANGCTRL if (mod_timer(&prq->hang_timer, jiffies + msecs_to_jiffies(HANG_INTVAL_MS))) { @@ -719,11 +727,10 @@ ptnet_rx_poll(struct napi_struct *napi, int budget) ring->head = ring->cur = head; kring->rcur = ring->cur; kring->rhead = ring->head; - ptnetmap_guest_write_kring_csb(ptring, kring->rcur, - kring->rhead); + nm_sync_kloop_appl_write(atok, kring->rcur, kring->rhead); /* Kick the host if needed. */ - if (NM_ACCESS_ONCE(ptring->host_need_kick)) { - ptring->sync_flags = NAF_FORCE_READ; + if (NM_ACCESS_ONCE(ktoa->kern_need_kick)) { + atok->sync_flags = NAF_FORCE_READ; iowrite32(0, pq->kick); } } @@ -751,6 +758,17 @@ ptnet_netpoll(struct net_device *netdev) } #endif + +static unsigned int +ptnet_get_irq_vector(struct ptnet_info *pi, unsigned int i) +{ +#ifdef NETMAP_LINUX_HAVE_PCI_ENABLE_MSIX + return pi->msix_entries[i].vector; +#else + return pci_irq_vector(pi->pdev, i); +#endif +} + static int ptnet_irqs_init(struct ptnet_info *pi) { @@ -758,59 +776,62 @@ ptnet_irqs_init(struct ptnet_info *pi) int i; /* Allocate the MSI-X interrupt vectors we need. */ +#ifdef NETMAP_LINUX_HAVE_PCI_ENABLE_MSIX pi->msix_entries = kzalloc(sizeof(*pi->msix_entries) * pi->num_rings, GFP_KERNEL); if (!pi->msix_entries) { - pr_err("Failed to allocate msix entires\n"); + pr_err("%s: Failed to allocate msix entries\n", __func__); return -ENOMEM; } for (i=0; inum_rings; i++) { - struct ptnet_queue *pq = pi->queues[i]; - - memset(&pq->msix_affinity_mask, 0, sizeof(pq->msix_affinity_mask)); - if (!alloc_cpumask_var(&pq->msix_affinity_mask, GFP_KERNEL)) { - pr_err("Failed to alloc cpumask var\n"); - goto err_masks; - } pi->msix_entries[i].entry = i; } ret = pci_enable_msix(pi->pdev, pi->msix_entries, pi->num_rings); - if (ret) { - pr_err("Failed to enable msix vectors (%d)\n", ret); - goto err_masks; + if (ret == 0) { /* ok */ + ret = pi->num_rings; + } +#else + ret = pci_alloc_irq_vectors(pi->pdev, pi->num_rings, pi->num_rings, + PCI_IRQ_MSIX); +#endif + if (ret != pi->num_rings) { + pr_err("%s: Failed to enable msix vectors (%d)\n", + __func__, ret); + goto err_alloc; } for (i=0; inum_rings; i++) { struct ptnet_queue *pq = pi->queues[i]; irq_handler_t handler = (i < pi->num_tx_rings) ? ptnet_tx_intr : ptnet_rx_intr; + unsigned int vector = ptnet_get_irq_vector(pi, i); snprintf(pq->msix_name, sizeof(pq->msix_name), - "ptnet-%d", i); - ret = request_irq(pi->msix_entries[i].vector, handler, - 0, pq->msix_name, pq); + "%s-%d", pi->netdev->name, i); + ret = request_irq(vector, handler, 0, pq->msix_name, pq); if (ret) { - pr_err("Unable to allocate interrupt (%d)\n", ret); + pr_err("%s: Unable to allocate interrupt (%d)\n", + __func__, ret); goto err_irqs; } - pr_info("IRQ for ring #%d --> %u, handler %p\n", i, - pi->msix_entries[i].vector, handler); + pr_info("%s: IRQ for ring #%d --> %u\n", __func__, i, vector); } return 0; err_irqs: - for (; i>=0; i--) { - free_irq(pi->msix_entries[i].vector, pi->queues[i]); + for (i--; i>=0; i--) { + free_irq(ptnet_get_irq_vector(pi, i), pi->queues[i]); } i = pi->num_rings-1; -err_masks: - for (; i>=0; i--) { - free_cpumask_var(pi->queues[i]->msix_affinity_mask); - } - +err_alloc: +#ifdef NETMAP_LINUX_HAVE_PCI_ENABLE_MSIX + kfree(pi->msix_entries); +#else + pci_free_irq_vectors(pi->pdev); +#endif return ret; } @@ -822,13 +843,14 @@ ptnet_irqs_fini(struct ptnet_info *pi) for (i=0; inum_rings; i++) { struct ptnet_queue *pq = pi->queues[i]; - free_irq(pi->msix_entries[i].vector, pq); - if (pq->msix_affinity_mask) { - free_cpumask_var(pq->msix_affinity_mask); - } + free_irq(ptnet_get_irq_vector(pi, i), pq); } +#ifdef NETMAP_LINUX_HAVE_PCI_ENABLE_MSIX pci_disable_msix(pi->pdev); kfree(pi->msix_entries); +#else + pci_free_irq_vectors(pi->pdev); +#endif } static int ptnet_nm_register(struct netmap_adapter *na, int onoff); @@ -847,32 +869,32 @@ ptnet_open(struct net_device *netdev) int ret; int i; - D("%s: netif_running %u", __func__, netif_running(netdev)); - netmap_update_config(na_dr); ret = netmap_mem_finalize(na_dr->nm_mem, na_dr); if (ret) { - pr_err("netmap_mem_finalize() failed\n"); + pr_err("%s: netmap_mem_finalize() failed\n", __func__); goto err_mem_finalize; } - if (pi->ptna->backend_regifs == 0) { + if (pi->ptna->backend_users == 0) { ret = ptnet_nm_krings_create(na_nm); if (ret) { - pr_err("ptnet_nm_krings_create() failed\n"); + pr_err("%s: ptnet_nm_krings_create() failed\n", + __func__); goto err_mem_finalize; } ret = netmap_mem_rings_create(na_dr); if (ret) { - pr_err("netmap_mem_rings_create() failed\n"); + pr_err("%s: netmap_mem_rings_create() failed\n", + __func__); goto err_rings_create; } ret = netmap_mem_get_lut(na_dr->nm_mem, &na_dr->na_lut); if (ret) { - pr_err("netmap_mem_get_lut() failed\n"); + pr_err("%s: netmap_mem_get_lut() failed\n", __func__); goto err_get_lut; } } @@ -892,8 +914,6 @@ ptnet_open(struct net_device *netdev) netif_tx_start_all_queues(netdev); - pr_info("%s: %p\n", __func__, pi); - for (i = 0; i < na_dr->num_rx_rings; i++){ struct ptnet_rx_queue *prq = (struct ptnet_rx_queue *) pi->rxqueues[i]; @@ -913,7 +933,8 @@ ptnet_open(struct net_device *netdev) * the interface was down. Schedule NAPI to flush packets that * are pending in the RX ring. We won't receive further * interrupts until the pending ones will be processed. */ - D("Schedule NAPI to flush RX ring #%d", i); + pr_info("%s: Schedule NAPI to flush RX ring #%d\n", + __func__, i); ptnet_napi_schedule(&prq->q); } @@ -944,8 +965,6 @@ ptnet_close(struct net_device *netdev) struct netmap_adapter *na_nm = &pi->ptna->hwup.up; int i; - D("%s: netif_running %u", __func__, netif_running(netdev)); - netif_tx_stop_all_queues(netdev); for (i = 0; i < na_dr->num_rx_rings; i++){ @@ -972,23 +991,21 @@ ptnet_close(struct net_device *netdev) ptnet_nm_register(na_dr, 0 /* off */); - if (pi->ptna->backend_regifs == 0) { + if (pi->ptna->backend_users == 0) { netmap_mem_rings_delete(na_dr); ptnet_nm_krings_delete(na_nm); } netmap_mem_deref(na_dr->nm_mem, na_dr); - pr_info("%s: %p\n", __func__, pi); - return 0; } static const struct net_device_ops ptnet_netdev_ops = { - .ndo_open = ptnet_open, - .ndo_stop = ptnet_close, - .ndo_start_xmit = ptnet_start_xmit, - .ndo_get_stats = ptnet_get_stats, - .ndo_change_mtu = ptnet_change_mtu, + .ndo_open = ptnet_open, + .ndo_stop = ptnet_close, + .ndo_start_xmit = ptnet_start_xmit, + .ndo_get_stats = ptnet_get_stats, + .NETMAP_LINUX_CHANGE_MTU = ptnet_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ptnet_netpoll, #endif @@ -996,10 +1013,8 @@ static const struct net_device_ops ptnet_netdev_ops = { static uint32_t -ptnet_nm_ptctl(struct net_device *netdev, uint32_t cmd) +ptnet_nm_ptctl(struct ptnet_info *pi, uint32_t cmd) { - struct ptnet_info *pi = netdev_priv(netdev); - /* Write a command and read back error status, * with zero meaning success. */ iowrite32(cmd, pi->ioaddr + PTNET_IO_PTCTL); @@ -1014,25 +1029,26 @@ ptnet_sync_from_csb(struct ptnet_info *pi, struct netmap_adapter *na) /* Sync krings from the host, reading from * CSB. */ for (i = 0; i < pi->num_rings; i++) { - struct ptnet_ring *ptring = pi->queues[i]->ptring; + struct nm_csb_atok *atok = pi->queues[i]->atok; + struct nm_csb_ktoa *ktoa = pi->queues[i]->ktoa; struct netmap_kring *kring; if (i < na->num_tx_rings) { - kring = na->tx_rings + i; + kring = na->tx_rings[i]; } else { - kring = na->rx_rings + i - na->num_tx_rings; + kring = na->rx_rings[i - na->num_tx_rings]; } - kring->rhead = kring->ring->head = ptring->head; - kring->rcur = kring->ring->cur = ptring->cur; - kring->nr_hwcur = ptring->hwcur; + kring->rhead = kring->ring->head = atok->head; + kring->rcur = kring->ring->cur = atok->cur; + kring->nr_hwcur = ktoa->hwcur; kring->nr_hwtail = kring->rtail = - kring->ring->tail = ptring->hwtail; + kring->ring->tail = ktoa->hwtail; - ND("%d,%d: csb {hc %u h %u c %u ht %u}", t, i, - ptring->hwcur, ptring->head, ptring->cur, - ptring->hwtail); - ND("%d,%d: kring {hc %u rh %u rc %u h %u c %u ht %u rt %u t %u}", - t, i, kring->nr_hwcur, kring->rhead, kring->rcur, + nm_prdis("%s: csb {hc %u h %u c %u ht %u}", kring->name, + ktoa->hwcur, atok->head, atok->cur, + ktoa->hwtail); + nm_prdis("%s: kring {hc %u rh %u rc %u h %u c %u ht %u rt %u t %u}", + kring->name, kring->nr_hwcur, kring->rhead, kring->rcur, kring->ring->head, kring->ring->cur, kring->nr_hwtail, kring->rtail, kring->ring->tail); } @@ -1056,15 +1072,15 @@ ptnet_nm_register(struct netmap_adapter *na, int onoff) struct net_device *netdev = na->ifp; struct ptnet_info *pi = netdev_priv(netdev); int native = (na == &pi->ptna->hwup.up); - struct ptnet_ring *ptring; - enum txrx t; + struct nm_csb_atok *atok; + struct nm_csb_ktoa *ktoa; int ret = 0; int i; BUG_ON(!(na == &pi->ptna->hwup.up || na == &pi->ptna->dr.up)); if (!onoff) { - pi->ptna->backend_regifs--; + pi->ptna->backend_users--; } /* If this is the last netmap client, guest interrupt enable flags may @@ -1074,13 +1090,15 @@ ptnet_nm_register(struct netmap_adapter *na, int onoff) * in the RX rings, since we will not receive further interrupts * until these will be processed. */ if (native && !onoff && na->active_fds == 0) { - D("Exit netmap mode, re-enable interrupts"); + pr_info("%s: Exit netmap mode, re-enable interrupts\n", + __func__); for (i = 0; i < pi->num_rings; i++) { - ptring = pi->queues[i]->ptring; - ptring->guest_need_kick = 1; + atok = pi->queues[i]->atok; + atok->appl_need_kick = 1; } if (netif_running(netdev)) { - D("Exit netmap mode, schedule NAPI to flush RX ring"); + pr_info("%s: Exit netmap mode, schedule NAPI to flush RX ring\n", + __func__); for (i = 0; i < na->num_rx_rings; i++){ ptnet_napi_schedule(pi->rxqueues[i]); } @@ -1089,12 +1107,13 @@ ptnet_nm_register(struct netmap_adapter *na, int onoff) } if (onoff) { - if (pi->ptna->backend_regifs == 0) { + if (pi->ptna->backend_users == 0) { /* Initialize notification enable fields in the CSB. */ for (i = 0; i < pi->num_rings; i++) { - ptring = pi->queues[i]->ptring; - ptring->host_need_kick = 1; - ptring->guest_need_kick = (i >= pi->num_tx_rings); + atok = pi->queues[i]->atok; + ktoa = pi->queues[i]->ktoa; + atok->appl_need_kick = (i >= pi->num_tx_rings); + ktoa->kern_need_kick = 1; } /* Set the virtio-net header length. */ @@ -1102,80 +1121,56 @@ ptnet_nm_register(struct netmap_adapter *na, int onoff) /* Make sure the host adapter passed through is ready * for txsync/rxsync. */ - ret = ptnet_nm_ptctl(netdev, PTNETMAP_PTCTL_CREATE); + ret = ptnet_nm_ptctl(pi, PTNETMAP_PTCTL_CREATE); if (ret) { return ret; } - } - /* Sync from CSB must be done after REGIF PTCTL. Skip this - * step only if this is a netmap client and it is not the - * first one. */ - if ((!native && pi->ptna->backend_regifs == 0) || - (native && na->active_fds == 0)) { + /* Align the guest krings and rings to the state stored + * in the CSB. */ ptnet_sync_from_csb(pi, na); } /* If not native, don't call nm_set_native_flags, since we don't want * to replace ndo_start_xmit method, nor set NAF_NETMAP_ON */ if (native) { - for_rx_tx(t) { - for (i = 0; i <= nma_get_nrings(na, t); i++) { - struct netmap_kring *kring = &NMR(na, t)[i]; - - if (nm_kring_pending_on(kring)) { - kring->nr_mode = NKR_NETMAP_ON; - } - } - } + netmap_krings_mode_commit(na, onoff); nm_set_native_flags(na); } } else { if (native) { nm_clear_native_flags(na); - for_rx_tx(t) { - for (i = 0; i <= nma_get_nrings(na, t); i++) { - struct netmap_kring *kring = &NMR(na, t)[i]; - - if (nm_kring_pending_off(kring)) { - kring->nr_mode = NKR_NETMAP_OFF; - } - } - } - } - - /* Sync from CSB must be done before UNREGIF PTCTL, on the last - * netmap client. */ - if (native && na->active_fds == 0) { - ptnet_sync_from_csb(pi, na); + netmap_krings_mode_commit(na, onoff); } - if (pi->ptna->backend_regifs == 0) { - ret = ptnet_nm_ptctl(netdev, PTNETMAP_PTCTL_DELETE); + if (pi->ptna->backend_users == 0) { + ret = ptnet_nm_ptctl(pi, PTNETMAP_PTCTL_DELETE); } } if (onoff) { - pi->ptna->backend_regifs++; + pi->ptna->backend_users++; } return ret; } static int -ptnet_nm_config(struct netmap_adapter *na, unsigned *txr, unsigned *txd, - unsigned *rxr, unsigned *rxd) +ptnet_nm_config(struct netmap_adapter *na, + struct nm_config_info *info) { struct ptnet_info *pi = netdev_priv(na->ifp); - *txr = ioread32(pi->ioaddr + PTNET_IO_NUM_TX_RINGS); - *rxr = ioread32(pi->ioaddr + PTNET_IO_NUM_RX_RINGS); - *txd = ioread32(pi->ioaddr + PTNET_IO_NUM_TX_SLOTS); - *rxd = ioread32(pi->ioaddr + PTNET_IO_NUM_RX_SLOTS); + info->num_tx_rings = ioread32(pi->ioaddr + PTNET_IO_NUM_TX_RINGS); + info->num_rx_rings = ioread32(pi->ioaddr + PTNET_IO_NUM_RX_RINGS); + info->num_tx_descs = ioread32(pi->ioaddr + PTNET_IO_NUM_TX_SLOTS); + info->num_rx_descs = ioread32(pi->ioaddr + PTNET_IO_NUM_RX_SLOTS); + info->rx_buf_maxsize = NETMAP_BUF_SIZE(na); - pr_info("txr %u, rxr %u, txd %u, rxd %u\n", - *txr, *rxr, *txd, *rxd); + pr_info("%s: txr %u, rxr %u, txd %u, rxd %u, rxbufsz %u\n", __func__, + info->num_tx_rings, info->num_rx_rings, info->num_tx_descs, + info->num_rx_descs, info->rx_buf_maxsize); return 0; } @@ -1187,7 +1182,7 @@ ptnet_nm_txsync(struct netmap_kring *kring, int flags) struct ptnet_queue *pq = pi->queues[kring->ring_id]; bool notify; - notify = netmap_pt_guest_txsync(pq->ptring, kring, flags); + notify = netmap_pt_guest_txsync(pq->atok, pq->ktoa, kring, flags); if (notify) { iowrite32(0, pq->kick); } @@ -1202,7 +1197,7 @@ ptnet_nm_rxsync(struct netmap_kring *kring, int flags) struct ptnet_queue *pq = pi->rxqueues[kring->ring_id]; bool notify; - notify = netmap_pt_guest_rxsync(pq->ptring, kring, flags); + notify = netmap_pt_guest_rxsync(pq->atok, pq->ktoa, kring, flags); if (notify) { iowrite32(0, pq->kick); } @@ -1210,6 +1205,18 @@ ptnet_nm_rxsync(struct netmap_kring *kring, int flags) return 0; } +static void +ptnet_nm_intr(struct netmap_adapter *na, int onoff) +{ + struct ptnet_info *pi = netdev_priv(na->ifp); + int i; + + for (i = 0; i < pi->num_rings; i++) { + struct ptnet_queue *pq = pi->queues[i]; + pq->atok->appl_need_kick = onoff; + } +} + static struct netmap_adapter ptnet_nm_ops = { .nm_register = ptnet_nm_register, .nm_config = ptnet_nm_config, @@ -1218,10 +1225,11 @@ static struct netmap_adapter ptnet_nm_ops = { .nm_krings_create = ptnet_nm_krings_create, .nm_krings_delete = ptnet_nm_krings_delete, .nm_dtor = ptnet_nm_dtor, + .nm_intr = ptnet_nm_intr, }; /* - * ptnet_probe - Device Initialization Routine + * ptnet_probe - Device initialization routine * @ent: entry in ptnet_pci_table * * Returns 0 on success, negative on failure @@ -1266,10 +1274,11 @@ ptnet_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } err = -EIO; - pr_info("IO BAR (registers): start 0x%llx, len %llu, flags 0x%lx\n", - pci_resource_start(pdev, PTNETMAP_IO_PCI_BAR), - pci_resource_len(pdev, PTNETMAP_IO_PCI_BAR), - pci_resource_flags(pdev, PTNETMAP_IO_PCI_BAR)); + pr_info("%s: IO BAR (registers): start 0x%llx, len %llu, flags 0x%lx\n", + __func__, + (unsigned long long)pci_resource_start(pdev, PTNETMAP_IO_PCI_BAR), + (unsigned long long)pci_resource_len(pdev, PTNETMAP_IO_PCI_BAR), + pci_resource_flags(pdev, PTNETMAP_IO_PCI_BAR)); ioaddr = pci_iomap(pdev, PTNETMAP_IO_PCI_BAR, 0); if (!ioaddr) { @@ -1311,6 +1320,12 @@ ptnet_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pi->num_rings = num_tx_rings + num_rx_rings; pi->num_tx_rings = num_tx_rings; + if (pi->num_rings * sizeof(struct nm_csb_atok) > PAGE_SIZE) { + pr_err("%s: CSB for device %s cannot handle too many " + "rings (%u)\n",__func__, netdev->name, pi->num_rings); + goto err_ptfeat; + } + /* Initialize the arrays of pointers with the per-ring structures. */ pi->queues = (struct ptnet_queue **)(pi + 1); pi->rxqueues = pi->queues + num_tx_rings; @@ -1330,39 +1345,35 @@ ptnet_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } -#ifndef PTNET_CSB_ALLOC - /* Map the CSB memory exposed by the device. We don't use - * pci_ioremap_bar(), since we want the ioremap_cache() function - * to be called internally, rather than ioremap_nocache(). */ - pr_info("MEMORY BAR (CSB): start 0x%llx, len %llu, flags 0x%lx\n", - pci_resource_start(pdev, PTNETMAP_MEM_PCI_BAR), - pci_resource_len(pdev, PTNETMAP_MEM_PCI_BAR), - pci_resource_flags(pdev, PTNETMAP_MEM_PCI_BAR)); - pi->csbaddr = ioremap_cache(pci_resource_start(pdev, PTNETMAP_MEM_PCI_BAR), - pci_resource_len(pdev, PTNETMAP_MEM_PCI_BAR)); - if (!pi->csbaddr) - goto err_csb; - pi->csb = (struct ptnet_csb *)pi->csbaddr; - -#else /* PTNET_CSB_ALLOC */ - /* Alloc the CSB here and tell the hypervisor its physical address. */ - pi->csb = kzalloc(sizeof(struct ptnet_csb), GFP_KERNEL); - if (!pi->csb) { + pi->csb_pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (pi->csb_pages == NULL) { goto err_csb; } + pi->csb_gh = page_address(pi->csb_pages); + pi->csb_hg = page_address(pi->csb_pages) + PAGE_SIZE; { - phys_addr_t paddr = virt_to_phys(pi->csb); + /* CSB allocation protocol. Write to GH_BAH first, then + * to GH_BAL. Same for HG_BAH and HG_BAL. */ + phys_addr_t paddr = virt_to_phys(pi->csb_gh); + phys_addr_t hipa = 0; + +#if BITS_PER_LONG == 64 + hipa = (paddr >> 32) & 0xffffffff; +#endif + iowrite32(hipa, ioaddr + PTNET_IO_CSB_GH_BAH); + iowrite32(paddr & 0xffffffff, + ioaddr + PTNET_IO_CSB_GH_BAL); - /* CSB allocation protocol. Write CSBBAH first, then - * CSBBAL. */ - iowrite32((paddr >> 32) & 0xffffffff, - ioaddr + PTNET_IO_CSBBAH); + paddr = virt_to_phys(pi->csb_hg); +#if BITS_PER_LONG == 64 + hipa = (paddr >> 32) & 0xffffffff; +#endif + iowrite32(hipa, ioaddr + PTNET_IO_CSB_HG_BAH); iowrite32(paddr & 0xffffffff, - ioaddr + PTNET_IO_CSBBAL); + ioaddr + PTNET_IO_CSB_HG_BAL); } -#endif /* PTNET_CSB_ALLOC */ /* Initialize common parts of all the queues (interrupt * setup excluded). */ @@ -1370,11 +1381,12 @@ ptnet_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct ptnet_queue *pq = pi->queues[i]; pq->pi = pi; pq->kring_id = i; - pq->kick = ioaddr + PTNET_IO_KICK_BASE + 4 * i; - pq->ptring = pi->csb->rings + i; if (i >= num_tx_rings) { pq->kring_id -= num_tx_rings; } + pq->kick = ioaddr + PTNET_IO_KICK_BASE + 4 * i; + pq->atok = pi->csb_gh + i; + pq->ktoa = pi->csb_hg + i; } netdev->netdev_ops = &ptnet_netdev_ops; @@ -1382,10 +1394,10 @@ ptnet_probe(struct pci_dev *pdev, const struct pci_device_id *ent) for (i = 0; i < queue_pairs; i++) { struct ptnet_rx_queue *prq = (struct ptnet_rx_queue *) pi->rxqueues[i]; - netif_napi_add(netdev, &prq->napi, ptnet_rx_poll, NAPI_POLL_WEIGHT); + NM_NETIF_NAPI_ADD(netdev, &prq->napi, ptnet_rx_poll, NAPI_POLL_WEIGHT); } - strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); + strlcpy(netdev->name, pci_name(pdev), sizeof(netdev->name)); /* Read MAC address from device and put it into the netdev struct. */ macreg = ioread32(ioaddr + PTNET_IO_MAC_HI); @@ -1396,7 +1408,7 @@ ptnet_probe(struct pci_dev *pdev, const struct pci_device_id *ent) macaddr[3] = (macreg >> 16) & 0xff; macaddr[4] = (macreg >> 8) & 0xff; macaddr[5] = macreg & 0xff; - memcpy(netdev->dev_addr, macaddr, netdev->addr_len); + NM_DEV_ADDR_SET(netdev, macaddr); netdev->features = NETIF_F_HIGHDMA; @@ -1405,7 +1417,9 @@ ptnet_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ptnet_gso) { hw_features |= NETIF_F_TSO +#ifdef NETMAP_LINUX_HAVE_UFO | NETIF_F_UFO +#endif /* NETMAP_LINUX_HAVE_UFO */ | NETIF_F_TSO_ECN | NETIF_F_TSO6; netdev->features |= NETIF_F_GSO_ROBUST; @@ -1449,8 +1463,8 @@ ptnet_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ptnet_nm_ops.num_rx_rings = num_rx_rings; na_arg = ptnet_nm_ops; na_arg.ifp = pi->netdev; - netmap_pt_guest_attach(&na_arg, pi->csb, nifp_offset, - ioread32(ioaddr + PTNET_IO_HOSTMEMID)); + netmap_pt_guest_attach(&na_arg, nifp_offset, + ioread32(ioaddr + PTNET_IO_HOSTMEMID)); /* Now a netmap adapter for this device has been allocated, and it * can be accessed through NA(ifp). We have to initialize the CSB * pointer. */ @@ -1463,21 +1477,16 @@ ptnet_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netif_carrier_on(netdev); - pr_info("%s: %p\n", __func__, pi); + pr_info("%s: device %s registered \n", __func__, netdev->name); return 0; - pr_info("%s: failed\n", __func__); + err_netreg: ptnet_irqs_fini(pi); err_irqs: -#ifdef PTNET_CSB_ALLOC - kfree(pi->csb); -#endif /* PTNET_CSB_ALLOC */ + put_page(pi->csb_pages); err_csb: -#ifndef PTNET_CSB_ALLOC - iounmap(pi->csbaddr); -#endif /* !PTNET_CSB_ALLOC */ free_netdev(netdev); err_ptfeat: iounmap(ioaddr); @@ -1485,11 +1494,23 @@ ptnet_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_release_selected_regions(pdev, bars); err_pci_reg: pci_disable_device(pdev); + pr_info("%s: failed to probe device\n", __func__); return err; } +/* Stop the host sync-kloop in case it was running. */ +static void +ptnet_device_shutdown(struct ptnet_info *pi) +{ + ptnet_nm_ptctl(pi, PTNETMAP_PTCTL_DELETE); + iowrite32(0, pi->ioaddr + PTNET_IO_CSB_GH_BAH); + iowrite32(0, pi->ioaddr + PTNET_IO_CSB_GH_BAL); + iowrite32(0, pi->ioaddr + PTNET_IO_CSB_HG_BAH); + iowrite32(0, pi->ioaddr + PTNET_IO_CSB_HG_BAL); +} + /* - * ptnet_remove - Device Removal Routine + * ptnet_remove - Device removal routine * * ptnet_remove is called by the PCI subsystem to alert the driver * that it should release a PCI device. The could be caused by a @@ -1503,13 +1524,17 @@ ptnet_remove(struct pci_dev *pdev) struct ptnet_info *pi = netdev_priv(netdev); int i; + /* Stop the host sync-kloop. */ + ptnet_device_shutdown(pi); + netif_carrier_off(netdev); /* When the netdev is unregistered, ptnet_close() is invoked * for the device. Therefore, the uninitialization of the the * two netmap adapters (ptna, ptna->dr) must happen - * afterwards. */ + * afterwards. */ unregister_netdev(netdev); + pr_info("%s: device %s unregistered\n", __func__, netdev->name); /* Uninitialize netmap adapters for this device. */ netmap_detach(netdev); @@ -1520,35 +1545,31 @@ ptnet_remove(struct pci_dev *pdev) netif_napi_del(&prq->napi); } + /* Deallocate resources and disable the device. */ ptnet_irqs_fini(pi); - iounmap(pi->ioaddr); -#ifndef PTNET_CSB_ALLOC - iounmap(pi->csbaddr); -#else /* !PTNET_CSB_ALLOC */ - iowrite32(0, pi->ioaddr + PTNET_IO_CSBBAH); - iowrite32(0, pi->ioaddr + PTNET_IO_CSBBAL); - kfree(pi->csb); -#endif /* !PTNET_CSB_ALLOC */ + put_page(pi->csb_pages); pci_release_selected_regions(pdev, pi->bars); free_netdev(netdev); pci_disable_device(pdev); - - pr_info("%s: %p\n", __func__, pi); } -#if 0 -static void +/* + * Device shutdown routine, called when the system is going to + * power off or reboot. + */ +void ptnet_shutdown(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); + struct ptnet_info *pi = netdev_priv(netdev); - netif_device_detach(netdev); + /* Stop the host sync-kloop. */ + ptnet_device_shutdown(pi); + netif_device_detach(netdev); if (netif_running(netdev)) { ptnet_close(netdev); } - pci_disable_device(pdev); } -#endif diff --git a/LINUX/read-vars.mak b/LINUX/read-vars.mak index 4ef892d85..87088f312 100644 --- a/LINUX/read-vars.mak +++ b/LINUX/read-vars.mak @@ -1,3 +1,4 @@ +-include extdrv-versions.mak -include default-config.mak -include config.mak -include drivers.mak diff --git a/LINUX/scripts/np b/LINUX/scripts/np index 437493dba..ff4c4eef2 100755 --- a/LINUX/scripts/np +++ b/LINUX/scripts/np @@ -1,9 +1,12 @@ #!/bin/bash +# shellcheck disable=SC1091,SC2064,SC2012 ## Manage linux driver patches for netmap. ## ## Initial setup: ## ## - a git clone of netmap/linux with all the netmap-* branches created (GITDIR) +## (also make sure that you have all the git tags in place; you may need to +## git fetch --tags from https://github.com/torvalds/linux.git) ## ## - a directory where the linux trees for each major version of linux can ## be extracted (LINUX_SOURCES) @@ -51,7 +54,7 @@ PROGNAME=$0 [ -n "$1" ] || { - scripts/help $PROGNAME; + scripts/help "$PROGNAME"; exit 1 } @@ -74,24 +77,24 @@ function error { } function get-params { - local params=$1; shift - err_msg="$PROGNAME $COMMAND $(echo $params| perl -pe 's/\S+/<$&>/g')" - local param + local params param + params=$1; shift + err_msg="$PROGNAME $COMMAND $(echo "$params"| perl -pe 's/\S+/<$&>/g')" for param in $params; do - [[ -z "$@" ]] && error "$err_msg" - pname=$(echo -n $param | perl -pe 's/\W/_/g') - eval $pname="$1" + [[ -z "$*" ]] && error "$err_msg" + pname=$(echo -n "$param" | perl -pe 's/\W/_/g') + eval "$pname=$1" shift done - [[ -n "$@" ]] && error "$err_msg" + [[ -n "$*" ]] && error "$err_msg" } function need { eval "local v=\${$1}" - [ -n "$v" -a -d "$v${2:+/$2}" ] || error "Variable $1 not set or not valid" + { [ -n "$v" ] && [ -d "$v${2:+/$2}" ]; } || error "Variable $1 not set or not valid" } -## The following enviroment variables must be set: +## The following environment variables must be set: ## ## GITDIR: the absolute path of the netmap linux ## git repository, containing all the required netmap-* @@ -125,16 +128,18 @@ need LINUX_CONFIGS ## file system search. function driver-path() { + declare driver version get-params "driver version" "$@" - cat cache/$version/$driver/path 2>/dev/null && return - local kern=$(get-kernel $version) + cat cache/"$version"/vanilla-"$driver"/path 2>/dev/null && return + local kern + kern="$(get-kernel "$version")" [ -z "$kern" ] && error "no such kernel version: $version" - mkdir -p cache/$version/$driver + mkdir -p cache/"$version"/vanilla-"$driver" ( - cd $kern - find drivers/net -name $driver - ) | tee cache/$version/$driver/path + cd "$kern" || exit + find drivers/net -name "$driver" + ) | tee cache/"$version"/vanilla-"$driver"/path } @@ -151,25 +156,55 @@ function get-patch() local use_cache [ "$1" = -c ] && { use_cache=1; shift; } + declare driver version get-params "driver version" "$@" + local v1 v2 patchname out drvpath drvdir # convert kernel version to fixed notation - local v1=$(scripts/vers $version -c) + v1=$(scripts/vers "$version" -c) # compute next kernel version (in fixed notation) - local v2=$(scripts/vers $version -i -c) - local patchname=vanilla--$driver--$v1--$v2 - local out=tmp-patches/$patchname - [ -n "$use_cache" -a -s $out ] && { echo $out; return; } - local drvpath=$(driver-path $driver $version) + v2=$(scripts/vers "$version" -i -c) + patchname=vanilla--$driver--$v1--$v2 + out=tmp-patches/$patchname + [ -n "$use_cache" ] && [ -s "$out" ] && { echo "$out"; return; } + drvpath=$(driver-path "$driver" "$version") [ -n "$drvpath" ] || return - local drvdir=$(dirname $drvpath) + drvdir=$(dirname "$drvpath") ( - cd $GITDIR - git diff --relative=$drvdir v$version..netmap-$version -- $drvpath - ) > $out + cd "$GITDIR" || exit + git diff --relative="$drvdir" "v$version..netmap-$version" -- "$drvpath" + ) > "$out" # an empty patch means no netmap support for this driver - [ -s $out ] || { rm $out; return 1; } - echo $out + [ -s "$out" ] || { rm "$out"; return 1; } + echo "$out" + return 0; +} + +## +## put-patch +function put-patch() +{ + declare driver version + get-params "driver version" "$@" + + local drvpath drvdir v patchname + # convert kernel version to fixed notation + drvpath=$(driver-path "$driver" "$version") + [ -n "$drvpath" ] || return + drvdir=$(dirname "$drvpath") + v=$(scripts/vers "$version" -c) + patchname=$(ls final-patches/vanilla--"$driver"--* | awk -v v="$v" -F -- '($3 != "*") && ($3 <= v"") && (v"" < $4)') + [ -n "$patchname" ] || return + ( + set -e + cd "$GITDIR" + git checkout netmap-$version || git checkout -b netmap-$version v$version + cd "$drvdir" + patch -N --no-backup-if-mismatch -p1 || { git restore .; git clean -fdx .; exit; } + git add -u + git commit -m "patch for netmap $driver driver" + git clean -fdx . + ) < "$patchname" return 0; } @@ -181,27 +216,43 @@ function get-patch() ## and their names are output to stdout. function get-range() { + declare driver version1 version2 get-params "driver version1 version2" "$@" - local v=$version1 - local nv + local v nv V1 V2 p + v=$version1 # while version is less than $version2 - while scripts/vers -b $v $version2 -L; do + while scripts/vers -b "$v" "$version2" -L; do # compute next version - nv=$(scripts/vers $v -i) + nv=$(scripts/vers "$v" -i) if [ -z "$EXTDRV" ]; then - get-patch $driver $v + get-patch "$driver" "$v" else - local V1=$(scripts/vers $v -c) - local V2=$(scripts/vers $nv -c) - local p=tmp-patches/external--$driver--$V1--$V2 - touch $p - echo $p + V1=$(scripts/vers "$v" -c) + V2=$(scripts/vers "$nv" -c) + p=tmp-patches/external--$driver--$V1--$V2 + touch "$p" + echo "$p" fi v=$nv done } +## +## put-range +function put-range() +{ + declare version1 version2 + get-params "version1 version2" "$@" + + local v + v=$version1 + while scripts/vers -b "$v" "$version2" -L; do + forall put-patch "$v" + v=$(scripts/vers "$v" -i) + done +} + ## ## get-src @@ -210,29 +261,33 @@ function get-range() ## directory. function get-src() { + declare driver version dest get-params "driver version dest" "$@" - local kern=$(get-kernel $version) + local kern src + kern=$(get-kernel "$version") [ -z "$kern" ] && error "no such kernel version: $version" - local src=$(driver-path $driver $version) - cp -r $kern/$src $dest + src=$(driver-path "$driver" "$version") + cp -r "$kern/$src" "$dest" } ## ## extend -## checks wether the range of applicability of the -## given can be extented to include . +## checks whether the range of applicability of the +## given can be extended to include . ## It returns 0 on success and 1 on failure. function extend() { + declare patch version get-params "patch version" "$@" - local _patch=$(realpath $patch) + local _patch driver tmpdir1 tmpdir2 patch2 + _patch=$(realpath "$patch") # extract the driver name from the patch name - local driver=$(scripts/vers $_patch -s -p -p) - local tmpdir1=$(mktemp -d) - local tmpdir2=$(mktemp -d) + driver=$(scripts/vers "$_patch" -s -p -p) + tmpdir1=$(mktemp -d) + tmpdir2=$(mktemp -d) trap "rm -rf $tmpdir1 $tmpdir2" 0 # we get the driver sources for the given and # we apply two patches separately: @@ -241,19 +296,19 @@ function extend() # We declare to be extendable if # - it is still applicable AND # - we obtain the same files from i) and ii) (ignoring whitespace) - get-src $driver $version $tmpdir1 - get-src $driver $version $tmpdir2 + get-src "$driver" "$version" "$tmpdir1" + get-src "$driver" "$version" "$tmpdir2" ( - cd $tmpdir1 - patch --no-backup-if-mismatch -p1 < $_patch >/dev/null 2>&1 + cd "$tmpdir1" || exit 1 + patch --no-backup-if-mismatch -p1 < "$_patch" >/dev/null 2>&1 ) || return 1 - local patch2=$(get-patch -c $driver $version) - patch2=$(realpath $patch2) + patch2=$(get-patch -c "$driver" "$version") + patch2=$(realpath "$patch2") ( - cd $tmpdir2 - patch -p1 < $patch2 >/dev/null 2>&1 + cd "$tmpdir2" || exit 1 + patch -p1 < "$patch2" >/dev/null 2>&1 ) # this will certainly apply - diff -qbBr $tmpdir1 $tmpdir2 >/dev/null || return 1 + diff -qbBr "$tmpdir1" "$tmpdir2" >/dev/null || return 1 return 0 } @@ -266,35 +321,37 @@ function extend() ## they are deleted first. function minimize() { + declare driver get-params "driver" "$@" mkdir -p final-patches - local drv=$(basename $driver) - local patches=$(ls tmp-patches/vanilla--$drv--* 2>/dev/null) + local drv patches pivot ple pre nle nre + drv=$(basename "$driver") + patches=$(ls tmp-patches/vanilla--"$drv"--* 2>/dev/null) [ -n "$patches" ] || return 1 # put the patch names in $1, $2, ... set $patches - rm -f final-patches/vanilla--$drv--* + rm -f final-patches/vanilla--"$drv"--* # the original patches (in tmp-patches) are ordered by version number. # We consider one patch in turn (the 'pivot') and try # to extend its range to cover the range of the next - # patch. If this succedes, the merged patch is the new + # patch. If this succeeds, the merged patch is the new # pivot, otherwise the current pivot is output and the # next patch becomes the new pivot. The process # is repeated until there are no more patches to consider. - local pivot=$1 - [ -n "$pivot" -a -e "$pivot" ] || return 1 + pivot=$1 + [ -n "$pivot" ] && [ -e "$pivot" ] || return 1 # extract the left end and right end of the pivot's range - local ple=$(scripts/vers $pivot -s -p -C) - local pre=$(scripts/vers $pivot -s -C) + ple=$(scripts/vers "$pivot" -s -p -C) + pre=$(scripts/vers "$pivot" -s -C) while [ -n "$pivot" ]; do shift if [ -n "$1" ]; then # extract the left end and right end of the next patch - local nle=$(scripts/vers $1 -s -p -C) - local nre=$(scripts/vers $1 -s -C) + nle=$(scripts/vers "$1" -s -p -C) + nre=$(scripts/vers "$1" -s -C) # we admit no gaps in the range - if [ $pre = $nle ] && extend $pivot $nle; then + if [ "$pre" = "$nle" ] && extend "$pivot" "$nle"; then pre=$nre continue fi @@ -302,9 +359,9 @@ function minimize() # either out of patches or failed merge. # Compute the file name of the current pivot and store # the patch in its final location - out=$(scripts/vers vanilla $drv $ple -c $pre -c -S4) - cp $pivot final-patches/$out - # the new pivot becames the next patch (if any) + out=$(scripts/vers vanilla "$drv" "$ple" -c "$pre" -c -S4) + cp "$pivot" final-patches/"$out" + # the new pivot becomes the next patch (if any) pivot=$1 pre=$nre ple=$nle @@ -319,32 +376,36 @@ function minimize() ## Do nothing otherwise. function infty() { + declare driver version get-params "driver version" "$@" - local drv=$(basename $driver) + local drv v last + drv=$(basename "$driver") # convert kernel version to fixed notation - local v=$(scripts/vers $version -c) - local last=$(ls final-patches/vanilla--$drv--*--$v 2>/dev/null|tail -n1) + v=$(scripts/vers "$version" -c) + last=$(ls final-patches/vanilla--"$drv"--*--"$v" 2>/dev/null|tail -n1) [ -n "$last" ] || return 1 - mv -n $last $(scripts/vers $last -s -p 99999 -S4) 2>/dev/null + mv -n "$last" "$(scripts/vers "$last" -s -p 99999 -S4)" 2>/dev/null } function get-kernel() { + declare version get-params "version" "$@" - local dst="$(realpath $LINUX_SOURCES)/linux-$version" + local dst v + dst=$(realpath "$LINUX_SOURCES")/linux-"$version" - [ -d $dst ] && { echo $dst; return; } + [ -d "$dst" ] && { echo "$dst"; return; } - local v=$version + v=$version ( - cd $GITDIR + cd "$GITDIR" || exit if git show-ref --tags --quiet --verify -- "refs/tags/v$v"; then - mkdir -p $dst - git archive v$v | tar xf - -C $dst - echo $dst + mkdir -p "$dst" + git archive v"$v" | tar xf - -C "$dst" + echo "$dst" fi ) } @@ -360,40 +421,65 @@ function get-kernel() ## as a default; if also the latter does not exist, the kernel is ## configure using 'allmodconfig'. ## Errors are logged to $LINUX_CONFIGS/linux-.log. -## If $LINUX_SOURCES/linux- already exists, +## If $LINUX_SOURCES/linux-/.build-prep already exists, ## nothing is done. ## In all cases, the absolute path of linux- is ## output. function build-prep() { + declare version get-params "version" "$@" - local dst=$(get-kernel $version) + local dst + dst=$(get-kernel "$version") + local last - [ -z "$dst" ] && return + [ -f "$dst"/.build-prep ] && { echo "$dst"; return; } ( - cd $dst - [ -e include/linux/compiler-gcc5.h ] || - ln -s compiler-gcc4.h include/linux/compiler-gcc5.h - [ -e include/linux/compiler-gcc6.h ] || - ln -s compiler-gcc5.h include/linux/compiler-gcc6.h - # force disabling PIE + cd "$dst" || exit 1 + # fix for incompatible GNU make change + sed -i -e '/^squote/a\ +pound := \\# + /\/{s/\\#/$(pound)/}' tools/build/Build.include || true + last=compiler-gcc.h + for i in $(seq 15); do + [ -e include/linux/compiler-gcc"$i".h ] || + ln -s "$last" include/linux/compiler-gcc"$i".h + last=compiler-gcc$i.h + done + # force disabling PIE and fcf-protection sed -i -e '/^all: vmlinux/a\ \ KBUILD_CFLAGS += $(call cc-option, -fno-pie)\ KBUILD_CFLAGS += $(call cc-option, -no-pie)\ +KBUILD_CFLAGS += $(call cc-option, -fcf-protection=none)\ KBUILD_AFLAGS += $(call cc-option, -fno-pie)\ KBUILD_CPPFLAGS += $(call cc-option, -fno-pie)' Makefile - if [ -f $LINUX_CONFIGS/config-$version ]; then - cp $LINUX_CONFIGS/config-$version .config + # remove duplicated yylloc definition, since gcc-10 defaults to -fno-common + sed -i -e '/^[[:blank:]]*YYLTYPE[[:blank:]][[:blank:]]*yylloc;[[:blank:]]*$/d' \ + scripts/dtc/dtc-lexer* + # make sure per_cpu_load_addr is static + sed -i -e 's/^[[:blank:]]*Elf_Addr[[:blank:]][[:blank:]]*per_cpu_load_addr;/static &/' \ + arch/x86/tools/relocs.c + if [ -f "$LINUX_CONFIGS/config-$version" ]; then + cp "$LINUX_CONFIGS/config-$version" .config yes '' | make oldconfig else make allmodconfig fi - make modules_prepare - ) >$dst.log 2>&1 || error "build-prep failed for linux $version. Please check $dst.log" - echo $dst + # old kernels' selinux causes compilation failures with gcc >= 9 + echo "CONFIG_SECURITY_SELINUX=n" >> .config + # workaround for some objtool/binutils incompatibility + sed -i -e 's/^CONFIG_UNWINDER_ORC=/#&/' .config + echo "CONFIG_UNWINDER_FRAME_POINTER=y" >> .config + yes '' | make oldconfig + # some tools do not compile with -Werror and gcc >= 9 + sed -i 's/-Werror/-Wno-error/g' $(grep -Rl -- -Werror tools) + make scripts modules_prepare + touch .build-prep + ) >"$dst".log 2>&1 || error "build-prep failed for linux $version. Please check $dst.log" + echo "$dst" } ## @@ -403,65 +489,71 @@ KBUILD_CPPFLAGS += $(call cc-option, -fno-pie)' Makefile ## Errors are logged to log/. function check-patch() { + declare patch get-params "patch" "$@" - local _patch=$(basename $patch) + local _patch v1 v2 end driver dtype p log nmcommit warn + _patch=$(basename "$patch") # extract the left version - local v1=$(scripts/vers $_patch -s -p -C) + v1=$(scripts/vers "$_patch" -s -p -C) # extract the right version - local v2=$(scripts/vers $_patch -s -C) - # extract the uncoverted right version (might be 99999) - local end=$(scripts/vers $_patch -s) + v2=$(scripts/vers "$_patch" -s -C) + # extract the unconverted right version (might be 99999) + end=$(scripts/vers "$_patch" -s) # extract the driver name - local driver=$(scripts/vers $_patch -s -p -p) + driver=$(scripts/vers "$_patch" -s -p -p) + # possibly extract the selected driver version + driver_version=$(echo "$driver" | sed -n 's/^.*://p') + driver=${driver%%:*} # extract the driver type (vanilla or external) - local dtype=$(scripts/vers $_patch -s -p) - local p=$(realpath $patch) + dtype=$(scripts/vers "$_patch" -s -p -p -p) + p=$(realpath "$patch") mkdir -p log - local log="$(realpath log)/$(basename $patch)" - local nmcommit=$(cd ..; git show-ref -s heads/$NETMAP_BRANCH) - local warn=false + log=$(realpath log)/$(basename "$patch") + nmcommit=$(cd .. || exit 1; git show-ref -s heads/"$NETMAP_BRANCH") + warn=false - rm -f $log + rm -f "$log" - echo -n $patch... + echo -n "$patch..." - while scripts/vers -b $v1 $v2 -L; do + local cache cpatch cnmcommit cstatus cwarn clog ksrc tmpdir + while scripts/vers -b "$v1" "$v2" -L; do # cache lookup - local cache=$PWD/cache/$v1/$dtype-$driver - mkdir -p $cache - local cpatch=$cache/patch - local cnmcommit=$cache/nmcommit - local cstatus=$cache/status - local cwarn=$cache/warn - local clog=$cache/log - if [ -f $cpatch ] && - cmp -s $cpatch $patch && - [ "$nmcommit" = "$(cat $cnmcommit)" ]; then - cp $clog $log - ok=$(cat $cstatus) - if [ -f "$cwarn" ]; then warn=$(cat $cwarn); fi + cache=$PWD/cache/$v1/$dtype-$driver${driver_version:+:$driver_version} + mkdir -p "$cache" + cpatch=$cache/patch + cnmcommit=$cache/nmcommit + cstatus=$cache/status + cwarn=$cache/warn + clog=$cache/log + if [ -f "$cpatch" ] && + cmp -s "$cpatch" "$patch" && + [ "$nmcommit" = "$(cat "$cnmcommit")" ]; then + cp "$clog" "$log" + ok=$(cat "$cstatus") + if [ -f "$cwarn" ]; then warn=$(cat "$cwarn"); fi else # update cache - cp $patch $cpatch - echo $nmcommit > $cnmcommit + cp "$patch" "$cpatch" + echo "$nmcommit" > "$cnmcommit" - local ksrc=$(build-prep $v1) + ksrc=$(build-prep "$v1") if [ -z "$ksrc" ]; then - rm -rf $cache + rm -rf "$cache" if [ "$end" = 99999 ]; then ok=true break fi error "no such kernel version: $v1" fi - local tmpdir=$(mktemp -d) + tmpdir=$(mktemp -d) if [ "$NP_DEBUG" -lt 1 ]; then - trap "rm -rf $tmpdir" 0 + trap "rm -rf '$tmpdir'" 0 fi - echo "====== $tmpdir =====" >>$log - (cd ..; git archive $NETMAP_BRANCH | tar xf - -C $tmpdir ) - pushd $tmpdir/LINUX >/dev/null + echo "====== $tmpdir =====" >>"$log" + (cd ..; git archive "$NETMAP_BRANCH" | tar xf - -C "$tmpdir" ) + pushd "$tmpdir"/LINUX >/dev/null || exit rm -f patches ok=false config_opts= @@ -469,39 +561,43 @@ function check-patch() config_opts=--no-ext-drivers mkdir single-patch ln -s single-patch patches - cp $p single-patch + cp "$p" single-patch else rm -rf ext-drivers - ln -s $EXT_DRIVERS ext-drivers + ln -s "$EXT_DRIVERS" ext-drivers ln -s final-patches patches + if [ -n "$driver_version" ]; then + config_opts="$config_opts --select-version=$driver:$driver_version" + fi fi if [ "$driver" != "veth.c" ]; then config_opts="$config_opts --disable-pipe" fi - ./configure --kernel-dir=$ksrc --driver-suffix=$DRIVER_SUFFIX \ - --drivers=$driver \ + ./configure --kernel-dir="$ksrc" --driver-suffix="$DRIVER_SUFFIX" \ + --drivers="$driver" \ --disable-generic \ --disable-vale \ --disable-monitor \ --disable-ptnetmap \ + --disable-null \ --no-apps \ --kernel-opts=CONFIG_STACK_VALIDATION= \ $config_opts \ - --cache=$cache >>$log - (make get-$driver && make -j $PARALLEL_MAKE) >>$log 2>&1 && ok=true - grep -q warning: $log && { warn=true; echo $warn > $cwarn; } - cat config.log >>$log - cat netmap_linux_config.h >>$log - popd >/dev/null - cp $log $clog + --cache="$cache" >>"$log" + (make get-"$driver" && KBUILD_MODPOST_WARN=1 make -j "$PARALLEL_MAKE") >>"$log" 2>&1 && ok=true + grep -q warning: "$log" && { warn=true; echo "$warn" > "$cwarn"; } + cat config.log >>"$log" + cat netmap_linux_config.h >>"$log" 2>/dev/null + popd >/dev/null || exit + cp "$log" "$clog" fi - [ "$ok" = true ] || { echo FAILED; echo false > $cstatus; return 1; } - echo true > $cstatus + [ "$ok" = true ] || { echo FAILED; echo false > "$cstatus"; return 1; } + echo true > "$cstatus" if [ "$NP_DEBUG" -lt 2 ]; then - rm -rf $tmpdir + rm -rf "$tmpdir" fi # compute next version - v1=$(scripts/vers $v1 -i) + v1=$(scripts/vers "$v1" -i) done if [ "$warn" = true ]; then echo WARNING @@ -517,22 +613,23 @@ function check-patch() ## are moved to failed-patches. function build-check() { + declare driver get-params "driver" "$@" mkdir -p failed-patches - local dtype=vanilla + local dtype drv patches p + dtype=vanilla if [ "$EXTDRV" = 1 ]; then dtype=external fi - local drv=$(basename $driver) - local patches=$(ls tmp-patches/$dtype--$drv--* 2>/dev/null) - local p + drv=$(basename "$driver") + patches=$(ls tmp-patches/"$dtype"--"$drv"--* 2>/dev/null) for p in $patches; do - check-patch $p || mv $p failed-patches + check-patch "$p "|| mv "$p" failed-patches done } -MINVERS=2.6.32 +MINVERS=4.0 MAXVERS= NEXTVERS= ## @@ -542,30 +639,31 @@ NEXTVERS= ## available netmap-* branches in GITDIR function auto() { + declare driver get-params "driver" "$@" + local branches b v if [ -z "$MAXVERS" ]; then # get the latest netmap-* branch - local branches=$( - cd $GITDIR; + branches=$( + cd "$GITDIR" || exit 1 git for-each-ref refs/heads/netmap-[0-9]* --format='%(refname:short)' ) MAXVERS=$MINVERS - local b for b in $branches; do - local v=${b#netmap-} - if scripts/vers -b $MAXVERS $v -L; then + v=${b#netmap-} + if scripts/vers -b "$MAXVERS" "$v" -L; then MAXVERS=$v fi done echo "Latest netmap linux branch: $MAXVERS" - NEXTVERS=$(scripts/vers $MAXVERS -i) + NEXTVERS=$(scripts/vers "$MAXVERS" -i) fi - get-range $driver $MINVERS $NEXTVERS - build-check $driver + get-range "$driver" "$MINVERS" "$NEXTVERS" + build-check "$driver" if [ -z "$EXTDRV" ]; then - minimize $driver - infty $driver $NEXTVERS + minimize "$driver" + infty "$driver" "$NEXTVERS" fi } @@ -574,12 +672,12 @@ function auto() ## exec [args...] for all known drivers. function forall() { - local cmd=$1 + local cmd driver + cmd=$1 shift - local driver - for driver in $(./configure --show${EXTDRV:+-ext}-drivers); do - $cmd $(basename $driver) "$@" + for driver in $(./configure --show"${EXTDRV:+-ext}"-drivers); do + "$cmd" "$(basename "$driver")" "$@" done } @@ -597,6 +695,8 @@ if [ "$1" = -e ]; then fi EXTDRV=1 shift +else + EXT_DRIVERS= fi COMMAND=$1; shift case $COMMAND in @@ -604,12 +704,12 @@ case $COMMAND in ## -all [args...] ## same as: forall [args...] *-all) - forall ${COMMAND%-all} "$@" + forall "${COMMAND%-all}" "$@" ;; -[hH]|--help|-help|help) - scripts/help $PROGNAME + scripts/help "$PROGNAME" ;; *) - $COMMAND "$@" + "$COMMAND" "$@" ;; esac diff --git a/LINUX/scripts/vers b/LINUX/scripts/vers index 00d15a77d..f7e2a3ec6 100755 --- a/LINUX/scripts/vers +++ b/LINUX/scripts/vers @@ -18,7 +18,10 @@ sub checkversion if ($may < 2 || $min < 0 || $sub < 0 || ($may == 2 && $min != 6 && !($sub >= 32 && !$sub <= 39)) || - ($may == 3 && $min > 19)) { + ($may == 3 && $min > 19) || + ($may == 4 && $min > 20) || + ($may == 5 && $min > 19) || + ($may == 6 && $min > 19)) { die "Bad version $v"; } } @@ -59,6 +62,18 @@ sub next } else { return "4.0"; } + } elsif ($may == 4) { + if ($min < 20) { + return "4." . ($min + 1); + } else { + return "5.0"; + } + } elsif ($may == 5 || $may == 6) { + if ($min < 19) { + return "" . $may . "." . ($min +1); + } else { + return "" . ($may + 1) . ".0"; + } } else { return "$may." . ($min + 1); } @@ -74,6 +89,10 @@ sub prev return "2.6.39"; } elsif ($may == 4) { return "3.19"; + } elsif ($may == 5) { + return "4.20"; + } elsif ($may == 6 || $may == 7) { + return "" . ($may - 1) . ".19"; } else { die "Unknown version: $v"; } diff --git a/LINUX/veth_netmap.h b/LINUX/veth_netmap.h index 304090aff..a34686423 100644 --- a/LINUX/veth_netmap.h +++ b/LINUX/veth_netmap.h @@ -36,51 +36,64 @@ static int veth_open(struct ifnet *ifp); static int veth_close(struct ifnet *ifp); -/* To be called under RCU read lock */ +struct netmap_veth_adapter { + struct netmap_hw_adapter up; + struct netmap_veth_adapter *peer; + int peer_ref; +}; + +/* To be called under RCU read lock. This also sets peer_ref in the + * same way netmap_get_pipe_na() does. */ static struct netmap_adapter * veth_get_peer_na(struct netmap_adapter *na) { + struct netmap_adapter *ona; struct ifnet *ifp = na->ifp; struct veth_priv *priv = netdev_priv(ifp); struct ifnet *peer_ifp; - - peer_ifp = rcu_dereference(priv->peer); - if (!peer_ifp) { - return NULL; + struct netmap_veth_adapter *vna = + (struct netmap_veth_adapter *)na; + + if (vna->peer == NULL) { + /* Only one of the two endpoint enters here, + * and only once. */ + peer_ifp = rcu_dereference(priv->peer); + if (!peer_ifp) { + return NULL; + } + /* Cross link the peer netmap adapters. Note that we + * can retrieve the peer to do our clean-up even if + * the peer_ifp is detached from us. */ + ona = NA(peer_ifp); + vna->peer = (struct netmap_veth_adapter *)ona; + vna->peer->peer = vna; + + /* Both endpoints must have identical ring configurations */ + ona->num_rx_rings = na->num_tx_rings; + ona->num_tx_rings = na->num_rx_rings; + + /* Get a reference to the other endpoint. */ + netmap_adapter_get(&vna->peer->up.up); + vna->peer_ref = 1; } - return NA(peer_ifp); + return &vna->peer->up.up; } -/* - * Returns true if our krings needed by the other peer, false - * if they are not, or they do not exist. - */ -static bool -krings_needed(struct netmap_adapter *na) +static void +veth_netmap_dtor(struct netmap_adapter *na) { - enum txrx t; - int i; - - if (na->tx_rings == NULL) { - return false; - } - - for_rx_tx(t) { - for (i = 0; i < nma_get_nrings(na, t) + 1; i++) { - struct netmap_kring *kring = &NMR(na, t)[i]; - - if (kring->nr_kflags & NKR_NEEDRING) { - return true; - } - } + struct netmap_veth_adapter *vna = + (struct netmap_veth_adapter *)na; + if (vna->peer_ref) { + vna->peer_ref = 0; + vna->peer->peer = NULL; + netmap_adapter_put(&vna->peer->up.up); } - - return false; } /* - * Register/unregister. We are already under netmap lock. + * Register/unregister. We are already under RCU lock. * This register function is similar to the one used by * pipes; in addition to the regular tasks (commit the rings * in/out netmap node and call nm_(set|clear)_native_flags), @@ -90,18 +103,15 @@ krings_needed(struct netmap_adapter *na) static int veth_netmap_reg(struct netmap_adapter *na, int onoff) { + struct netmap_veth_adapter *vna = + (struct netmap_veth_adapter *)na; struct netmap_adapter *peer_na; struct ifnet *ifp = na->ifp; bool was_up; - enum txrx t; int error; - int i; - - rcu_read_lock(); peer_na = veth_get_peer_na(na); if (!peer_na) { - rcu_read_unlock(); return EINVAL; } @@ -113,170 +123,122 @@ veth_netmap_reg(struct netmap_adapter *na, int onoff) /* Enable or disable flags and callbacks in na and ifp. */ if (onoff) { - for_rx_tx(t) { - for (i = 0; i < nma_get_nrings(na, t); i++) { - struct netmap_kring *kring = &NMR(na, t)[i]; + enum txrx t; - if (nm_kring_pending_on(kring)) { - /* mark the peer ring as needed */ - kring->pipe->nr_kflags |= NKR_NEEDRING; - } - } - } - - /* create all missing needed rings on the other end */ - error = netmap_mem_rings_create(peer_na); + error = netmap_pipe_reg_both(na, peer_na); if (error) { - rcu_read_unlock(); return error; } - - /* In case of no error we put our rings in netmap mode */ for_rx_tx(t) { - for (i = 0; i < nma_get_nrings(na, t) + 1; i++) { - struct netmap_kring *kring = &NMR(na, t)[i]; + int i; + + for (i = nma_get_nrings(na, t); + i < netmap_real_rings(na, t); i++) { + struct netmap_kring *kring = NMR(na, t)[i]; if (nm_kring_pending_on(kring)) { - kring->nr_mode = NKR_NETMAP_ON; + /* mark the peer ring as needed */ + kring->nr_mode |= NKR_NETMAP_ON ; } } } nm_set_native_flags(na); if (netmap_verbose) { - D("registered veth %s", na->name); + nm_prinf("registered veth %s", na->name); } } else { nm_clear_native_flags(na); - - for_rx_tx(t) { - for (i = 0; i < nma_get_nrings(na, t) + 1; i++) { - struct netmap_kring *kring = &NMR(na, t)[i]; - - if (nm_kring_pending_off(kring)) { - kring->nr_mode = NKR_NETMAP_OFF; - /* If hw kring, mark the peer kring - * as no longer needed by us (it may - * still be kept if sombody else is - * using it). - */ - if (kring->pipe) { - kring->pipe->nr_kflags &= - ~NKR_NEEDRING; - } - } - } - } - /* delete all the peer rings that are no longer needed */ - netmap_mem_rings_delete(peer_na); + netmap_krings_mode_commit(na, onoff); if (netmap_verbose) { - D("unregistered veth %s", na->name); + nm_prinf("unregistered veth %s", na->name); } } - rcu_read_unlock(); - if (na->active_fds == 0 && was_up) { veth_open(ifp); } - return error; + if (vna->peer_ref) { + return 0; + } + if (onoff) { + if (vna->peer->peer_ref) { + vna->peer->peer_ref = 0; + netmap_adapter_put(na); + } + } else { + if (!vna->peer->peer_ref) { + netmap_adapter_get(na); + vna->peer->peer_ref = 1; + } + } + + return 0; } static int veth_netmap_krings_create(struct netmap_adapter *na) { + struct netmap_veth_adapter *vna = (struct netmap_veth_adapter *)na; struct netmap_adapter *peer_na; - int error = 0; - enum txrx t; - - if (krings_needed(na)) { - /* Our krings are already needed by our peer, which - * means they were already created. */ - if (netmap_verbose) { - D("krings already created for %s, nothing to do", - na->name); - } - return 0; - } + /* The nm_krings_create callback is called first in netmap_do_regif(), + * so the the cross linking happens now (if this is the first endpoint + * to register). */ rcu_read_lock(); peer_na = veth_get_peer_na(na); + rcu_read_unlock(); if (!peer_na) { - rcu_read_unlock(); - D("veth peer not found"); + nm_prerr("veth peer not found for %s", na->name); return ENXIO; } - /* create my krings */ - error = netmap_krings_create(na, 0); - if (error) - goto err; - - /* create the krings of the other end */ - error = netmap_krings_create(peer_na, 0); - if (error) - goto del_krings1; - - /* cross link the krings (only the hw ones, not the host krings) */ - for_rx_tx(t) { - enum txrx r = nm_txrx_swap(t); /* swap NR_TX <-> NR_RX */ - int i; - - for (i = 0; i < nma_get_nrings(na, t); i++) { - NMR(na, t)[i].pipe = NMR(peer_na, r) + i; - NMR(peer_na, r)[i].pipe = NMR(na, t) + i; - } - } - - rcu_read_unlock(); - - if (netmap_verbose) { - D("created krings for %s and its peer", na->name); - } + if (vna->peer_ref) + return netmap_pipe_krings_create_both(na, peer_na); return 0; - -del_krings1: - netmap_krings_delete(na); -err: - rcu_read_unlock(); - return error; } static void veth_netmap_krings_delete(struct netmap_adapter *na) { + struct netmap_veth_adapter *vna = (struct netmap_veth_adapter *)na; struct netmap_adapter *peer_na; - if (krings_needed(na)) { - /* Our krings are needed by the other peer, so we - * do nothing here, and let the peer destroy also - * our krings when it needs to destroy its krings. */ - if (netmap_verbose) { - D("krings for %s are still needed by its peer", - na->name); - } + if (!vna->peer_ref) { return; } if (netmap_verbose) { - D("Delete krings for %s and its peer", na->name); + nm_prinf("Delete krings for %s and its peer", na->name); } - /* Destroy my krings. */ - netmap_krings_delete(na); - - /* Destroy the krings of our peer. */ rcu_read_lock(); peer_na = veth_get_peer_na(na); + rcu_read_unlock(); if (!peer_na) { - rcu_read_unlock(); - D("veth peer not found"); + nm_prinf("veth peer not found"); return; } - netmap_krings_delete(peer_na); - rcu_read_unlock(); + netmap_pipe_krings_delete_both(na, peer_na); + + netmap_adapter_put(&vna->peer->up.up); + vna->peer_ref = 0; + vna->peer->peer = NULL; + vna->peer = NULL; +} + +static int +veth_netmap_config(struct netmap_adapter *na, struct nm_config_info *info) +{ + /* To maintain identical ring configurations, + * only one of the two endpoint should be configured. */ + if (((struct netmap_veth_adapter *)na)->peer != NULL) { + return EBUSY; + } + + return nm_os_config(na, info); } static void @@ -295,8 +257,11 @@ veth_netmap_attach(struct ifnet *ifp) na.nm_rxsync = netmap_pipe_rxsync; na.nm_krings_create = veth_netmap_krings_create; na.nm_krings_delete = veth_netmap_krings_delete; + na.nm_dtor = veth_netmap_dtor; + na.nm_config = veth_netmap_config; na.num_tx_rings = na.num_rx_rings = 1; - netmap_attach(&na); + netmap_attach_ext(&na, sizeof(struct netmap_veth_adapter), + 0 /* do not override reg */); } /* end of file */ diff --git a/LINUX/virtio_net.mak b/LINUX/virtio_net.mak new file mode 100644 index 000000000..189f5cb35 --- /dev/null +++ b/LINUX/virtio_net.mak @@ -0,0 +1,24 @@ +ifneq ($(KERNELRELEASE),) + +# virtio_net_src.c is just a symbolic link to virtio_net.c +# This workaround is needed because when defining modulename-y +# it is not possible to have a source called "modulename.c". +# Note that this is a problem only when NETMAP_DRIVER_SUFFIX +# is empty. +obj-m := virtio_net$(NETMAP_DRIVER_SUFFIX).o +virtio_net$(NETMAP_DRIVER_SUFFIX)-y := virtio_net_src.o + +else + +KSRC ?= /lib/modules/$(shell uname -r)/build + +all: virtio_net.c + $(MAKE) -C "${KSRC}" M=$(shell pwd) modules + +install: + $(MAKE) -C "${KSRC}" M=$(shell pwd) modules_install + +clean: + $(MAKE) -C "${KSRC}" M=$(shell pwd) clean + +endif diff --git a/LINUX/virtio_netmap.h b/LINUX/virtio_netmap.h index 1eec93fde..bb15287c3 100644 --- a/LINUX/virtio_netmap.h +++ b/LINUX/virtio_netmap.h @@ -173,6 +173,12 @@ virtio_netmap_init_sgs(struct virtnet_info *vi) #endif /* VIRTIO_NOTIFY */ +struct netmap_virtio_adapter { + struct netmap_hw_adapter hwna; /* base class */ + struct virtio_net_hdr_mrg_rxbuf shared_rxvhdr ____cacheline_aligned_in_smp; + struct virtio_net_hdr_mrg_rxbuf shared_txvhdr ____cacheline_aligned_in_smp; +}; + static void virtio_netmap_clean_used_rings(struct virtnet_info *vi, struct netmap_adapter *na) @@ -193,7 +199,7 @@ virtio_netmap_clean_used_rings(struct virtnet_info *vi, } n++; } - D("got %d used bufs on queue tx-%d", n, i); + nm_prinf("got %d used bufs on queue tx-%d", n, i); } for (i = 0; i < DEV_NUM_RX_QUEUES(vi->dev); i++) { @@ -206,7 +212,7 @@ virtio_netmap_clean_used_rings(struct virtnet_info *vi, n++; RXNUM_DEC(vi, i); } - D("got %d used bufs on queue rx-%d", n, i); + nm_prinf("got %d used bufs on queue rx-%d", n, i); } } @@ -232,7 +238,7 @@ virtio_netmap_reclaim_unused(struct virtnet_info *vi) while ((token = virtqueue_detach_unused_buf(vq)) != NULL) { n++; } - D("detached %d pending bufs on queue tx-%d", n, i); + nm_prinf("detached %d pending bufs on queue tx-%d", n, i); } for (i = 0; i < DEV_NUM_RX_QUEUES(vi->dev); i++) { @@ -244,99 +250,108 @@ virtio_netmap_reclaim_unused(struct virtnet_info *vi) RXNUM_DEC(vi, i); n++; } - D("detached %d pending bufs on queue rx-%d", n, i); - } -} - -/* Set or clear nr_pending_mode and nr_mode for all the rings, independently - * of the specific user request. This is necessary for now because the - * virtio-net driver patches do not support single-queue mode (modifications - * would be needed to free_unused_bufs() free_receive_bufs()).*/ -static void -virtio_netmap_set_kring_mode(struct netmap_adapter *na, int mode) -{ - int i; - - for (i = 0; i < DEV_NUM_TX_QUEUES(na->ifp); i++) { - struct netmap_kring *kring = &na->tx_rings[i]; - - kring->nr_pending_mode = kring->nr_mode = mode; - } - - for (i = 0; i < DEV_NUM_RX_QUEUES(na->ifp); i++) { - struct netmap_kring *kring = &na->rx_rings[i]; - - kring->nr_pending_mode = kring->nr_mode = mode; + nm_prinf("detached %d pending bufs on queue rx-%d", n, i); } - } /* Register and unregister. */ static int virtio_netmap_reg(struct netmap_adapter *na, int onoff) { + struct netmap_virtio_adapter *vna = (struct netmap_virtio_adapter *)na; struct ifnet *ifp = na->ifp; struct virtnet_info *vi = netdev_priv(ifp); + int hwrings_pending = 0, hwrings; bool was_up = false; int error = 0; + enum txrx t; + int i; - if (na == NULL) - return EINVAL; + /* These virtio-net driver patches do not support single-queue mode + * (modifications would be needed to free_unused_bufs() + * free_receive_bufs()). As a result, we fail here if we detect + * the user is trying to open or close only a subset of the rings. */ + hwrings = nma_get_nrings(na, NR_TX) + nma_get_nrings(na, NR_RX); + for_rx_tx(t) { + for (i = 0; i < nma_get_nrings(na, t); i++) { + struct netmap_kring *kring = NMR(na, t)[i]; + + if ((onoff && nm_kring_pending_on(kring)) || + (!onoff && nm_kring_pending_off(kring))) { + hwrings_pending ++; + } + } + } - if (na->active_fds > 0) { - /* virtio-net adapter currently does not support single-queue - * mode. As a consequence, register (unregister) operations - * only have effect with first (last) user.*/ - return 0; + if (!(hwrings_pending == 0 || hwrings_pending == hwrings)) { + nm_prerr("virtio-net native adapter can only open " + "all RX and TX hw rings"); + return EINVAL; } /* It's important to make sure each virtnet_close() matches * a virtnet_open(), otherwise a napi_disable() is not matched by * a napi_enable(), which results in a deadlock. */ - if (netif_running(ifp)) { + if (hwrings_pending && netif_running(ifp)) { was_up = true; /* Down the interface. This also disables napi. */ virtnet_close(ifp); } if (onoff) { - /* Get and free any used buffers. This is necessary - * before calling free_unused_bufs(), that uses - * virtqueue_detach_unused_buf(). */ - virtio_netmap_clean_used_rings(vi, na); - - /* Initialize scatter-gather lists used to publish netmap - * buffers through virtio descriptors, in such a way that each - * each scatter-gather list contains exactly one descriptor - * (which can point to a netmap buffer). This initialization is - * necessary to prevent the virtio frontend (host) to think - * we are using multi-descriptors scatter-gather lists. */ - virtio_netmap_init_sgs(vi); - - /* We have to drain the RX virtqueues, otherwise the - * virtio_netmap_init_buffer() called by the subsequent - * virtnet_open() cannot link the netmap buffers to the - * virtio RX ring. - * The unused buffers point to memory allocated by - * the virtio-driver (e.g. sk_buffs). We need to free that - * memory, otherwise we have leakage. - */ - free_unused_bufs(vi); - /* Also free the pages allocated by the driver. */ - free_receive_bufs(vi); + if (hwrings_pending) { + /* TX shared virtio-net header must be zeroed because its + * content is exposed to the host. RX shared virtio-net + * header is zeroed only for security reasons. */ + memset(&vna->shared_txvhdr, 0, sizeof(vna->shared_txvhdr)); + memset(&vna->shared_rxvhdr, 0, sizeof(vna->shared_rxvhdr)); + + /* Get and free any used buffers. This is necessary + * before calling free_unused_bufs(), that uses + * virtqueue_detach_unused_buf(). */ + virtio_netmap_clean_used_rings(vi, na); + + /* Initialize scatter-gather lists used to publish netmap + * buffers through virtio descriptors, in such a way that each + * each scatter-gather list contains exactly one descriptor + * (which can point to a netmap buffer). This initialization is + * necessary to prevent the virtio frontend (host) to think + * we are using multi-descriptors scatter-gather lists. */ + virtio_netmap_init_sgs(vi); + + /* We have to drain the RX virtqueues, otherwise the + * virtio_netmap_init_buffer() called by the subsequent + * virtnet_open() cannot link the netmap buffers to the + * virtio RX ring. + * The unused buffers point to memory allocated by + * the virtio-driver (e.g. sk_buffs). We need to free that + * memory, otherwise we have leakage. + */ + free_unused_bufs(vi); + + /* Also free the pages allocated by the driver. Since + * Linux 4.10, free_receive_bufs() takes the rtnl lock + * to support XDP. To avoid deadlock, we temporarily + * release the lock during this call. */ + rtnl_unlock(); + free_receive_bufs(vi); + rtnl_lock(); + } /* enable netmap mode */ - virtio_netmap_set_kring_mode(na, NKR_NETMAP_ON); + netmap_krings_mode_commit(na, onoff); nm_set_native_flags(na); } else { nm_clear_native_flags(na); - virtio_netmap_set_kring_mode(na, NKR_NETMAP_OFF); + netmap_krings_mode_commit(na, onoff); - /* Get and free any used buffer. This is necessary - * before calling virtqueue_detach_unused_buf(). */ - virtio_netmap_clean_used_rings(vi, na); + if (hwrings_pending) { + /* Get and free any used buffer. This is necessary + * before calling virtqueue_detach_unused_buf(). */ + virtio_netmap_clean_used_rings(vi, na); - virtio_netmap_reclaim_unused(vi); + virtio_netmap_reclaim_unused(vi); + } } if (was_up) { @@ -347,9 +362,6 @@ virtio_netmap_reg(struct netmap_adapter *na, int onoff) return (error); } -static struct virtio_net_hdr_mrg_rxbuf shared_tx_vnet_hdr; -static struct virtio_net_hdr_mrg_rxbuf shared_rx_vnet_hdr; - /* Reconcile kernel and user view of the transmit ring. */ static int virtio_netmap_txsync(struct netmap_kring *kring, int flags) @@ -366,30 +378,15 @@ virtio_netmap_txsync(struct netmap_kring *kring, int flags) /* device-specific */ COMPAT_DECL_SG + struct netmap_virtio_adapter *vna = (struct netmap_virtio_adapter *)na; struct virtnet_info *vi = netdev_priv(ifp); struct virtqueue *vq = GET_TX_VQ(vi, ring_nr); struct scatterlist *sg = GET_TX_SG(vi, ring_nr); size_t vnet_hdr_len = vi->mergeable_rx_bufs ? - sizeof(shared_tx_vnet_hdr) : - sizeof(shared_tx_vnet_hdr.hdr); + sizeof(vna->shared_txvhdr) : + sizeof(vna->shared_txvhdr.hdr); struct netmap_adapter *token; - - virtqueue_disable_cb(vq); - - /* Free used slots. We only consider our own used buffers, recognized - * by the token we passed to virtqueue_add_outbuf. - */ - n = 0; - for (;;) { - token = virtqueue_get_buf(vq, &nic_i); /* dummy 2nd arg */ - if (token == NULL) - break; - if (likely(token == na)) - n++; - } - kring->nr_hwtail += n; - if (kring->nr_hwtail > lim) - kring->nr_hwtail -= lim + 1; + int interrupts = !(kring->nr_kflags & NKR_NOINTR); /* * First part: process new packets to send. @@ -403,13 +400,12 @@ virtio_netmap_txsync(struct netmap_kring *kring, int flags) nm_i = kring->nr_hwcur; if (nm_i != head) { /* we have new packets to send */ - int nospace = 0; - nic_i = netmap_idx_k2n(kring, nm_i); for (n = 0; nm_i != head; n++) { struct netmap_slot *slot = &ring->slot[nm_i]; u_int len = slot->len; void *addr = NMB(na, slot); + int nospace; NM_CHECK_ADDR_LEN(na, addr, len); @@ -417,11 +413,11 @@ virtio_netmap_txsync(struct netmap_kring *kring, int flags) /* Initialize the scatterlist and expose it to * the hypervisor. */ COMPAT_INIT_SG(sg); - sg_set_buf(sg, &shared_tx_vnet_hdr, vnet_hdr_len); + sg_set_buf(sg, &vna->shared_txvhdr, vnet_hdr_len); sg_set_buf(sg + 1, addr, len); nospace = virtqueue_add_outbuf(vq, sg, 2, na, GFP_ATOMIC); if (nospace) { - RD(3, "virtqueue_add_outbuf failed [err=%d]", + nm_prlim(2, "virtqueue_add_outbuf failed [err=%d]", nospace); break; } @@ -433,17 +429,31 @@ virtio_netmap_txsync(struct netmap_kring *kring, int flags) virtqueue_kick(vq); /* Update hwcur depending on where we stopped. */ - kring->nr_hwcur = nm_i; /* note we migth break early */ - - /* No more free virtio descriptors or netmap slots? Ask the - * hypervisor for notifications, possibly only when a - * considerable amount of work has been done. - */ - if (nospace || nm_kr_txempty(kring)) { - virtqueue_enable_cb_delayed(vq); - } + kring->nr_hwcur = nm_i; /* note we might break early */ } out: + /* Ask the hypervisor for notifications, possibly only when it has + * freed a considerable amount of pending descriptors. */ + if (interrupts) { + virtqueue_enable_cb_delayed(vq); + } + + /* Free used slots. We only consider our own used buffers, recognized + * by the token we passed to virtqueue_add_outbuf. + */ + n = 0; + for (;;) { + token = virtqueue_get_buf(vq, &nic_i); /* dummy 2nd arg */ + if (token == NULL) + break; + if (likely(token == na)) + n++; + } + if (n) { + kring->nr_hwtail += n; + if (kring->nr_hwtail > lim) + kring->nr_hwtail -= lim + 1; + } return 0; } @@ -466,12 +476,14 @@ virtio_netmap_rxsync(struct netmap_kring *kring, int flags) /* device-specific */ COMPAT_DECL_SG + struct netmap_virtio_adapter *vna = (struct netmap_virtio_adapter *)na; struct virtnet_info *vi = netdev_priv(ifp); struct virtqueue *vq = GET_RX_VQ(vi, ring_nr); struct scatterlist *sg = GET_RX_SG(vi, ring_nr); size_t vnet_hdr_len = vi->mergeable_rx_bufs ? - sizeof(shared_rx_vnet_hdr) : - sizeof(shared_rx_vnet_hdr.hdr); + sizeof(vna->shared_rxvhdr) : + sizeof(vna->shared_rxvhdr.hdr); + int interrupts = !(kring->nr_kflags & NKR_NOINTR); /* XXX netif_carrier_ok ? */ @@ -483,18 +495,19 @@ virtio_netmap_rxsync(struct netmap_kring *kring, int flags) rmb(); /* * First part: import newly received packets. - * Only accept our - * own buffers (matching the token). We should only get - * matching buffers, because of free_unused_bufs() - * and virtio_netmap_init_buffers(). + * Only accept our own buffers (matching the token). We should only get + * matching buffers, because of free_unused_bufs() and + * virtio_netmap_init_buffers(). We may need to stop early to avoid + * hwtail to overrun hwcur; */ if (netmap_no_pendintr || force_update) { - uint16_t slot_flags = kring->nkr_slot_flags; + uint32_t hwtail_lim = nm_prev(kring->nr_hwcur, lim); struct netmap_adapter *token; + nm_i = kring->nr_hwtail; n = 0; - for (;;) { + while (nm_i != hwtail_lim) { int len; token = virtqueue_get_buf(vq, &len); if (token == NULL) @@ -503,19 +516,19 @@ virtio_netmap_rxsync(struct netmap_kring *kring, int flags) RXNUM_DEC(vi, ring_nr); if (unlikely(token != na)) { - RD(5, "Received unexpected virtqueue token %p\n", + nm_prlim(2, "Received unexpected virtqueue token %p\n", token); } else { /* Skip the virtio-net header. */ len -= vnet_hdr_len; if (unlikely(len < 0)) { - RD(5, "Truncated virtio-net-header, missing %d" + nm_prlim(2, "Truncated virtio-net-header, missing %d" " bytes", -len); len = 0; } ring->slot[nm_i].len = len; - ring->slot[nm_i].flags = slot_flags; + ring->slot[nm_i].flags = 0; nm_i = nm_next(nm_i, lim); n++; } @@ -523,7 +536,7 @@ virtio_netmap_rxsync(struct netmap_kring *kring, int flags) kring->nr_hwtail = nm_i; kring->nr_kflags &= ~NKR_PENDINTR; } - ND("[B] h %d c %d hwcur %d hwtail %d", + nm_prdis("[B] h %d c %d hwcur %d hwtail %d", ring->head, ring->cur, kring->nr_hwcur, kring->nr_hwtail); @@ -546,11 +559,11 @@ virtio_netmap_rxsync(struct netmap_kring *kring, int flags) /* Initialize the scatterlist and expose it to * the hypervisor. */ COMPAT_INIT_SG(sg); - sg_set_buf(sg, &shared_rx_vnet_hdr, vnet_hdr_len); + sg_set_buf(sg, &vna->shared_rxvhdr, vnet_hdr_len); sg_set_buf(sg + 1, addr, NETMAP_BUF_SIZE(na)); nospace = virtqueue_add_inbuf(vq, sg, 2, na, GFP_ATOMIC); if (nospace) { - RD(3, "virtqueue_add_inbuf failed [err=%d]", + nm_prlim(2, "virtqueue_add_inbuf failed [err=%d]", nospace); break; } @@ -565,10 +578,12 @@ virtio_netmap_rxsync(struct netmap_kring *kring, int flags) * the hypervisor to make a call when more used RX buffers will be * ready. */ - virtqueue_enable_cb(vq); + if (interrupts) { + virtqueue_enable_cb(vq); + } - ND("[C] h %d c %d t %d hwcur %d hwtail %d", + nm_prdis("[C] h %d c %d t %d hwcur %d hwtail %d", ring->head, ring->cur, ring->tail, kring->nr_hwcur, kring->nr_hwtail); @@ -582,16 +597,18 @@ virtio_netmap_init_buffers(struct virtnet_info *vi) { struct ifnet *ifp = vi->dev; struct netmap_adapter* na = NA(ifp); + struct netmap_virtio_adapter *vna = (struct netmap_virtio_adapter *)na; size_t vnet_hdr_len = vi->mergeable_rx_bufs ? - sizeof(shared_rx_vnet_hdr) : - sizeof(shared_rx_vnet_hdr.hdr); + sizeof(vna->shared_rxvhdr) : + sizeof(vna->shared_rxvhdr.hdr); unsigned int r; if (!nm_native_on(na)) return 0; + for (r = 0; r < na->num_rx_rings; r++) { COMPAT_DECL_SG - struct netmap_ring *ring = na->rx_rings[r].ring; + struct netmap_ring *ring = na->rx_rings[r]->ring; struct virtqueue *vq = GET_RX_VQ(vi, r); struct scatterlist *sg = GET_RX_SG(vi, r); struct netmap_slot* slot; @@ -600,25 +617,29 @@ virtio_netmap_init_buffers(struct virtnet_info *vi) slot = netmap_reset(na, NR_RX, r, 0); if (!slot) { - D("strange, null netmap ring %d", r); - return 0; + continue; } - /* Add up to na>-num_rx_desc-1 buffers to this RX virtqueue. - * It's important to leave one virtqueue slot free, otherwise - * we can run into ring->cur/ring->tail wraparounds. + /* + * Add exactly na->num_rx_desc descriptor chains to this RX + * virtqueue, as virtio_netmap_rxsync() assumes the chains + * are returned in the same order by virtqueue_get_buf(). + * It is technically possible that the hypervisor returns + * na->num_rx_desc chains before the user can consume them, + * so virtio_netmap_rxsync() must prevent ring->tail to + * wrap around ring->head. */ - for (i = 0; i < na->num_rx_desc-1; i++) { + for (i = 0; i < na->num_rx_desc; i++) { void *addr; slot = &ring->slot[i]; addr = NMB(na, slot); COMPAT_INIT_SG(sg); - sg_set_buf(sg, &shared_rx_vnet_hdr, vnet_hdr_len); + sg_set_buf(sg, &vna->shared_rxvhdr, vnet_hdr_len); sg_set_buf(sg + 1, addr, NETMAP_BUF_SIZE(na)); err = virtqueue_add_inbuf(vq, sg, 2, na, GFP_ATOMIC); if (err < 0) { - D("virtqueue_add_inbuf failed"); + nm_prerr("virtqueue_add_inbuf failed"); return 0; } @@ -627,45 +648,40 @@ virtio_netmap_init_buffers(struct virtnet_info *vi) if (VQ_FULL(vq, err)) break; } - D("added %d inbufs on queue %d", i, r); + nm_prinf("added %d inbufs on queue %d", i, r); virtqueue_kick(vq); } - return 1; } -/* Update the virtio-net device configurations. Number of queues can - * change dinamically, by 'ethtool --set-channels $IFNAME combined $N'. - * This is actually the only way virtio-net can currently enable - * the multiqueue mode. - */ -static int -virtio_netmap_config(struct netmap_adapter *na, u_int *txr, u_int *txd, - u_int *rxr, u_int *rxd) +/* Enable/disable interrupts on all virtqueues. */ +static void +virtio_netmap_intr(struct netmap_adapter *na, int onoff) { - struct ifnet *ifp = na->ifp; - struct virtnet_info *vi = netdev_priv(ifp); + struct virtnet_info *vi = netdev_priv(na->ifp); + enum txrx t; + int i; - *txr = ifp->real_num_tx_queues; - *txd = virtqueue_get_vring_size(GET_TX_VQ(vi, 0)); - *rxr = 1; - *rxd = virtqueue_get_vring_size(GET_RX_VQ(vi, 0)); - D("virtio config txq=%d, txd=%d rxq=%d, rxd=%d", - *txr, *txd, *rxr, *rxd); + for_rx_tx(t) { + for (i = 0; i < nma_get_nrings(na, t); i++) { + struct virtqueue *vq; - return 0; + vq = t == NR_RX ? GET_RX_VQ(vi, i) : GET_TX_VQ(vi, i); + + if (onoff) { + virtqueue_enable_cb(vq); + } else { + virtqueue_disable_cb(vq); + } + } + } } static void virtio_netmap_attach(struct virtnet_info *vi) { struct netmap_adapter na; /* temporary container of methods */ - - /* TX shared virtio-net header must be zeroed because its - * content is exposed to the host. RX shared virtio-net - * header is zeroed only for security reasons. */ - bzero(&shared_tx_vnet_hdr, sizeof(shared_tx_vnet_hdr)); - bzero(&shared_rx_vnet_hdr, sizeof(shared_rx_vnet_hdr)); + int ret; bzero(&na, sizeof(na)); @@ -676,12 +692,22 @@ virtio_netmap_attach(struct virtnet_info *vi) na.nm_register = virtio_netmap_reg; na.nm_txsync = virtio_netmap_txsync; na.nm_rxsync = virtio_netmap_rxsync; - na.nm_config = virtio_netmap_config; + na.nm_intr = virtio_netmap_intr; - netmap_attach(&na); + ret = netmap_attach_ext(&na, sizeof(struct netmap_virtio_adapter), 1); + if (ret) { + nm_prerr("Failed to attach virtio-net interface"); + return; + } - D("virtio attached txq=%d, txd=%d rxq=%d, rxd=%d", + nm_prinf("virtio attached txq=%d, txd=%d rxq=%d, rxd=%d", na.num_tx_rings, na.num_tx_desc, - na.num_tx_rings, na.num_rx_desc); + na.num_rx_rings, na.num_rx_desc); +} + +static inline void +nm_napi_complete(struct napi_struct *napi) +{ + napi_complete(napi); } /* end of file */ diff --git a/PORTING b/PORTING index 4cafd4e77..807b74537 100644 --- a/PORTING +++ b/PORTING @@ -43,7 +43,7 @@ Device driver patches The argument is either the ifnet or the private device descriptor. This is in foo_attach() on FreeBSD, and somewhere in the path of - XXX foo_open() in Linux + foo_probe() in Linux + near the code called on device removal, add
diff --git a/README b/README
deleted file mode 100644
index 26ea36a90..000000000
--- a/README
+++ /dev/null
@@ -1,304 +0,0 @@
-	    Netmap - a framework for fast packet I/O
-	VALE -  a Virtual Local Ethernet using the netmap API
-========================================================================
-
-NETMAP is a framework for very fast packet I/O from userspace.
-VALE is an equally fast in-kernel software switch using the netmap API.
-Both are implemented as a single kernel module for FreeBSD, Linux and
-since summer 2015, also for Windows.
-Netmap/VALE can handle tens of millions of packets per second, matching
-the speed of 10G and 40G ports even with minimum sized frames.
-See details at
-
-	http://info.iet.unipi.it/~luigi/netmap/
-
-This repository, hosted at https://github.com/luigirizzo/netmap , contains
-source code (BSD-Copyright) for FreeBSD, Linux and Windows.
-Note that recent FreeBSD distributions already include both NETMAP and VALE.
-
-
-What is this good for
----------------------
-Netmap is mostly useful for userspace applications that must deal with raw
-packets: traffic generators, sinks, monitors, loggers, software switches
-and routers, generic middleboxes, interconnection of virtual machines.
-
-The example/ directory includes pkt-gen.c (a fast traffic generator/receiver)
-and bridge.c, a simple bidirectional interconnect between two ports.
-The kernel module itself implements a learning ethernet bridge.
-
-More resources are hosted on other repositories. For example
-
-  https://github.com/luigirizzo/netmap-libpcap
-
-    contains a netmap-enabled version of libpcap (which is also
-    included in FreeBSD distribution) so you can run any libpcap client
-    on top of netmap at much higher speeds than using bpf.
-
-  https://github.com/luigirizzo/netmap-ipfw
-
-    is a userspace version of ipfw and dummynet which can handle several
-    million packets per second in a single thread
-
-Qemu/kvm has native netmap support, so it can interconnect VMs at high speed
-through netmap ports. There is experimental netmap support in the FreeBSD's
-bhyve hypervisor.
-
-
-Netmap alone DOES NOT accelerate your TCP. For that you need to implement
-your own tcp/ip stack probably using some of the techniques indicated
-below to reduce the processing costs.
-
-Architecture
-------------
-netmap uses a number of techniques to establish a fast and efficient path
-between applications and the network. In order of importance:
-
-	1. I/O batching
-	2. efficient device drivers
-	3. pre-allocated tx/rx buffers
-	4. memory mapped buffers
-
-Despite the name, memory mapping is NOT the key feature for netmap's
-speed; systems that do not apply all these techniques do not achieve
-the same speed _and_ efficiency.
-
-Netmap clients use a select()-able file descriptor to synchronize
-with the network card/software switch, and exchange multiple packets
-per system call through device-independent memory mapped buffers and
-descriptors. Device drivers are completely in the kernel, and the system
-does not rely on IOMMU or other special mechanisms.
-
-
-Installation instructions
--------------------------
-A single kernel module implements the core NETMAP functions, including
-the VALE switch and access to physical NICS using unmodified device drivers
-(at the price of much lower performance than netmap-aware drivers).
-
-Netmap-aware device drivers are needed to use netmap at high speed
-on ethernet ports.  To date, we have support for Intel ixgbe (10G),
-ixl (10/40G), e1000/e1000e/igb (1G), Realtek 8169 (1G) and Nvidia (1G).
-FreeBSD has also native netmap support in the Chelsio 10/40G cards.
-
-  FreeBSD
-  -------
-  Since recent FreeBSD distributions already include netmap, you only
-  need build the new kernel or modules as below:
-
-  + add 'device netmap' to your kernel config file and rebuild a kernel.
-    This will include the netmap module and netmap support in the device
-    drivers.  Alternatively, you can build standalone modules
-    (netmap, ixgbe, em, lem, re, igb)
-  + sample applications are in the examples/ directory in this archive,
-    or in src/tools/tools/netmap/ in FreeBSD distributions
-
-  Linux
-  -----
-  The ./configure && make build system in the LINUX/
-  directory will let you patch device driver sources and build
-  some netmap-enabled device drivers.
-  Please look at LINUX/README for details.
-
-  + make sure you have kernel headers matching your installed kernel.
-
-  + the sources for e1000e, igb, ixgbe and i40e will be downloaded
-    from the Intel e1000 project on sourceforce.
-
-  + if you need the netmap enabled drivers for e1000, veth, forcedeth,
-    virtio-net or r8169 you will also need the full kernel sources.
-
-  + Configure netmap.
-    To compile NETMAP/VALE and the Intel drivers above: 
-
-	./configure
-
-    (This will also download the Intel driver sources from sourceforce).
-    To compile only NETMAP/VALE (using unmodified drivers):
-
-	./configure --no-drivers # only netmap
-
-    If you need the full kernel sources and you have installed them in
-    /a/b/c/linux-A.B.C/, then you should do
-
-	./configure --kernel-dir=/a/b/c/linux-A.B.C/ # netmap+device drivers
-
-    You can omit --kernel-dir if your kernel sources are in a standard place.
-
-    If you use distribution packages, full sources and headers  may be in
-    different places contain headers (e.g., on debian systems). Use
-
-        ./configure --kernel-sources=/a/b/c/linux-sources-A.B/ \
-		    --kernel-dir=/a/b/c/linux-headers-A.B/
-
-  + build kernel modules and sample applications:
-
-	make
-
-  + (optionally) install the new modules and the applications:
-
-	make install # as root
-   
-    To have the new netmap-enabled driver modules alongside the original
-    ones, you may want to add --driver-suffix=-netmap to the configure
-    command above. The new drivers will then be called e1000e-netmap,
-    ixgbe-netmap and so on.
-
-   WINDOWS
-   -------
-   Netmap has been ported to Windows in summer 2015 by Alessio Faina as part of
-   his Master thesis. Please look at WINDOWS/README.txt for details.
-
-Applications
-------------
-The directory examples/ contains some programs that use the netmap API
-
-    pkt-gen.c	a packet generator/receiver working at line rate at 10Gbit/s
-    vale-cfg.c	utility to configure ports of a VALE switch
-    bridge.c	a utility that bridges two interfaces or one interface
-		with the host stack
-
-For libpcap and other applications look at the extra/ directory.
-
-Testing
--------
-pkt-gen is a generic test program which can act as a sender or receiver.
-It has a large number of options, but the simplest form is:
-
-    pkt-gen -i ix0 -f rx	# receive and print stats
-    pkt-gen -i ix0 -f tx -l 60	# send a stream of 60-byte packets
-
-(replace ix0 with the name of the interface or VALE port).
-This should be able to work at line rate (up to 14.88 Mpps on 10
-Gbit/interfaces, even higher on VALE) but note the following
-
-OPERATING SPEED
----------------
-Netmap is able to send packets at very high rates, and for simple
-packet transmission and reception, speed generally not limited by
-the CPU but by other factors (link speed, bus or NIC hw limitations).
-
-For a physical link, the maximum numer of packets per second can
-be computed with the formula:
-
-	pps = line_rate / (672 + 8 * pkt_size)
-
-where "line_rate" is the nominal link rate (e.g 10 Gbit/s) and
-pkt_size is the actual packet size including MAC headers and CRC.
-The following table summarizes some results
-
-			LINE RATE
-    pkt_size \	100M	1G	10G	40G
-
-	  64	.1488	1.488	14.88	59.52
-	 128	.0589	0.589	 5.89	23.58
-	 256	.0367	0.367	 3.67	14.70
-	 512	.0209	0.209	 2.09	 8.38
-	1024	.0113	0.113	 1.13	 4.51
-	1518	.0078	0.078	 0.78	 3.12
-
-On VALE ports, there is no physical link and the throughput is
-limited by CPU or memory depending on the packet size.
-
-COMMON PROBLEMS
----------------
-Before reporting slow send or receive speed on a physical interface,
-check ALL of the following:
-
-CANNOT SET THE DEVICE IN NETMAP MODE:
-  + make sure that the netmap module and drivers are correctly
-    loaded and can allocate all the memory they need (check into
-    /var/log/messages or equivalent)
-  + check permissions on /dev/netmap
-  + make sure the interface is up before invoking pkt-gen
-
-SENDER DOES NOT TRANSMIT
-  + some switches/interfaces take a long time to (re)negotiate
-    the link after starting pkt-gen; in case, use the -w N option
-    to increase the initial delay to N seconds;
-
-    	This may cause inability to transmit, or lost packets for
-	the first few seconds of transmission
-
-RECEIVER DOES NOT RECEIVE
-  + make sure traffic uses a broadcast MAC addresses, or the UNICAST
-    address of the receiving interface, or the receiving interface is in
-    promiscuous mode (this must be done with ifconfig; pkt-gen does not
-    change the operating mode)
-
-LOWER SPEED THAN LINE RATE
-  + check that your CPUs are running at the maximum clock rate
-    and are not throttled down by the governor/powerd.
-
-	Linux:
-		lscpu # shows current cpu speed
-		# install cpufrequtils
-		# sudo apt-get install cpufrequtils
-
-
-  + make sure that the sender/receiver interfaces and switch have
-    flow control (FC) disabled (either via sysctl or ethtool).
-
-        If FC is enabled and the receiving end is unable to cope
-	with the traffic, the driver will try to slow down transmission,
-	sometimes to very low rates.
-
-  + a lot of hardware is not able to sustain line rate. For instance,
-    ixgbe has problems with receiving frames that are not multiple
-    of 64 bytes (with/without CRC depending on the driver); also on
-    transmissions, ixgbe tops at about 12.5 Mpps unless the driver
-    prefetches tx descriptors. igb does line rate in all configurations.
-    e1000/e1000e vary between 1.15 and 1.32 Mpps. re/r8169 is
-    extremely slow in sending (max 4-500 Kpps)
-
-
-Credits
--------
-NETMAP and VALE are projects of the Universita` di Pisa,
-partially supported by various entities including:
-Intel Research Berkeley, EU FP7 projects CHANGE and OPENLAB,
-Netapp/Silicon Valley Community Foundation, ICSI
-
-Author:		Luigi Rizzo
-Contributors:
-		Giuseppe Lettieri
-		Michio Honda
-		Marta Carbone
-		Gaetano Catalli
-		Matteo Landi
-		Vincenzo Maffione
-		Stefano Garzarella
-		Alessio Faina
-
-References
-----------
-There are a few academic papers describing netmap, VALE and applications.
-You can find the papers at http://info.iet.unipi.it/~luigi/research.html
-
-+ Luigi Rizzo,
-	netmap: a novel framework for fast packet I/O,
-	Usenix ATC'12, Boston, June 2012
-
-+ Luigi Rizzo,
-	Revisiting network I/O APIs: the netmap framework,
-	Communications of the ACM 55 (3), 45-51, March 2012
-
-+ Luigi Rizzo, Marta Carbone, Gaetano Catalli,
-	Transparent acceleration of software packet forwarding using netmap,
-	IEEE Infocom 2012, Orlando, March 2012
-
-+ Luigi Rizzo, Giuseppe Lettieri,
-	VALE: a switched ethernet for virtual machines,
-	ACM Conext 2012, Nice, Dec. 2012
-
-+ Luigi Rizzo, Giuseppe Lettieri, Vincenzo Maffione,
-	Speeding up packet I/O in virtual machines,
-	IEEE/ACM ANCS 2013, San Jose, Oct. 2013
-
-+ Stefano Garzarella, Giuseppe Lettieri, Luigi Rizzo,
-	Virtual device passthrough for high speed VM networking
-	IEEE/ACM ANCS 2015, Oakland, May 2015
-
-+ Vincenzo Maffione, Luigi Rizzo, Giuseppe Lettieri,
-	Flexible virtual machine networking using netmap passthrough
-	IEEE Lanman 2016, Rome, June 2016
diff --git a/README.images b/README.images
deleted file mode 100644
index 1023c506f..000000000
--- a/README.images
+++ /dev/null
@@ -1,428 +0,0 @@
-	EXPERIMENTING WITH NETMAP, VALE AND FAST QEMU
-	---------------------------------------------
-
-***** DISCLAIMER *****
-2016-12-20
-The bootable images referenced here are still available, but they contain
-a very old version of the netmap software. As a result, also the
-instructions here are outdated, as they refer to the old images.
-In particular, the e1000-paravirt solution has been deprecated and
-replaced by a solution based on the netmap passthrough.
-New images with updated software are being produced and will be published
-soon.
-
-***** END OF DIS *****
-
-To ease experiments with Netmap, the VALE switch and our Qemu enhancements
-we have prepared a couple of bootable images (linux and FreeBSD).
-You can find them on the netmap page
-
-	http://info.iet.unipi.it/~luigi/netmap/
-
-where you can also look at more recent versions of this file.
-
-Below are step-by-step instructions on experiments you can run
-with these images. The two main versions are
-
-	picobsd.hdd	-> FreeBSD HEAD (netmap + VALE)
-	tinycore.hdd	-> Linux (qemu + netmap + VALE)
-
-Booting the image
------------------
-For all experiments you need to copy the image on a USB stick
-and boot a PC with it. Alternatively, you can use the image
-with VirtualBox, Qemu or other emulators, as an example
-
-    qemu-system-x86_64 -hda IMAGE_FILE -m 1G -machine accel=kvm ...
-
-(remove 'accel=kvm' if your host does not support kvm).
-The images do not install anything on the hard disk.
-
-Both systems have preloaded drivers for a number of network cards
-(including the intel 10 Gbit ones) with netmap extensions.
-The VALE switch is also available (it is part of the netmap module).
-ssh, scp and a few other utilities are also included.
-
-FreeBSD image:
-
-  + the OS boots directly in console mode, you can switch
-    between terminals with ALT-Fn.
-    The password for the 'root' account is 'setup'
-
-  + if you are connected to a network, you can use
-    	dhclient em0 # or other interface name
-    to obtain an IP address and external connectivity.
-
-Linux image:
-
-  + in addition to the netmap/VALE modules, the KVM kernel module
-    is also preloaded.
-
-  + the boot-loader gives you two main options (each with
-    a variant to delay boot in case you have slow devices):
-
-    + "Boot TinyCore"
-      boots in an X11 environment as user 'tc'.
-      You can create a few terminals using the icon at the
-      bottom. You can use "sudo -s" to get root access.
-      In case no suitable video card is available/detected,
-      it falls back to command line mode.
-
-    + "Boot Core (command line only)"
-      boots in console mode with virtual terminals.
-      You're automatically logged in as user 'tc'.
-      To log in the other terminals use the same username
-      (no password required).
-
-  + The system should automatically recognize the existing ethernet
-    devices, and load the appropriate netmap-capable device drivers
-    when available.  Interfaces are configured through DHCP when possible.
-
-
-General test recommendations
-----------------------------
-NOTE: The tests outlined in the following sections can generate very high
-packet rates, and some hardware misconfiguration problems may prevent
-you from achieving maximum speed.
-Common problems are:
-
-+ slow link autonegotiation.
-  Our programs typically wait 2-4 seconds for
-  link negotiation to complete, but some NIC/switch combinations
-  are much slower. In this case you should increase the delay
-  (pkt-gen has the -w XX option for that) or possibly force
-  the link speed and duplex mode on both sides.
-
-  Check the link speed to make sure there are no nogotiation
-  problems, and that you see the expected speed.
-
-    ethtool IFNAME	# on linux
-    ifconfig IFNAME	# on FreeBSD
-
-+ ethernet flow control.
-  If the receiving port is slow (often the case in presence
-  of multicast/broadcast traffic, or also unicast if you are
-  sending to non-netmap receivers), it will generate ethernet
-  flow control frames that throttle down the sender.
-
-  We recommend to disable BOTH RX and TX ethernet flow control
-  on BOTH sender and receiver.
-  On Linux this can be done with ethtool:
-
-    ethtool -A IFNAME tx off rx off
-
-  whereas on FreeBSD there are device-specific sysctl
-
-	sysctl dev.ix.0.queue0.flow_control = 0
-
-+ CPU power saving.
-  The CPU governor on linux, or equivalent in FreeBSD, tend to
-  throttle down the clock rate reducing performance.
-  Unlike other similar systems, netmap does not have busy-wait
-  loops, so the CPU load is generally low and this can trigger
-  the clock slowdown.
-
-  Make sure that ALL CPUs run at maximum speed, possibly
-  disabling the dynamic frequency-scaling mechanisms.
-
-    cpufreq-set -gperformance	# on linux
-
-    sysctl dev.cpu.0.freq=3401	# on FreeBSD.
-
-+ wrong MAC address
-  netmap does not put the NIC in promiscuous mode, so unless the
-  application does it, the NIC will only receive broadcast traffic or
-  unicast directed to its own MAC address.
-
-
-STANDARD SOCKET TESTS
----------------------
-For most socket-based experiments you can use the "netperf" tool installed
-on the system (version 2.6.0). Be careful to use a matching version for
-the other netperf endpoint (e.g. netserver) when running tests between
-different machines.
-
-Interesting experiments are:
-
-    netperf -H x.y.z.w -tTCP_STREAM  # test TCP throughput
-    netperf -H x.y.z.w -tTCP_RR      # test latency
-    netperf -H x.y.z.w -tUDP_STREAM -- -m8  # test UDP throughput with short packets
-
-where x.y.z.w is the host running "netserver".
-
-
-RAW SOCKET AND TAP TESTS
-------------------------
-For experiments with raw sockets and tap devices you can use the l2
-utilities (l2open, l2send, l2recv) installed on the system.
-With these utilities you can send/receive custom network packets
-to/from raw sockets or tap file descriptors.
-
-The receiver can be run with one of the following commands
-
-    l2open -r IFNAME l2recv     # receive from a raw socket attached to IFNAME
-    l2open -t IFNAME l2recv     # receive from a file descriptor opened on the tap IFNAME
-
-The receiver process will wait indefinitely for the first packet
-and then keep receiving as long as packets keep coming. When the
-flow stops (after a 2 seconds timeout) the process terminates and
-prints the received packet rate and packet count.
-
-To run the sender in an easy way, you can use the script l2-send.sh
-in the home directory. This script defines several shell variables
-that can be manually changed to customize the test (see
-the comments in the script itself).
-
-As an example, you can test configurations with Virtual
-Machines attached to host tap devices bridged together.
-
-
-Tests using the Linux in-kernel pktgen
---------------------------------------
-To use the Linux in-kernel packet generator, you can use the
-script "linux-pktgen.sh" in the home directory.
-The pktgen creates a kernel thread for each hardware TX queue
-of a given NIC.
-
-By manually changing the script shell variable definitions you
-can change the test configuration (e.g. addresses in the generated
-packet). Please change the "NCPU" variable to match the number
-of CPUs on your machine. The script has an argument which
-specifies the number of NIC queues (i.e. kernel threads)
-to use minus one.
-
-For example:
-
-    ./linux-pktgen.sh 2  # Uses 3 NIC queues
-
-When the script terminates, it prints the per-queue rates and
-the total rate achieved.
-
-
-NETMAP AND VALE EXPERIMENTS
----------------------------
-
-For most experiments with netmap you can use the "pkt-gen" command
-(do not confuse it with the Linux in-kernel pktgen), which has a large
-number of options to send and receive traffic (also on TAP devices).
-
-pkt-gen normally generates UDP traffic for a specific IP address
-and using the brodadcast MAC address
-
-Netmap testing with network interfaces
---------------------------------------
-
-Remember that you need a netmap-capable driver in order to use
-netmap on a specific NIC. Currently supported drivers are e1000,
-e1000e, ixgbe, igb. For updated information please visit
-http://info.iet.unipi.it/~luigi/netmap/
-
-Before running pkt-gen, make sure that the link is up.
-
-Run pkt-gen on an interface called "IFNAME":
-
-    pkt-gen -i IFNAME -f tx  # run a pkt-gen sender
-    pkt-gen -i IFNAME -f rx  # run a pkt-gen receiver
-
-pkt-gen without arguments will show other options, e.g.
-  + -w sec	modifies the wait time for link negotioation
-  + -l len	modifies the packet size
-  + -d, -s	set the IP destination/source addresses and ports
-  + -D, -S	set the MAC destination/source addresses
-
-and more.
-
-Testing the VALE switch
-------------------------
-
-To use the VALE switch instead of physical ports you only need
-to change the interface name in the pkt-gen command.
-As an example, on a single machine, you can run senders and receivers
-on multiple ports of a VALE switch as follows (run the commands into
-separate terminals to see the output)
-
-    pkt-gen -ivale0:01 -ftx  # run a sender on the port 01 of the switch vale0
-    pkt-gen -ivale0:02 -frx  # receiver on the port 02 of same switch
-    pkt-gen -ivale0:03 -ftx  # another sender on the port 03
-
-The VALE switches and ports are created (and destroyed) on the fly.
-
-
-Transparent connection of physical ports to the VALE switch
------------------------------------------------------------
-
-It is also possible to use a network device as a port of a VALE
-switch. You can do this with the following command:
-
-    vale-ctl -h vale0:eth0  # attach interface "eth0" to the "vale0" switch
-
-To detach an interface from a bridge:
-
-    vale-ctl -d vale0:eth0  # detach interface "eth0" from the "vale0" switch
-
-These operations can be issued at any moment.
-
-
-Tests with our modified QEMU
-----------------------------
-
-The Linux image also contains our modified QEMU, with the VALE backend and
-the "e1000-paravirt" frontend (a paravirtualized e1000 emulation).
-
-After you have booted the image on a physical machine (so you can exploit
-KVM), you can boot the same image a second time (recursively) with QEMU.
-Therefore, you can run all the tests above also from within the virtual
-machine environment.
-
-To make VM testing easier, the home directory contains some
-some useful scripts to set up and launch VMs on the physical machine.
-
-+ "prep-taps.sh"
-  creates and sets up two permanent tap interfaces ("tap01" and "tap02")
-  and a Linux in-kernel bridge. The tap interfaces are then bridged
-  together on the same bridge. The bridge interface ("br0"), is given
-  the address 10.0.0.200/24.
-
-  This setup can be used to make two VMs communicate through the
-  host bridge, or to test the speed of a linux switch using
-  l2open
-
-+ "unprep-taps.sh"
-  undoes the above setup.
-
-+ "launch-qemu.sh"
-  can be used to run QEMU virtual machines. It takes four arguments:
-
-    + The first argument can be "qemu" or "kvm", depending on
-      whether we want to use the standard QEMU binary translation
-      or the hardware virtualization acceleration.
-
-    + The third argument can be "--tap", "--netuser" or "--vale",
-      and tells QEMU what network backend to use: a tap device,
-      the QEMU user networking (slirp), or a VALE switch port.
-
-    + When the third argument is "--tap" or "--vale", the fourth
-      argument specifies an index (e.g. "01", "02", etc..) which
-      tells QEMU what tap device or VALE port to use as backend.
-
-  You can manually modify the script to set the shell variables that
-  select the type of emulated device (e.g.  e1000, virtio-net-pci, ...)
-  and related options (ioeventfd, virtio vhost, e1000 mitigation, ....).
-
-  The default setup has an "e1000" device with interrupt mitigation
-  disabled.
-
-You can try the paravirtualized e1000 device ("e1000-paravirt")
-or the "virtio-net" device to get better performance. However, bear
-in mind that these paravirtualized devices don't have netmap support
-(whereas the standard e1000 does have netmap support).
-
-Examples:
-
-    # Run a kvm VM attached to the port 01 of a VALE switch
-    ./launch-qemu.sh kvm --vale 01
-
-    # Run a kvm VM attached to the port 02 of the same VALE switch
-    ./launch-qemu.sh kvm --vale 02
-
-    # Run a kvm VM attached to the tap called "tap01"
-    ./launch-qemu.sh kvm --tap 01
-
-    # Run a kvm VM attached to the tap called "tap02"
-    ./launch-qemu.sh kvm --tap 02
-
-
-Guest-to-guest tests
---------------------
-
-If you run two VMs attached to the same switch (which can be a Linux
-bridge or a VALE switch), you can run guest-to-guest experiments.
-
-All the tests reported in the previous sections are possible (normal
-sockets, raw sockets, pkt-gen, ...), indipendently of the backend used.
-
-In the following examples we assume that:
-
-    + Each VM has an ethernet interface called "eth0".
-
-    + The interface of the first VM is given the IP 10.0.0.1/24.
-
-    + The interface of the second VM is given the IP 10.0.0.2/24.
-
-    + The Linux bridge interface "br0" on the host is given the
-      IP 10.0.0.200/24.
-
-Examples:
-
-    [1] ### Test UDP short packets over traditional sockets ###
-        # On the guest 10.0.0.2 run
-            netserver
-        # on the guest 10.0.0.1 run
-            netperf -H10.0.0.2 -tUDP_STREAM -- -m8
-
-    [2] ### Test UDP short packets with pkt-gen ###
-        # On the guest 10.0.0.2 run
-            pkt-gen -ieth0 -frx
-        # On the guest 10.0.0.1 run
-            pkt-gen -ieth0 -ftx
-
-    [3] ### Test guest-to-guest latency ###
-        # On the guest 10.0.0.2 run
-            netserver
-        # On the guest 10.0.0.1 run
-            netperf -H10.0.0.2 -tTCP_RR
-
-Note that you can use pkt-gen into a VM only if the emulated ethernet
-device is supported by netmap. The default emulated device is
-"e1000", which has netmap support.  If you try to run pkt-gen on
-an unsupported device, pkt-gen will not work, reporting that it is
-unable to register the interface.
-
-
-Guest-to-host tests (follows from the previous section)
--------------------------------------------------------
-
-If you run only a VM on your host machine, you can measure the
-network performance between the VM and the host machine.  In this
-case the experiment setup depends on the backend you are using.
-
-With the tap backend, you can use the bridge interface "br0" as a
-communication endpoint. You can run normal/raw sockets experiments,
-but you cannot use pkt-gen on the "br0" interface, since the Linux
-bridge interface is not supported by netmap.
-
-Examples with the tap backend:
-
-    [1] ### Test TCP throughput over traditional sockets ###
-        # On the host run
-            netserver
-        # on the guest 10.0.0.1 run
-            netperf -H10.0.0.200 -tTCP_STREAM
-
-    [2] ### Test UDP short packets with pkt-gen and l2 ###
-        # On the host run
-            l2open -r br0 l2recv
-        # On the guest 10.0.0.1 run (xx:yy:zz:ww:uu:vv is the
-        # "br0" hardware address)
-            pkt-gen -ieth0 -ftx -d10.0.0.200:7777 -Dxx:yy:zz:ww:uu:vv
-
-
-With the VALE backend you can perform only UDP tests, since we don't have
-a netmap application which implements a TCP endpoint: pkt-gen generates
-UDP packets.
-As a communication endpoint on the host, you can use a virtual VALE port
-opened on the fly by a pkt-gen instance.
-
-Examples with the VALE backend:
-
-    [1] ### Test UDP short packets ###
-        # On the host run
-            pkt-gen -ivale0:99 -frx
-        # On the guest 10.0.0.1 run
-            pkt-gen -ieth0 -ftx
-
-    [2] ### Test UDP big packets (receiver on the guest) ###
-        # On the guest 10.0.0.1 run
-            pkt-gen -ieth0 -frx
-        # On the host run pkt-gen -ivale0:99 -ftx -l1460
-
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..2342226c0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,323 @@
+# Netmap: a framework for fast packet I/O
+
+## Introduction
+
+Netmap is a an framework for very fast packet I/O from userspace.
+VALE is an equally fast in-kernel L2 software switch using the netmap API.
+Both are implemented as a single kernel module for FreeBSD and Linux.
+Netmap/VALE can handle tens of millions of packets per second, matching
+the speed of 10G and 40G ports even with minimum sized frames.
+
+To learn about netmap, you can use the following resources:
+
+* the man pages (https://www.freebsd.org/cgi/man.cgi?query=netmap&sektion=4 or
+`share/man/man4/netmap.4` in this repository)
+* the [papers](#references).
+* the tutorials, available at https://github.com/netmap-unipi/netmap-tutorial
+
+This repository contains source code (BSD-Copyright) for FreeBSD, Linux and
+Windows.
+Netmap, VALE and related applications are already included in FreeBSD
+since version 10.x. FreeBSD users should use the code included in the
+FreeBSD src tree rather than the one in this repository, although the two
+codebases are mostly aligned.
+
+## Why should I use netmap?
+
+Netmap is mostly useful for userspace applications that must deal with raw
+packets: traffic generators, sinks, monitors, loggers, software switches
+and routers, generic middleboxes, interconnection of virtual machines.
+
+The `apps/` directory includes `pkt-gen.c` (a fast traffic generator/receiver)
+and `bridge.c`, a simple bidirectional interconnect between two ports.
+The kernel module itself implements a learning ethernet bridge.
+
+More resources are hosted on other repositories. For example
+https://github.com/luigirizzo/netmap-libpcap contains a netmap-enabled version
+of libpcap (which is also included in FreeBSD distribution) so you can run
+any libpcap client on top of netmap at much higher speeds than using bpf.
+The https://github.com/luigirizzo/netmap-ipfw repository contains
+a userspace version of ipfw and dummynet which can handle several
+million packets per second in a single thread
+
+QEMU has native netmap support, so it can interconnect VMs at high speed
+through netmap ports (e.g., using VALE ports or netmap pipes).
+For maximum performance, it is also possible to pass-through any netmap port
+into a QEMU VM, as described [here](README.ptnetmap.md).
+Also the FreeBSD bhyve hypervisor has native support for netmap.
+
+Netmap alone **does not** accelerate your TCP. For that you need to implement
+your own tcp/ip stack probably using some of the techniques indicated
+below to reduce the processing costs.
+
+## Architecture
+
+netmap uses a number of techniques to establish a fast and efficient path
+between applications and the network. In order of importance:
+
+* I/O batching
+* efficient device drivers
+* pre-allocated tx/rx buffers
+* memory mapped buffers
+
+Despite the name, memory mapping is NOT the key feature for netmap's
+speed; systems that do not apply all these techniques do not achieve
+the same speed _and_ efficiency.
+
+Netmap clients use a select()-able file descriptor to synchronize
+with the network card/software switch, and exchange multiple packets
+per system call through device-independent memory mapped buffers and
+descriptors. Device drivers are completely in the kernel, and the system
+does not rely on IOMMU or other special mechanisms.
+
+
+## Installation instructions
+
+A single kernel module implements the core Netmap functions, including
+the VALE switch and access to physical NICS using unmodified device drivers
+(at the price of much lower performance than netmap-aware drivers).
+
+Netmap-aware device drivers are needed to use netmap at high speed
+on ethernet ports.  To date, we have support for Intel ixgbe (10G),
+ixl (10/40G), e1000/e1000e/igb (1G), Realtek 8169 (1G) and Nvidia (1G).
+FreeBSD has also native netmap support in the Chelsio 10/40G cards.
+
+### FreeBSD
+FreeBSD already includes netmap kernel support by
+default since version 11.
+If your kernel configuration does not include netmap, you can enable it
+by adding a `dev netmap` line, and rebuilding the kernel.
+Alternatively, you can build standalone modules (netmap, ixgbe, em, lem,
+re, igb, ...).
+
+FreeBSD users will find the netmap example applications
+in `src/tools/tools/netmap/` within the FreeBSD src tree.
+
+
+### Linux
+
+The `./configure && make` build system in the LINUX/
+directory will let you patch device driver sources and build
+some netmap-enabled device drivers.
+Please look [here](LINUX/README.md) for more instructions.
+
+Make sure you have kernel headers matching your installed kernel.
+The sources for e1000e, igb, ixgbe and i40e will be downloaded
+from the Intel e1000 project on sourceforce.
+If you need the netmap enabled drivers for e1000, veth, forcedeth,
+virtio-net or r8169 you will also need the full kernel sources.
+
+Linux users can find the netmap example applications in the `apps/`
+directory in this repository.
+
+#### Step 1
+
+Configure netmap. To compile Netmap/VALE and the Intel drivers above:
+
+	./configure
+
+(This will also download the Intel driver sources from sourceforce).
+To compile only Netmap/VALE (using unmodified drivers):
+
+	./configure --no-drivers # only netmap, no unmodified drivers
+
+If you need the full kernel sources and you have installed them in
+/a/b/c/linux-A.B.C/, then you should do
+
+	./configure --kernel-dir=/a/b/c/linux-A.B.C/ # netmap+device drivers
+
+You can omit --kernel-dir if your kernel sources are in a standard place.
+
+If you use distribution packages, full sources and headers  may be in
+different places contain headers (e.g., on debian systems). Use
+
+	./configure --kernel-sources=/a/b/c/linux-sources-A.B/ --kernel-dir=/a/b/c/linux-headers-A.B/
+
+#### Step 2
+
+Build kernel modules and sample applications:
+
+	make
+
+#### Step 3
+
+Install the new modules and the applications:
+
+	sudo make install
+
+To have the new netmap-enabled driver modules alongside the original
+ones, you may want to add `--driver-suffix=-netmap` to the configure
+command above. The new drivers will then be called `e1000e-netmap`,
+`ixgbe-netmap`, and so on.
+
+### Windows
+
+Netmap has been ported to Windows in summer 2015 by Alessio Faina as part of
+his Master thesis. You may take a look [here](WINDOWS/README.txt) for details,
+but please be aware that the port has been left behind for years, and is
+currently unmantained.
+
+## Applications
+
+The directory `apps/` contains some programs that use the netmap API
+
+* `pkt-gen.c`	a packet generator/receiver working at line rate at 10Gbit/s
+* `vale-ctl.c`	utility to configure ports of a VALE switch
+* `bridge.c`	a utility that bridges two interfaces or one interface
+		with the host stack
+
+For libpcap and other applications look at the extra/ directory.
+
+## Testing
+
+`pkt-gen` is a generic test program which can act as a sender or receiver.
+It has a large number of options, but the simplest form is:
+
+    pkt-gen -i ix0 -f rx	# receive and print stats
+    pkt-gen -i ix0 -f tx -l 60	# send a stream of 60-byte packets
+
+(replace ix0 with the name of the interface or VALE port).
+This should be able to work at line rate (up to 14.88 Mpps on 10
+Gbit/interfaces, even higher on VALE) but note the following
+
+## Operating Speed
+
+Netmap is able to send packets at very high rates, and for simple
+packet transmission and reception, speed generally not limited by
+the CPU but by other factors (link speed, bus or NIC hw limitations).
+
+For a physical link, the maximum number of packets per second can
+be computed with the formula:
+
+	pps = line_rate / (672 + 8 * pkt_size)
+
+where "line_rate" is the nominal link rate (e.g 10 Gbit/s) and
+pkt_size is the actual packet size including MAC headers and CRC.
+The following table summarizes some results (in Mpps)
+
+			LINE RATE
+    pkt_size 	100M	1G	10G	40G
+
+          64	.1488	1.488	14.88	59.52
+         128	.0589	0.589	 5.89	23.58
+         256	.0367	0.367	 3.67	14.70
+         512	.0209	0.209	 2.09	 8.38
+        1024	.0113	0.113	 1.13	 4.51
+        1518	.0078	0.078	 0.78	 3.12
+
+On VALE ports, there is no physical link and the throughput is
+limited by CPU or memory depending on the packet size.
+
+## Common problems
+
+Before reporting slow send or receive speed on a physical interface,
+check ALL of the following:
+
+### Cannot set the device in netmap mode:
+* make sure that the netmap module and drivers are correctly
+    loaded and can allocate all the memory they need (check into
+    /var/log/messages or equivalent)
+* check permissions on `/dev/netmap`
+* make sure the interface is up before invoking `pkt-gen`
+
+### Sender does not transmit
+* some switches/interfaces take a long time to (re)negotiate
+the link after starting `pkt-gen`; in case, use the -w N option
+to increase the initial delay to N seconds;
+
+This may cause inability to transmit, or lost packets for
+the first few seconds of transmission
+
+### Receiver does not receive
+* make sure traffic uses a broadcast MAC addresses, or the UNICAST
+address of the receiving interface, or the receiving interface is in
+promiscuous mode (this must be done with ifconfig; `pkt-gen` does not
+change the operating mode)
+
+### Lower speed than line rate
+* check that your CPUs are running at the maximum clock rate
+and are not throttled down by the governor/powerd.
+On Linux:
+
+	lscpu # shows current cpu speed
+	sudo apt-get install cpufrequtils
+
+* make sure that the sender/receiver interfaces and switch have
+flow control (FC) disabled (either via sysctl or ethtool).
+If FC is enabled and the receiving end is unable to cope
+with the traffic, the driver will try to slow down transmission,
+sometimes to very low rates.
+
+* a lot of hardware is not able to sustain line rate. For instance,
+ixgbe has problems with receiving frames that are not multiple
+of 64 bytes (with/without CRC depending on the driver); also on
+transmissions, ixgbe tops at about 12.5 Mpps unless the driver
+prefetches tx descriptors. igb does line rate in all configurations.
+e1000/e1000e vary between 1.15 and 1.32 Mpps. re/r8169 is
+extremely slow in sending (max 4-500 Kpps)
+
+### Host rings do not work
+
+* disable NIC offloads, because netmap does not support them and
+packets exchanged between netmap and the kernel stack can be dropped
+because of invalid checksums. On FreeBSD offloads can be disabled with
+a command like
+
+	sudo ifconfig vtnet0 -txcsum -rxcsum -tso4 -tso6 -lro -txcsum6 -rxcsum6
+
+Check [here](LINUX/README.md) for the corresponding Linux command.
+
+
+## Credits
+
+Netmap and VALE are projects of the Universita` di Pisa,
+partially supported by various entities including:
+Intel Research Berkeley, EU FP7 projects CHANGE and OPENLAB,
+Netapp/Silicon Valley Community Foundation, ICSI
+
+Authors:
+* Luigi Rizzo
+
+Contributors (https://github.com/netmap-unipi/netmap/graphs/contributors):
+
+* Giuseppe Lettieri
+* Michio Honda
+* Marta Carbone
+* Gaetano Catalli
+* Matteo Landi
+* Vincenzo Maffione
+* Stefano Garzarella
+* Alessio Faina
+
+## References
+
+There are a few academic papers describing netmap, VALE and applications.
+You can find the papers at http://info.iet.unipi.it/~luigi/research.html
+
+* Luigi Rizzo,
+	netmap: a novel framework for fast packet I/O,
+	Usenix ATC'12, Boston, June 2012
+
+* Luigi Rizzo,
+	Revisiting network I/O APIs: the netmap framework,
+	Communications of the ACM 55 (3), 45-51, March 2012
+
+* Luigi Rizzo, Marta Carbone, Gaetano Catalli,
+	Transparent acceleration of software packet forwarding using netmap,
+	IEEE Infocom 2012, Orlando, March 2012
+
+* Luigi Rizzo, Giuseppe Lettieri,
+	VALE: a switched ethernet for virtual machines,
+	ACM Conext 2012, Nice, Dec. 2012
+
+* Luigi Rizzo, Giuseppe Lettieri, Vincenzo Maffione,
+	Speeding up packet I/O in virtual machines,
+	IEEE/ACM ANCS 2013, San Jose, Oct. 2013
+
+* Stefano Garzarella, Giuseppe Lettieri, Luigi Rizzo,
+	Virtual device passthrough for high speed VM networking
+	IEEE/ACM ANCS 2015, Oakland, May 2015
+
+* Vincenzo Maffione, Luigi Rizzo, Giuseppe Lettieri,
+	Flexible virtual machine networking using netmap passthrough
+	IEEE Lanman 2016, Rome, June 2016
diff --git a/README.ptnetmap.md b/README.ptnetmap.md
new file mode 100644
index 000000000..219597759
--- /dev/null
+++ b/README.ptnetmap.md
@@ -0,0 +1,231 @@
+# Netmap passthrough howto
+
+## 1. Introduction
+
+This document describes how to configure netmap passthrough, a technology
+that enables very fast network I/O (up to 30 Mpps and more) for QEMU Virtual
+Machines.
+With netmap passthrough you can make an arbitrary netmap port (physical NIC,
+VALE port, pipe endpoint, monitor, ...) available inside a VM. In this way
+your (unmodified) netmap application can run isolated inside a VM, without
+losing the performance advantages of netmap. In particular you will still
+able to zerocopy across the passed-through netmap ports (a.k.a. ptnetmap
+ports).
+Netmap passthrough requires support in both host (hypervisor) and guest OS.
+Host needs a ptnetmap-capable hypervisor like QEMU (Linux host with KVM
+enabled) or bhyve (FreeBSD host). Guest OS requires some ptnetmap drivers that
+are already included with netmap, although not enabled by default.
+Guest OS ptnetmap drivers are available for both Linux and FreeBSD guests.
+
+Netmap passthrough is an enabler technology for Network Function
+Virtualization, as it can be used to build chains of VMs for high-rate
+middlebox packet processing. Given the variety
+of netmap ports you can decide to connect the VMs together through
+zerocopy ports (i.e. netmap pipes), or with copy for untrusted VMs
+(i.e. VALE ports). You can get NIC-independent NIC passthrough by
+directly passing a dedicated physical netmap port to a VM.
+
+More information about ptnetmap are available in these slides:
+
+* https://github.com/vmaffione/netmap-tutorial/blob/master/virtualization.pdf
+
+and in these papers
+
+* http://info.iet.unipi.it/~luigi/papers/20160613-ptnet.pdf
+* http://info.iet.unipi.it/~luigi/papers/20150315-netmap-passthrough.pdf (older)
+
+and in section 7 of this document.
+
+## 2. Configure Linux host and QEMU for ptnetmap
+
+On the Linux host, configure, build and install netmap normally:
+
+	git clone https://github.com/luigirizzo/netmap.git
+	cd netmap
+	./configure [options]
+	make
+	sudo make install
+
+Download, build and install the ptnetmap-enabled QEMU:
+
+	git clone https://github.com/netmap-unipi/qemu
+	cd qemu
+	./configure --target-list=x86_64-softmmu --enable-kvm --enable-vhost-net --disable-werror --enable-netmap
+	make
+	sudo make install
+
+Load the netmap
+
+	sudo modprobe netmap
+
+Example to run a VM passing through a VALE port (vale1:10):
+
+	sudo qemu-system-x86_64 img.qcow2 -enable-kvm -smp 2 -m 2G -vga std -device ptnet-pci,netdev=data10,mac=00:AA:BB:CC:0a:0a -netdev netmap,ifname=vale1:10,id=data10,passthrough=on
+
+Example to run a VM passing though the "left" endpoints of two pipes endpoints
+(the "right" endpoints can be connected to other VMs or netmap programs running
+directly on the host.
+
+	sudo qemu-system-x86_64 img.qcow2 -enable-kvm -smp 2 -m 2G -vga std -device ptnet-pci,netdev=data1,mac=00:AA:BB:CC:0b:01 -netdev netmap,ifname=netmap:pipe0{1,id=data1,passthrough=on -device ptnet-pci,netdev=data1,mac=00:AA:BB:CC:0b:02 -netdev netmap,ifname=netmap:pipe1{1,id=data1,passthrough=on
+
+
+## 3. Configure FreeBSD host and bhyve for ptnetmap
+TODO
+
+
+## 4. Configure Linux guest for ptnetmap
+
+In the Linux guest, compile, build and install netmap with ptnetmap support:
+
+	git clone https://github.com/luigirizzo/netmap.git
+	cd netmap
+	./configure --enable-ptnetmap
+	make
+	sudo make install
+
+Load netmap module
+
+	sudo rmmod netmap  # Possibly remove a previous netmap module:
+	sudo modprobe netmap
+
+As the netmap module is loaded, a new network interface will show up for each
+passed-through netmap port, (e.g. 'ens4'). You can check that an interface is
+a netmap passthrough one checking the driver:
+
+	ethtool -i ens4
+	  driver: ptnetmap-guest-drivers
+	  version:
+	  [...]
+
+A guest ptnetmap port behaves like any other netmap ports. You can use pkt-gen
+to test transmission;
+
+	sudo pkt-gen -i ens4 -f tx
+
+
+## 5. Use ptnetmap with FreeBSD guests
+
+Netmap passthrough guest drivers are already included with netmap from FreeBSD
+12 versions. When running FreeBSD guest with ptnetmap ports (e.g. using QEMU as
+described above), an interface called "ptnet$N" will show up for each passed
+through port.
+If you want to use ptnetmap with older FreeBSD guests you can just update your
+FreeBSD source tree with the updated netmap code from github and rebuild your
+kernel.
+
+
+## 6. ptnetmap tunables
+
+While ptnetmap is mainly designed for the VMs to run middleboxes applications
+(e.g. firewall, DDoS prevention, load balancing, IDS, typically carried out
+by network operators), it also offers good performance when VMs run TCP/UDP
+user applications. To make this possible, virtualized offloadings are
+supported using the virtio-net header defined by the VirtIO standard
+(http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html#x1-1680006).
+When the header is enabled in the guest OS and supported by the VM network
+backend (e.g. VALE port and TAP devices both support it), two VMs on the
+same host (e.g. connected through a VALE switch) can exchange TSO packets
+(up to 64KB each) without the need to perform any TCP segmentation or
+computing TCP checksums.
+This header is the key for very high TCP VM-to-VM throughput (20+ Gbps),
+and it is stored before the ethernet header of each packet sent or received
+by your VMs.
+If you want to use ptnetmap mainly to run middleboxes application (which
+is the common case), you should disable the virtio-net header in the guest
+OS:
+
+	# echo 0 > /sys/module/netmap/parameters/ptnet_vnet_hdr
+
+This step is needed to avoid performance issues in case your datapath exits the
+hypervisor host through a physical NIC or goes through netmap ports that don't
+support the virtio-net header.
+
+
+## 7. Some background about ptnetmap
+
+Netmap is a framework for high performance network I/O. It exposes an
+hardware-independent API which allows userspace application to directly interact
+with NIC hardware rings, in order to receive and transmit Ethernet frames.
+Rings are always accessed in the context of system calls and NIC interrupts
+are used to notify applications about NIC processing completion.
+The performance boost of netmap w.r.t. traditional socket API primarily comes
+from: (i) batching, since it is possible to send/receive hundreds of packets
+with a single system call, (ii) preallocation of packet buffers and memory
+mapping of those in the application address space.
+
+Several netmap extension have been developed to support virtualization.
+Netmap support for various paravirtualized drivers - e.g. virtio-net, Xen
+netfront/netback - allows netmap applications to run in the guest over fast
+paravirtualized I/O devices.
+
+The Virtual Ethernet (VALE) software switch, which supports scalable high
+performance local communication (over 20 Mpps between two switch ports), can
+then be used to connect together multiple VMs.
+
+However, in a typical scenario with two communicating netmap applications
+running in different VMs (on the same host) connected through a VALE switch,
+the journey of a packet is still quite convoluted. As a matter of facts,
+while netmap is fast on both the host (the VALE switch) and the guest
+(interaction between application and the emulated device), each packet still
+needs to be processed from the hypervisor, which needs to emulate the
+device model used in the guest (e.g. e1000, virtio-net). The emulation
+involves device-specific overhead - queue processing, format conversions,
+packet copies, address translations, etc. As a consequence, the maximum
+packet rate between the two VMs is often limited by 2-5 Mpps.
+
+To overcome these limitations, ptnetmap has been introduced as a passthrough
+technique to completely avoid hypervisor processing in the packet
+datapath, unblocking the full potential of netmap also for virtual machine
+environments.
+With ptnetmap, a netmap port on the host can be exposed to the guest in a
+protected way, so that netmap applications in the guest can directly access
+the rings and packet buffers of the host port, avoiding all the extra overhead
+involved in the emulation of network devices. System calls issued by guest
+applications on ptnetmap ports are served by kernel threads (one
+per ring) running in the netmap host.
+
+Similarly to VirtIO paravirtualization, synchronization between
+guest netmap (driver) and host netmap (kernel threads) happens through a
+shared memory area called Communication Status Block (CSB), which is used
+to store producer-consumer state and notification suppression flags.
+
+Two notification mechanisms needs to be supported by the hypervisor to allow
+guest and host netmap to wake up each other.
+On QEMU/bhyve, notifications from guest to host are implemented with accesses
+to I/O registers which cause a trap in the hypervisor. Notifications in the
+other direction are implemented using KVM/bhyve interrupt injection mechanisms.
+MSI-X interrupts are used since they have less overhead than traditional
+PCI interrupts.
+
+Since I/O register accesses and interrupts are very expensive in the common
+case of hardware assisted virtualization, they are suppressed when not needed,
+i.e. each time the host (or the guest) is actively polling the CSB to
+check for more work. From an high-level perspective, the system tries to
+dynamically switch between polling operation under high load, and
+interrupt-based operation under lower loads.
+
+The original ptnetmap implementation required ptnetmap-enabled virtio-net/e1000
+drivers. Only the notification functionalities of those devices were reused,
+while the datapath (e.g. e1000 rings or virtio-net Virtual Queues) was
+completely bypassed.
+
+The ptnet device has been introduced as a cleaner approach to ptnetmap that
+also adds the ability to interact with the standard TCP/IP network stack
+and supports multi-ring netmap ports. The introduction of a new device model
+does not limit the adoption of this solution, since ptnet drivers are
+distributed together with netmap, and hypervisor modifications are needed in
+any case.
+
+The ptnet device belongs to the classes of paravirtualized devices, like
+virtio-net. Unlike virtio-net, however, ptnet does not define an interface
+to exchange packets (datapath), but the existing netmap API is used instead.
+However, a CSB - cleaned up and extended to support an arbitrary number of
+rings - is still used for producer-consumer synchronization and notification
+suppression.
+
+A number of device registers are used for configuration (number of rings and
+slots, device MAC address, supported features, ...) while "kick" registers
+are used for guest-to-host notifications.
+The ptnetmap kthread infrastructure, moreover, has been already extended to
+support an arbitrary number of rings, where currently each ring is served
+by a different kernel thread.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 000000000..2528b0ade
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,5 @@
+Reporting a Vulnerability
+=========================
+
+Please report suspected vulnerabilities to giuseppe.lettieri@unipi.it. You will receive a response as soon as possible.
+Patches will target the master branch.
diff --git a/WINDOWS/Loader/loader.vcxproj b/WINDOWS/Loader/loader.vcxproj
index be67f679c..e63660d39 100644
--- a/WINDOWS/Loader/loader.vcxproj
+++ b/WINDOWS/Loader/loader.vcxproj
@@ -1,7 +1,22 @@
 īģŋ
 
   
-
+    
+      Win10 Debug
+      Win32
+    
+    
+      Win10 Debug
+      x64
+    
+    
+      Win10 Release
+      Win32
+    
+    
+      Win10 Release
+      x64
+    
     
       Win8.1 Debug
       Win32
@@ -38,12 +53,10 @@
       Win7 Debug
       x64
     
-
     
       Win8.1 Release
       x64
     
-
     
       Win8 Release
       x64
@@ -53,24 +66,22 @@
       x64
     
   
-
   
     {A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}
     $(MSBuildProjectName)
-   
+    
     Win8.1 Debug
     Win32
     {BA5AA988-42F7-47EE-8963-173B9E7F94C5}
     loader
+    10.0.14393.0
   
   
-
   
     
-    WindowsApplicationForDrivers8.1
+    WindowsApplicationForDrivers10.0
     Application
   
-
   
     Win7
     False
@@ -83,6 +94,11 @@
     WindowsV6.3
     False
   
+  
+    
+    
+    False
+  
   
     Win7
     True
@@ -95,7 +111,11 @@
     WindowsV6.3
     True
   
-
+  
+    
+    
+    True
+  
   
   
     $(SolutionDir)\Output-$(ConfigurationName)\
@@ -112,8 +132,8 @@
   
   
     
-      true
-      Level4
+      false
+      Level3
       %(AdditionalIncludeDirectories);..\sys
     
     
@@ -145,4 +165,4 @@
     
   
   
-
+
\ No newline at end of file
diff --git a/WINDOWS/Makefile b/WINDOWS/Makefile
index 1de23435e..d4184846f 100644
--- a/WINDOWS/Makefile
+++ b/WINDOWS/Makefile
@@ -81,6 +81,7 @@ SRCS	+= netmap_mem2.c
 SRCS	+= netmap_monitor.c
 SRCS	+= netmap_pipe.c
 SRCS	+= netmap_vale.c
+SRCS	+= netmap_legacy.c
 SRCS	+= netmap_windows.c
 SRCS	+= win_glue.c
 
diff --git a/WINDOWS/README.txt b/WINDOWS/README.txt
index e6651a5ef..38fbaa247 100644
--- a/WINDOWS/README.txt
+++ b/WINDOWS/README.txt
@@ -1,3 +1,9 @@
+**************************************************************
+DISCLAIMER: The Windows port of netmap is currently unmantained.  Not even
+compilation is guaranteed.  Moreover, it is know to contain security-critcal
+bugs.
+**************************************************************
+
 This directory contains the Windows version of netmap, developed by
 Alessio Faina as part of his MS thesis at the Universita` di Pisa.
 
@@ -10,7 +16,7 @@ which also build the standard netmap test program, pkt-gen.
 	ports.  Performance is similar to that on FreeBSD and Linux:
 	20Mpps on switch ports, over 100 Mpps on pipes.
 
- 	To load the module, do the following (as adminstrator)
+	To load the module, do the following (as administrator)
 
 	    (cd Output-Win8.1Release/netmap-pkg; ./nm-loader l)
 
@@ -37,7 +43,7 @@ which also build the standard netmap test program, pkt-gen.
 		netmap:ethXX
 
 	as the port name. XX is the Windows "interface index" that
-	can be shown with the followin command (or many other ways):
+	can be shown with the following command (or many other ways):
 
 		netsh int ipv4 show interfaces
 
@@ -77,7 +83,7 @@ a) Build with command line tools and MsBuild.exe
 
 	make clean	# will clean output directories
 
-   The output will be found in the directory ./Output-
+   The output will be found in the directory ./Output-
 
    Please look at the makefile to select different configurations
 
@@ -156,7 +162,7 @@ native netmap mode available on FreeBSD and Linux).
 	pkt-gen-b -i vale0:a{1 -f rx
 
     NETMAP to HOST ring	about 2.3 Mpps if dropped, 1.8Mpps to windump
-       (replace the '5' with the inteface index from
+       (replace the '5' with the interface index from
 		netsh int ipv4 show interfaces
 
 	pkt-gen-b -i netmap:eth5^ -f tx	# on one vm
@@ -209,7 +215,7 @@ provide a similar one for the filter).
 
 To build the kernel modules we use the compiler from Visual Studio.
 
-For convenience, we have construted the "solution" file and the various
+For convenience, we have constructed the "solution" file and the various
 project files with VSC, and then manually cleaned up the .vcxprj files
 to remove the infinite copies of the same set of options generated
 by the GUI. The configurations include instructions to sign the drivers
diff --git a/WINDOWS/includes/net/ethernet.h b/WINDOWS/includes/net/ethernet.h
index 4bb61c550..a677a8a17 100644
--- a/WINDOWS/includes/net/ethernet.h
+++ b/WINDOWS/includes/net/ethernet.h
@@ -1,8 +1,5 @@
 /*
  * Fundamental constants relating to ethernet.
- *
- * $FreeBSD: head/sys/net/ethernet.h 249925 2013-04-26 12:50:32Z glebius $
- *
  */
 
 #ifndef _NET_ETHERNET_H_
@@ -43,7 +40,7 @@
 #define	M_HASFCS	M_PROTO5	/* FCS included at end of frame */
 
 /*
- * Ethernet CRC32 polynomials (big- and little-endian verions).
+ * Ethernet CRC32 polynomials (big- and little-endian versions).
  */
 #define	ETHER_CRC_POLY_LE	0xedb88320
 #define	ETHER_CRC_POLY_BE	0x04c11db6
diff --git a/WINDOWS/includes/net/if_dl.h b/WINDOWS/includes/net/if_dl.h
index 4d2b4f743..cfad7acb5 100644
--- a/WINDOWS/includes/net/if_dl.h
+++ b/WINDOWS/includes/net/if_dl.h
@@ -27,7 +27,6 @@
  * SUCH DAMAGE.
  *
  *	@(#)if_dl.h	8.1 (Berkeley) 6/10/93
- * $FreeBSD: src/sys/net/if_dl.h,v 1.14 2005/01/07 01:45:34 imp Exp $
  */
 
 #ifndef _NET_IF_DL_H_
diff --git a/WINDOWS/netmap-pkg.vcxproj b/WINDOWS/netmap-pkg.vcxproj
index a80e60f42..b232745c2 100644
--- a/WINDOWS/netmap-pkg.vcxproj
+++ b/WINDOWS/netmap-pkg.vcxproj
@@ -1,32 +1,93 @@
 īģŋ
 
-
   
     Utility
     Package
     true
-    WindowsKernelModeDriver8.1
+    WindowsKernelModeDriver10.0
   
-
-
+  
+    
+      Win10 Debug
+      Win32
+    
+    
+      Win10 Debug
+      x64
+    
+    
+      Win10 Release
+      Win32
+    
+    
+      Win10 Release
+      x64
+    
+    
+      Win8.1 Debug
+      Win32
+    
+    
+      Win8.1 Release
+      Win32
+    
+    
+      Win8 Debug
+      Win32
+    
+    
+      Win8 Release
+      Win32
+    
+    
+      Win7 Debug
+      Win32
+    
+    
+      Win7 Release
+      Win32
+    
+    
+      Win8.1 Debug
+      x64
+    
+    
+      Win8.1 Release
+      x64
+    
+    
+      Win8 Debug
+      x64
+    
+    
+      Win8 Release
+      x64
+    
+    
+      Win7 Debug
+      x64
+    
+    
+      Win7 Release
+      x64
+    
+  
   
     {72648F4C-6AAA-4E69-99C9-66D9D6400EE9}
     {4605da2c-74a5-4865-98e1-152ef136825f}
     netmap-pkg
+    10.0.14393.0
   
-
   
-
   
   
-
   
     
   
-
   
-  
-
+  
+    true
+  
   
     DbgengKernelDebugger
     False
@@ -39,29 +100,33 @@
     True
     
     133563
-    $(SolutionDir)\Output-$(ConfigurationName)\
-    $(SolutionDir)\Output-$(ConfigurationName)\tmp\netmap-pkg\
+    $(SolutionDir)Output-$(ConfigurationName)\
+    $(SolutionDir)Output-$(ConfigurationName)\tmp\$(ProjectName)\
+  
+  
+    true
+  
+  
+    true
+  
+  
+    true
   
-
   
     
       4
       true
     
   
-
   
     
   
-
   
     
       {789055e4-2677-413d-a638-3e2aed7c7427}
     
   
-
   
-
   
   
-
+
\ No newline at end of file
diff --git a/WINDOWS/netmap-pkg.vcxproj.user b/WINDOWS/netmap-pkg.vcxproj.user
index 469728d61..50f0ac150 100644
--- a/WINDOWS/netmap-pkg.vcxproj.user
+++ b/WINDOWS/netmap-pkg.vcxproj.user
@@ -21,4 +21,16 @@
   
     TestSign
   
+  
+    CN="WDKTestCert Ali,131544860649071360" | 10000FDB00D12EF1C204354F46F27C83B8A54118
+  
+  
+    CN="WDKTestCert Ali,131544860649071360" | 10000FDB00D12EF1C204354F46F27C83B8A54118
+  
+  
+    CN="WDKTestCert Ali,131544860649071360" | 10000FDB00D12EF1C204354F46F27C83B8A54118
+  
+  
+    CN="WDKTestCert Ali,131544860649071360" | 10000FDB00D12EF1C204354F46F27C83B8A54118
+  
 
\ No newline at end of file
diff --git a/WINDOWS/netmap.inf b/WINDOWS/netmap.inf
index dd9509a6e..32e221e6a 100644
--- a/WINDOWS/netmap.inf
+++ b/WINDOWS/netmap.inf
@@ -5,7 +5,7 @@
 [Version]
 Signature="$WINDOWS NT$"
 Class=System
-ClassGuid={9BF68A6C-4C18-4FB4-A2FD-0E54B36F9252}
+ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}
 Provider=%ManufacturerName%
 DriverVer=05/18/2015,0.0.1.1
 CatalogFile=Netmap.cat
@@ -46,7 +46,7 @@ DelService=netmap,0x200 ; SPSVCINST_STOPSERVICE
 [Netmap_Service_Inst]
 DisplayName    = %ClassName%
 ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
-StartType      = 2               ; SERVICE_AUTO_START 
+StartType      = 2               ; SERVICE_AUTO_START
 ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
 ServiceBinary  = %12%\netmap.sys
 ;LoadOrderGroup = Extended Base
diff --git a/WINDOWS/netmap.sln b/WINDOWS/netmap.sln
index 386351496..865400a05 100644
--- a/WINDOWS/netmap.sln
+++ b/WINDOWS/netmap.sln
@@ -1,7 +1,7 @@
 īģŋ
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.30501.0
+# Visual Studio 14
+VisualStudioVersion = 14.0.25123.0
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netmap", "netmap.vcxproj", "{789055E4-2677-413D-A638-3E2AED7C7427}"
 EndProject
@@ -19,8 +19,9 @@ EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nm-ndis", "nm-ndis\nm-ndis.vcxproj", "{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}"
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nm-ndis-pkg", "nm-ndis\nm-ndis-pkg.vcxproj", "{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sysctl", "Sysctl\Sysctl.vcxproj", "{C315D4A0-BBDC-46C4-973A-DE631517FA48}"
+	ProjectSection(ProjectDependencies) = postProject
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10} = {86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}
+	EndProjectSection
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -28,6 +29,10 @@ Global
 		Debug|x64 = Debug|x64
 		Release|Win32 = Release|Win32
 		Release|x64 = Release|x64
+		Win10 Debug|Win32 = Win10 Debug|Win32
+		Win10 Debug|x64 = Win10 Debug|x64
+		Win10 Release|Win32 = Win10 Release|Win32
+		Win10 Release|x64 = Win10 Release|x64
 		Win7 Debug|Win32 = Win7 Debug|Win32
 		Win7 Debug|x64 = Win7 Debug|x64
 		Win7 Release|Win32 = Win7 Release|Win32
@@ -52,6 +57,18 @@ Global
 		{789055E4-2677-413D-A638-3E2AED7C7427}.Release|x64.ActiveCfg = Win8.1 Release|x64
 		{789055E4-2677-413D-A638-3E2AED7C7427}.Release|x64.Build.0 = Win8.1 Release|x64
 		{789055E4-2677-413D-A638-3E2AED7C7427}.Release|x64.Deploy.0 = Win8.1 Release|x64
+		{789055E4-2677-413D-A638-3E2AED7C7427}.Win10 Debug|Win32.ActiveCfg = Win10 Debug|Win32
+		{789055E4-2677-413D-A638-3E2AED7C7427}.Win10 Debug|Win32.Build.0 = Win10 Debug|Win32
+		{789055E4-2677-413D-A638-3E2AED7C7427}.Win10 Debug|Win32.Deploy.0 = Win10 Debug|Win32
+		{789055E4-2677-413D-A638-3E2AED7C7427}.Win10 Debug|x64.ActiveCfg = Win10 Debug|x64
+		{789055E4-2677-413D-A638-3E2AED7C7427}.Win10 Debug|x64.Build.0 = Win10 Debug|x64
+		{789055E4-2677-413D-A638-3E2AED7C7427}.Win10 Debug|x64.Deploy.0 = Win10 Debug|x64
+		{789055E4-2677-413D-A638-3E2AED7C7427}.Win10 Release|Win32.ActiveCfg = Win10 Release|Win32
+		{789055E4-2677-413D-A638-3E2AED7C7427}.Win10 Release|Win32.Build.0 = Win10 Release|Win32
+		{789055E4-2677-413D-A638-3E2AED7C7427}.Win10 Release|Win32.Deploy.0 = Win10 Release|Win32
+		{789055E4-2677-413D-A638-3E2AED7C7427}.Win10 Release|x64.ActiveCfg = Win10 Release|x64
+		{789055E4-2677-413D-A638-3E2AED7C7427}.Win10 Release|x64.Build.0 = Win10 Release|x64
+		{789055E4-2677-413D-A638-3E2AED7C7427}.Win10 Release|x64.Deploy.0 = Win10 Release|x64
 		{789055E4-2677-413D-A638-3E2AED7C7427}.Win7 Debug|Win32.ActiveCfg = Win7 Debug|Win32
 		{789055E4-2677-413D-A638-3E2AED7C7427}.Win7 Debug|Win32.Build.0 = Win7 Debug|Win32
 		{789055E4-2677-413D-A638-3E2AED7C7427}.Win7 Debug|x64.ActiveCfg = Win7 Debug|x64
@@ -92,6 +109,18 @@ Global
 		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Release|x64.ActiveCfg = Win8.1 Release|x64
 		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Release|x64.Build.0 = Win8.1 Release|x64
 		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Release|x64.Deploy.0 = Win8.1 Release|x64
+		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win10 Debug|Win32.ActiveCfg = Win10 Debug|Win32
+		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win10 Debug|Win32.Build.0 = Win10 Debug|Win32
+		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win10 Debug|Win32.Deploy.0 = Win10 Debug|Win32
+		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win10 Debug|x64.ActiveCfg = Win10 Debug|x64
+		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win10 Debug|x64.Build.0 = Win10 Debug|x64
+		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win10 Debug|x64.Deploy.0 = Win10 Debug|x64
+		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win10 Release|Win32.ActiveCfg = Win10 Release|Win32
+		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win10 Release|Win32.Build.0 = Win10 Release|Win32
+		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win10 Release|Win32.Deploy.0 = Win10 Release|Win32
+		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win10 Release|x64.ActiveCfg = Win10 Release|x64
+		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win10 Release|x64.Build.0 = Win10 Release|x64
+		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win10 Release|x64.Deploy.0 = Win10 Release|x64
 		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win7 Debug|Win32.ActiveCfg = Win7 Debug|Win32
 		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win7 Debug|Win32.Build.0 = Win7 Debug|Win32
 		{72648F4C-6AAA-4E69-99C9-66D9D6400EE9}.Win7 Debug|x64.ActiveCfg = Win7 Debug|x64
@@ -134,6 +163,14 @@ Global
 		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Release|x64.ActiveCfg = Win8.1 Release|x64
 		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Release|x64.Build.0 = Win8.1 Release|x64
 		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Release|x64.Deploy.0 = Win8.1 Release|x64
+		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Win10 Debug|Win32.ActiveCfg = Win10 Debug|Win32
+		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Win10 Debug|Win32.Build.0 = Win10 Debug|Win32
+		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Win10 Debug|x64.ActiveCfg = Win10 Debug|x64
+		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Win10 Debug|x64.Build.0 = Win10 Debug|x64
+		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Win10 Release|Win32.ActiveCfg = Win10 Release|Win32
+		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Win10 Release|Win32.Build.0 = Win10 Release|Win32
+		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Win10 Release|x64.ActiveCfg = Win10 Release|x64
+		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Win10 Release|x64.Build.0 = Win10 Release|x64
 		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Win7 Debug|Win32.ActiveCfg = Win7 Debug|Win32
 		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Win7 Debug|Win32.Build.0 = Win7 Debug|Win32
 		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED}.Win7 Debug|Win32.Deploy.0 = Win7 Debug|Win32
@@ -181,6 +218,18 @@ Global
 		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Release|x64.ActiveCfg = Win8.1 Release|x64
 		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Release|x64.Build.0 = Win8.1 Release|x64
 		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Release|x64.Deploy.0 = Win8.1 Release|x64
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win10 Debug|Win32.ActiveCfg = Win10 Debug|Win32
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win10 Debug|Win32.Build.0 = Win10 Debug|Win32
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win10 Debug|Win32.Deploy.0 = Win10 Debug|Win32
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win10 Debug|x64.ActiveCfg = Win10 Debug|x64
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win10 Debug|x64.Build.0 = Win10 Debug|x64
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win10 Debug|x64.Deploy.0 = Win10 Debug|x64
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win10 Release|Win32.ActiveCfg = Win10 Release|Win32
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win10 Release|Win32.Build.0 = Win10 Release|Win32
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win10 Release|Win32.Deploy.0 = Win10 Release|Win32
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win10 Release|x64.ActiveCfg = Win10 Release|x64
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win10 Release|x64.Build.0 = Win10 Release|x64
+		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win10 Release|x64.Deploy.0 = Win10 Release|x64
 		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win7 Debug|Win32.ActiveCfg = Win7 Debug|Win32
 		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win7 Debug|Win32.Build.0 = Win7 Debug|Win32
 		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}.Win7 Debug|Win32.Deploy.0 = Win7 Debug|Win32
@@ -229,6 +278,18 @@ Global
 		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Release|x64.ActiveCfg = Win8.1 Release|x64
 		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Release|x64.Build.0 = Win8.1 Release|x64
 		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Release|x64.Deploy.0 = Win8.1 Release|x64
+		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win10 Debug|Win32.ActiveCfg = Win10 Debug|Win32
+		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win10 Debug|Win32.Build.0 = Win10 Debug|Win32
+		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win10 Debug|Win32.Deploy.0 = Win10 Debug|Win32
+		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win10 Debug|x64.ActiveCfg = Win10 Debug|x64
+		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win10 Debug|x64.Build.0 = Win10 Debug|x64
+		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win10 Debug|x64.Deploy.0 = Win10 Debug|x64
+		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win10 Release|Win32.ActiveCfg = Win10 Release|Win32
+		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win10 Release|Win32.Build.0 = Win10 Release|Win32
+		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win10 Release|Win32.Deploy.0 = Win10 Release|Win32
+		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win10 Release|x64.ActiveCfg = Win10 Release|x64
+		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win10 Release|x64.Build.0 = Win10 Release|x64
+		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win10 Release|x64.Deploy.0 = Win10 Release|x64
 		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win7 Debug|Win32.ActiveCfg = Win7 Debug|Win32
 		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win7 Debug|Win32.Build.0 = Win7 Debug|Win32
 		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win7 Debug|Win32.Deploy.0 = Win7 Debug|Win32
@@ -265,22 +326,6 @@ Global
 		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win8.1 Release|x64.ActiveCfg = Win8.1 Release|x64
 		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win8.1 Release|x64.Build.0 = Win8.1 Release|x64
 		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}.Win8.1 Release|x64.Deploy.0 = Win8.1 Release|x64
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Debug|Win32.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Debug|x64.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Release|Win32.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Release|x64.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Win7 Debug|Win32.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Win7 Debug|x64.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Win7 Release|Win32.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Win7 Release|x64.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Win8 Debug|Win32.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Win8 Debug|x64.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Win8 Release|Win32.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Win8 Release|x64.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Win8.1 Debug|Win32.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Win8.1 Debug|x64.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Win8.1 Release|Win32.ActiveCfg = Release|Win32
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48}.Win8.1 Release|x64.ActiveCfg = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -291,6 +336,5 @@ Global
 		{A0394AC2-3D0E-4B5E-9F5F-FA4CEE1ABBED} = {84E0C4A9-E647-4ECB-B7B6-76D908925A15}
 		{86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10} = {D93B3836-DCAF-4E38-9E2F-9D22C64DA946}
 		{F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E} = {D93B3836-DCAF-4E38-9E2F-9D22C64DA946}
-		{C315D4A0-BBDC-46C4-973A-DE631517FA48} = {84E0C4A9-E647-4ECB-B7B6-76D908925A15}
 	EndGlobalSection
 EndGlobal
diff --git a/WINDOWS/netmap.vcxproj b/WINDOWS/netmap.vcxproj
index ab41bea7a..86fc61fff 100644
--- a/WINDOWS/netmap.vcxproj
+++ b/WINDOWS/netmap.vcxproj
@@ -1,21 +1,98 @@
 īģŋ
 
-
+  
+    
+      Win10 Debug
+      Win32
+    
+    
+      Win10 Debug
+      x64
+    
+    
+      Win10 Release
+      Win32
+    
+    
+      Win10 Release
+      x64
+    
+    
+      Win8.1 Debug
+      Win32
+    
+    
+      Win8.1 Release
+      Win32
+    
+    
+      Win8 Debug
+      Win32
+    
+    
+      Win8 Release
+      Win32
+    
+    
+      Win7 Debug
+      Win32
+    
+    
+      Win7 Release
+      Win32
+    
+    
+      Win8.1 Debug
+      x64
+    
+    
+      Win8.1 Release
+      x64
+    
+    
+      Win8 Debug
+      x64
+    
+    
+      Win8 Release
+      x64
+    
+    
+      Win7 Debug
+      x64
+    
+    
+      Win7 Release
+      x64
+    
+  
   
     {789055E4-2677-413D-A638-3E2AED7C7427}
     {dd38f7fc-d7bd-488b-9242-7d8754cde80d}
     netmap
+    10.0.14393.0
   
-
   
-    Driver 
-    WDM 
-    WindowsKernelModeDriver8.1
+    Driver
+    
+    WDM
+    
+    WindowsKernelModeDriver10.0
   
-
   
   
-
+  
+    true
+  
+  
+    true
+  
+  
+    true
+  
+  
+    true
+  
   
     
       Speed
@@ -28,7 +105,6 @@
       %(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\hal.lib;$(DDK_LIB_PATH)\wmilib.lib;$(DDK_LIB_PATH)\netio.lib;
     
   
-
   
     
       Neither
@@ -41,7 +117,6 @@
       %(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\hal.lib;$(DDK_LIB_PATH)\wmilib.lib;
     
   
-
   
     
       Speed
@@ -51,7 +126,6 @@
       _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions);NDIS60=1;
     
   
-
   
     
       Speed
@@ -60,7 +134,6 @@
       true
     
   
-
   
     
       Speed
@@ -69,7 +142,6 @@
       true
     
   
-
   
     
       Speed
@@ -78,7 +150,50 @@
       true
     
   
-
+  
+    
+      Speed
+      AnySuitable
+      MaxSpeed
+      true
+      Level3
+      false
+      Level3
+      false
+    
+    
+      $(SolutionDir)Output-$(ConfigurationName)\$(TargetName)-pkg\$(TargetName).pdb
+    
+    
+      $(SolutionDir)Output-$(ConfigurationName)\$(TargetName)-pkg\$(TargetName).pdb
+    
+  
+  
+    
+      Level3
+    
+  
+  
+    
+      false
+    
+    
+      $(SolutionDir)Output-$(ConfigurationName)\$(TargetName)-pkg\$(TargetName).pdb
+    
+  
+  
+    
+      Level3
+    
+  
+  
+    
+      false
+    
+    
+      $(SolutionDir)Output-$(ConfigurationName)\$(TargetName)-pkg\$(TargetName).pdb
+    
+  
   
     
     
@@ -87,6 +202,7 @@
     
     
     
+    
     
     
   
diff --git a/WINDOWS/netmap.vcxproj.user b/WINDOWS/netmap.vcxproj.user
index e3dd0cf45..195c0b81a 100644
--- a/WINDOWS/netmap.vcxproj.user
+++ b/WINDOWS/netmap.vcxproj.user
@@ -1,10 +1,9 @@
 īģŋ
 
   
-    
-    
+    CN="WDKTestCert Ali,131544860236513530" | ECEA8629ADED6CB0522E374EB5072BC9D2D865C9
   
   
     TestSign
   
-
+
\ No newline at end of file
diff --git a/WINDOWS/netmap_windows.c b/WINDOWS/netmap_windows.c
index f5850d1be..7570a02fc 100644
--- a/WINDOWS/netmap_windows.c
+++ b/WINDOWS/netmap_windows.c
@@ -90,17 +90,17 @@ ioctlCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
 	    status = STATUS_INSUFFICIENT_RESOURCES;
 	} else {
 	    priv->np_refs = 1;
-	    D("Netmap.sys: ioctlCreate::priv->np_refcount = %i", priv->np_refs);
+	    nm_prinf("Netmap.sys: ioctlCreate::priv->np_refcount = %i", priv->np_refs);
 	    irpSp->FileObject->FsContext = priv;
 	}
     } else {
 	priv->np_refs += 1;
-	D("Netmap.sys: ioctlCreate::priv->np_refcount = %i", priv->np_refs);
+	nm_prinf("Netmap.sys: ioctlCreate::priv->np_refcount = %i", priv->np_refs);
     }
     NMG_UNLOCK();
 
     //--------------------------------------------------------
-    //D("Netmap.sys: Pid %i attached: memory allocated @%p", currentProcId, priv);
+    //nm_prinf("Netmap.sys: Pid %i attached: memory allocated @%p", currentProcId, priv);
 
     Irp->IoStatus.Status = status;
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
@@ -565,7 +565,7 @@ ioctlDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
 		}
 
 		ret = netmap_ioctl(priv, irpSp->Parameters.DeviceIoControl.IoControlCode,
-			(caddr_t)&arg, NULL);
+			(caddr_t)&arg, NULL, 1);
 		if (NT_SUCCESS(ret)) {
 			if (data && !NT_SUCCESS(copy_to_user((void*)data, &arg, argsize, Irp))) {
 				DbgPrint("Netmap.sys: ioctl failure/cannot copy data to user");
@@ -611,7 +611,7 @@ ifunit_ref(const char* name)
     struct net_device *	ifp = NULL;
 
     if (strlen(name) < 4 || _strnicmp(name, "eth", 3) != 0) {
-	D("not a NIC");
+	nm_prerr("not a NIC");
 	return NULL;
     }
 	if (ndis_hooks.ndis_regif == NULL)
@@ -626,7 +626,7 @@ ifunit_ref(const char* name)
 
     win32_init_lookaside_buffers(ifp);
 
-    RtlCopyMemory(ifp->if_xname, name, IFNAMSIZ);
+    RtlCopyMemory(if_name(ifp), name, IFNAMSIZ);
     ifp->ifIndex = deviceIfIndex;
 
 	win32_init_lookaside_buffers(ifp);
@@ -694,19 +694,19 @@ windows_netmap_mmap(PIRP Irp)
 	priv = irpSp->FileObject->FsContext;
 
 	if (priv == NULL) {
-		D("no priv");
+		nm_prerr("no priv");
 		return STATUS_DEVICE_DATA_ERROR;
 	}
 	na = priv->np_na;
 	if (na == NULL) {
-		D("na not attached");
+		nm_prerr("na not attached");
 		return STATUS_DEVICE_DATA_ERROR;
 	}
 	mb(); /* XXX really ? */
 
 	mdl = win32_build_user_vm_map(na->nm_mem);
 	if (mdl == NULL) {
-		D("failed building memory map");
+		nm_prerr("failed building memory map");
 		return STATUS_DEVICE_DATA_ERROR;
 	}
 
@@ -885,6 +885,16 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
     DbgPrint("bdg_mismatch_datapath unimplemented!!!\n");
 }
 
+void if_ref(struct net_device *ifp)
+{
+	/*
+	* XXX This is just to shut up the compiler.
+	* I wouldn't know what to out in here yet...
+	*/
+	DbgPrint("unimplemented if_ref!!!\n");
+/* 	dev_hold(ifp); */
+}
+
 void
 if_rele(struct net_device *ifp)
 {
@@ -917,6 +927,12 @@ nm_os_ifnet_fini(void)
 
 }
 
+unsigned
+nm_os_ifnet_mtu(struct ifnet *ifp)
+{
+       return 1500; /* XXX hardwired */
+}
+
 /*
  * Mitigation support
  */
@@ -924,7 +940,7 @@ nm_os_ifnet_fini(void)
 void
 generic_timer_handler(struct hrtimer *t)
 {
-	DbgPrint("unimplemented generic_timer_handler %p\n", t);
+	DbgPrint("unimplemented generic_timer_handler\n", t);
 #if 0
 	struct nm_generic_mit *mit =
 		container_of(t, struct nm_generic_mit, mit_timer);
@@ -952,7 +968,7 @@ generic_timer_handler(struct hrtimer *t)
 void nm_os_mitigation_init(struct nm_generic_mit *mit, int idx,
 struct netmap_adapter *na)
 {
-	DbgPrint("unimplemented generic_timer_handler %p\n");
+	DbgPrint("unimplemented generic_timer_handler\n");
 	//KeInitializeDpc(&mit->mit_timer.deferred_proc, &generic_timer_handler, NULL);
 	//KeInitializeTimer(&mit->mit_timer.timer);
 	//hrtimer_init(&mit->mit_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
@@ -964,7 +980,7 @@ struct netmap_adapter *na)
 
 void nm_os_mitigation_start(struct nm_generic_mit *mit)
 {
-	DbgPrint("unimplemented generic_timer_handler %p\n");
+	DbgPrint("unimplemented generic_timer_handler\n");
 	//LARGE_INTEGER test;
 	//KeSetTimerEx(&mit->mit_timer.timer, test, 1000, &mit->mit_timer.deferred_proc);
 	//mit->mit_timer.active = TRUE;
@@ -974,13 +990,13 @@ void nm_os_mitigation_start(struct nm_generic_mit *mit)
 
 void nm_os_mitigation_restart(struct nm_generic_mit *mit)
 {
-	DbgPrint("unimplemented nm_os_mitigation_start %p\n");
+	DbgPrint("unimplemented nm_os_mitigation_start\n");
 	//hrtimer_forward_now(&mit->mit_timer, ktime_set(0, netmap_generic_mit));
 }
 
 int nm_os_mitigation_active(struct nm_generic_mit *mit)
 {
-	DbgPrint("unimplemented nm_os_mitigation_active %p\n");
+	DbgPrint("unimplemented nm_os_mitigation_active\n");
 	return 0;
 	//return mit->mit_timer.active;
 	//return hrtimer_active(&mit->mit_timer);
@@ -1001,7 +1017,13 @@ nm_os_ncpus(void)
 }
 
 int
-nm_os_mbuf_has_offld(struct mbuf *m)
+nm_os_mbuf_has_csum_offld(struct mbuf *m)
+{
+	return 0;  // TODO
+}
+
+int
+nm_os_mbuf_has_seg_offld(struct mbuf *m)
 {
 	return 0;  // TODO
 }
@@ -1019,40 +1041,39 @@ nm_os_put_module(void)
 }
 
 
-struct nm_kthread {
+struct nm_kctx {
     int unused; /* To avoid compiler barfs */
 };
 
 void
-nm_os_kthread_set_affinity(struct nm_kthread *nmk, int affinity)
+nm_os_kctx_worker_setaff(struct nm_kctx *nmk, int affinity)
 {
 	// TODO
 }
 
-struct nm_kthread *
-nm_os_kthread_create(struct nm_kthread_cfg *cfg, unsigned int cfgtype,
-		     void *opaque)
+struct nm_kctx *
+nm_os_kctx_create(struct nm_kctx_cfg *cfg, void *opaque)
 {
 	// TODO
 	return NULL;
 }
 
 int
-nm_os_kthread_start(struct nm_kthread *nmk)
+nm_os_kctx_worker_start(struct nm_kctx *nmk)
 {
 	// TODO
 	return -1;
 }
 
 void
-nm_os_kthread_stop(struct nm_kthread *nmk)
+nm_os_kctx_worker_stop(struct nm_kctx *nmk)
 {
 	// TODO
 }
 
 
 void
-nm_os_kthread_delete(struct nm_kthread *nmk)
+nm_os_kctx_destroy(struct nm_kctx *nmk)
 {
 	// TODO
 }
diff --git a/WINDOWS/nm-ndis/filter.c b/WINDOWS/nm-ndis/filter.c
index a586d4e1f..06ed74ab1 100644
--- a/WINDOWS/nm-ndis/filter.c
+++ b/WINDOWS/nm-ndis/filter.c
@@ -478,7 +478,7 @@ Routine Description:
 
 Arguments:
 
-    FilterModuleContext - pointer to the filter context stucture
+    FilterModuleContext - pointer to the filter context structure
     PauseParameters     - additional information about the pause
 
 Return Value:
@@ -545,7 +545,7 @@ Routine Description:
 
 Arguments:
 
-    FilterModuleContext - pointer to the filter context stucture.
+    FilterModuleContext - pointer to the filter context structure.
     RestartParameters   - additional information about the restart operation.
 
 Return Value:
@@ -1838,7 +1838,7 @@ Routine Description:
     calls the NdisFCancelSendNetBufferLists to propagate the cancel operation.
 
     If your driver does not queue any send NBLs, you may omit this routine.  
-    NDIS will propagate the cancelation on your behalf more efficiently.
+    NDIS will propagate the cancellation on your behalf more efficiently.
 
 Arguments:
 
diff --git a/WINDOWS/nm-ndis/nm-ndis-pkg.vcxproj b/WINDOWS/nm-ndis/nm-ndis-pkg.vcxproj
index befd62240..0df8e7cdb 100644
--- a/WINDOWS/nm-ndis/nm-ndis-pkg.vcxproj
+++ b/WINDOWS/nm-ndis/nm-ndis-pkg.vcxproj
@@ -1,75 +1,132 @@
 īģŋ
 
-
-   
-    WindowsKernelModeDriver8.1
+  
     Utility
     Package
     true
+    WindowsKernelModeDriver10.0
   
-
-  
-
-  
-    WindowsKernelModeDriver8.1
-    Utility
-    Package
-    true
-    Win8.1 Debug
-  
-
+  
+    
+      Win10 Debug
+      Win32
+    
+    
+      Win10 Debug
+      x64
+    
+    
+      Win10 Release
+      Win32
+    
+    
+      Win10 Release
+      x64
+    
+    
+      Win8.1 Debug
+      Win32
+    
+    
+      Win8.1 Release
+      Win32
+    
+    
+      Win8 Debug
+      Win32
+    
+    
+      Win8 Release
+      Win32
+    
+    
+      Win7 Debug
+      Win32
+    
+    
+      Win7 Release
+      Win32
+    
+    
+      Win8.1 Debug
+      x64
+    
+    
+      Win8.1 Release
+      x64
+    
+    
+      Win8 Debug
+      x64
+    
+    
+      Win8 Release
+      x64
+    
+    
+      Win7 Debug
+      x64
+    
+    
+      Win7 Release
+      x64
+    
+  
   
     {F54EA0D4-064F-4AD9-AA21-8F9F3B90AC8E}
-    {EA453FB1-F3EF-493B-B384-DD83BAB817D3}
-    $(MSBuildProjectName)
+    {4605da2c-74a5-4865-98e1-152ef136825f}
     nm-ndis-pkg
+    10.0.14393.0
   
-
-
+  
   
   
-
   
     
   
-
-
-  
-    $(SolutionDir)\Output-$(ConfigurationName)\
-    $(SolutionDir)\Output-$(ConfigurationName)\tmp\$(ProjectName)\
+  
+  
+    true
   
-
   
     DbgengKernelDebugger
     False
-    False
-    None
+    True
     
     
-    
     
-    
-    %PathToInf%
     False
     False
     True
     
     133563
+    $(SolutionDir)Output-$(ConfigurationName)\
+    $(SolutionDir)Output-$(ConfigurationName)\tmp\$(ProjectName)\
+  
+  
+    true
+  
+  
+    true
+  
+  
+    true
   
-
-  
+  
+    
+      4
+      true
+    
   
-
   
-    
     
   
   
     
-      {86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}
+      {789055e4-2677-413d-a638-3e2aed7c7427}
     
   
   
   
   
-
+
\ No newline at end of file
diff --git a/WINDOWS/nm-ndis/nm-ndis-pkg.vcxproj.filters b/WINDOWS/nm-ndis/nm-ndis-pkg.vcxproj.filters
index 8f9d2ceff..e1b34f2aa 100644
--- a/WINDOWS/nm-ndis/nm-ndis-pkg.vcxproj.filters
+++ b/WINDOWS/nm-ndis/nm-ndis-pkg.vcxproj.filters
@@ -1,21 +1,9 @@
 īģŋ
 
   
-    
-      cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*
-      {446F6A95-37A1-4B03-8A92-410687C4D886}
-    
-    
-      h;hpp;hxx;hm;inl;inc;xsd
-      {EA0E0C58-3231-40D0-8E7C-2C45BAC612B4}
-    
-    
-      rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml
-      {9ACC37D4-AA63-4917-A0A9-CE371E1BBD46}
-    
     
+      {8E41214B-6785-4CFE-B992-037D68949A14}
       inf;inv;inx;mof;mc;
-      {02639A71-564D-46D6-89B9-7E42931F9188}
     
   
 
\ No newline at end of file
diff --git a/WINDOWS/nm-ndis/nm-ndis-pkg.vcxproj.user b/WINDOWS/nm-ndis/nm-ndis-pkg.vcxproj.user
index 7431a9009..50f0ac150 100644
--- a/WINDOWS/nm-ndis/nm-ndis-pkg.vcxproj.user
+++ b/WINDOWS/nm-ndis/nm-ndis-pkg.vcxproj.user
@@ -1,6 +1,36 @@
 īģŋ
 
-  
+  
+    False
+    True
+    DriverTest
+    
+    
+    C:\Program Files (x86)\Windows Kits\8.1\Testing\Tests\Utilities\DefaultDriverPackageInstallationTask.dll
+    10.216.1.205
+    10.216.1.205
+    
+    Microsoft.DriverKit.DefaultDriverPackageInstallationClass.PerformDefaultDriverPackageInstallation
+    
+    True
+  
+  
+    
+    
+  
+  
     TestSign
   
-
+  
+    CN="WDKTestCert Ali,131544860649071360" | 10000FDB00D12EF1C204354F46F27C83B8A54118
+  
+  
+    CN="WDKTestCert Ali,131544860649071360" | 10000FDB00D12EF1C204354F46F27C83B8A54118
+  
+  
+    CN="WDKTestCert Ali,131544860649071360" | 10000FDB00D12EF1C204354F46F27C83B8A54118
+  
+  
+    CN="WDKTestCert Ali,131544860649071360" | 10000FDB00D12EF1C204354F46F27C83B8A54118
+  
+
\ No newline at end of file
diff --git a/WINDOWS/nm-ndis/nm-ndis.vcxproj b/WINDOWS/nm-ndis/nm-ndis.vcxproj
index f3c131557..d032b3b63 100644
--- a/WINDOWS/nm-ndis/nm-ndis.vcxproj
+++ b/WINDOWS/nm-ndis/nm-ndis.vcxproj
@@ -1,28 +1,111 @@
 īģŋ
 
-
-
+  
+    
+      Win10 Debug
+      Win32
+    
+    
+      Win10 Debug
+      x64
+    
+    
+      Win10 Release
+      Win32
+    
+    
+      Win10 Release
+      x64
+    
+    
+      Win8.1 Debug
+      Win32
+    
+    
+      Win8.1 Release
+      Win32
+    
+    
+      Win8 Debug
+      Win32
+    
+    
+      Win8 Release
+      Win32
+    
+    
+      Win7 Debug
+      Win32
+    
+    
+      Win7 Release
+      Win32
+    
+    
+      Win8.1 Debug
+      x64
+    
+    
+      Win8.1 Release
+      x64
+    
+    
+      Win8 Debug
+      x64
+    
+    
+      Win8 Release
+      x64
+    
+    
+      Win7 Debug
+      x64
+    
+    
+      Win7 Release
+      x64
+    
+  
   
     {86BB0F88-ECD9-4E2C-AD47-AE4071D1DE10}
     {47A52D61-9E9E-4FEA-9033-C1434C5F8870}
     nm-ndis
+    10.0.14393.0
   
-
   
-    Driver 
-    WDM 
-    WindowsKernelModeDriver8.1
+    Driver
+    
+    WDM
+    
+    WindowsKernelModeDriver10.0
   
-
   
   
-
-
-
   
     
       %(AdditionalIncludeDirectories);..;.
@@ -32,8 +115,8 @@
       %(PreprocessorDefinitions);NDIS630=1
       %(PreprocessorDefinitions);NDISLWF=1
       %(PreprocessorDefinitions);NDIS_WDM=1
-      true
-      Level4
+      false
+      Level3
     
     
       %(AdditionalIncludeDirectories);..;.
@@ -43,7 +126,6 @@
       %(PreprocessorDefinitions);NDISLWF=1
       %(PreprocessorDefinitions);NDIS_WDM=1
     
-
     
       %(AdditionalIncludeDirectories);..;.
       %(PreprocessorDefinitions);NDIS60=1
@@ -52,28 +134,25 @@
       %(PreprocessorDefinitions);NDISLWF=1
       %(PreprocessorDefinitions);NDIS_WDM=1
     
-
   
-
   
     
       %(PreprocessorDefinitions);_KERNEL=1;
     
   
-
   
-
   
     
     
       %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib
+      $(SolutionDir)Output-$(ConfigurationName)\$(TargetName)-pkg\$(TargetName).pdb
+      $(SolutionDir)Output-$(ConfigurationName)\$(TargetName)-pkg\$(TargetName).pdb
     
     
       
       
     
   
-
   
     
       Speed
@@ -82,8 +161,6 @@
       true
     
   
-
-
   
     
       Speed
@@ -92,7 +169,20 @@
       true
     
   
-
+  
+    
+      Speed
+      AnySuitable
+      MaxSpeed
+      true
+    
+    
+      $(SolutionDir)Output-$(ConfigurationName)\$(TargetName)-pkg\$(TargetName).pdb
+    
+    
+      $(SolutionDir)Output-$(ConfigurationName)\$(TargetName)-pkg\$(TargetName).pdb
+    
+  
   
     
       Speed
@@ -101,7 +191,6 @@
       true
     
   
-
   
     
       Speed
@@ -110,7 +199,6 @@
       true
     
   
-
   
     
       Neither
@@ -120,9 +208,6 @@
       false
     
   
-
-
-
   
     
       ;%(AdditionalIncludeDirectories)
@@ -150,19 +235,15 @@
     
     
   
-
   
     
   
-
   
     
     
   
-
   
     
   
-
   
-
+
\ No newline at end of file
diff --git a/WINDOWS/nm-ndis/nm-ndis.vcxproj.filters b/WINDOWS/nm-ndis/nm-ndis.vcxproj.filters
index 0d3463669..2a74aaa1e 100644
--- a/WINDOWS/nm-ndis/nm-ndis.vcxproj.filters
+++ b/WINDOWS/nm-ndis/nm-ndis.vcxproj.filters
@@ -269,6 +269,42 @@
     
     
     
+    
+      Header Files
+    
+    
+    
+    
+    
+      Header Files
+    
+    
+    
+    
+    
+      Header Files
+    
+    
+    
+    
+    
+      Header Files
+    
+    
+    
+    
+    
+      Header Files
+    
+    
+    
+    
+    
+      Header Files
+    
+    
+    
+    
   
   
     
@@ -276,6 +312,6 @@
     
   
   
-    
+    
   
-
+
\ No newline at end of file
diff --git a/WINDOWS/nm-ndis/nm-ndis.vcxproj.user b/WINDOWS/nm-ndis/nm-ndis.vcxproj.user
index dedc94cc2..30fece355 100644
--- a/WINDOWS/nm-ndis/nm-ndis.vcxproj.user
+++ b/WINDOWS/nm-ndis/nm-ndis.vcxproj.user
@@ -12,9 +12,15 @@
   
     TestSign
   
+  
+    TestSign
+  
   
     TestSign
   
+  
+    TestSign
+  
   
     TestSign
   
diff --git a/WINDOWS/win_glue.h b/WINDOWS/win_glue.h
index f615a4fa6..44f61c184 100644
--- a/WINDOWS/win_glue.h
+++ b/WINDOWS/win_glue.h
@@ -22,7 +22,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
- 
+
 #ifndef NETMAP_WIN_GLUE_H
 #define NETMAP_WIN_GLUE_H
 
@@ -46,7 +46,7 @@
 #pragma warning(disable:4118)	//error in between signed and unsigned
 //#pragma warning(disable:4115)	//definition of type between parenthesis
 #pragma warning(disable:4127)	//constant conditional expression
-#pragma warning(disable:4133)	//warning: uncompatible types: From <1> to <2>
+#pragma warning(disable:4133)	//warning: incompatible types: From <1> to <2>
 #pragma warning(disable:4142)	//benign type redefinition
 // #pragma warning(disable:4189)	//local variable initialized but without references
 #pragma warning(disable:4200)	//non-standard extension: matrix of zero dimension in struct/union
@@ -54,8 +54,8 @@
 #pragma warning(disable:4229)	// zero-size arrays // XXX
 #pragma warning(disable:4242)	//possible loss of data in conversion
 #pragma warning(disable:4244)	//possible loss of data in conversion
-#pragma warning(disable:4245)	//conversion from int to uint_32t: corrispondence error between signed and unsigned
-#pragma warning(disable:4389)	//wrong corrispondence between signed and unsigned
+#pragma warning(disable:4245)	//conversion from int to uint_32t: correspondence error between signed and unsigned
+#pragma warning(disable:4389)	//wrong correspondence between signed and unsigned
 
 #pragma warning(disable:4267)	//conversion from 'size_t' to . possible loss of data
 
@@ -117,7 +117,7 @@ typedef char *			caddr_t;
 
 typedef PHYSICAL_ADDRESS 	vm_paddr_t;
 typedef uint32_t		vm_offset_t;
-typedef ULONG 			vm_ooffset_t; 
+typedef ULONG 			vm_ooffset_t;
 
 #define thread PIO_STACK_LOCATION
 
@@ -127,7 +127,7 @@ typedef ULONG 			vm_ooffset_t;
 /*
  *	ERRNO -> NTSTATUS TRANSLATION
  */
-#define ENOBUFS		STATUS_DEVICE_INSUFFICIENT_RESOURCES	
+#define ENOBUFS		STATUS_DEVICE_INSUFFICIENT_RESOURCES
 #define EOPNOTSUPP	STATUS_INVALID_DEVICE_REQUEST
 
 /*
@@ -135,7 +135,7 @@ typedef ULONG 			vm_ooffset_t;
  */
 #define destroy_dev(a)
 #define __user
-#define nm_iommu_group_id(dev)	0
+#define nm_iommu_group_id(dev)	-1
 
 
 /*
@@ -215,11 +215,12 @@ typedef struct _win_SELINFO
 	KGUARDED_MUTEX mutex;
 } win_SELINFO;
 
-static void 
-nm_os_selinfo_init(win_SELINFO* queue)
+static int
+nm_os_selinfo_init(win_SELINFO* queue, const char *name)
 {
 	KeInitializeEvent(&queue->queue, NotificationEvent, TRUE);
 	KeInitializeGuardedMutex(&queue->mutex);
+	return 0;
 }
 
 static void nm_os_selinfo_uninit(win_SELINFO *queue) { /* XXX nothing to do here? */ }
@@ -266,7 +267,7 @@ static int time_uptime_w32()
 struct netmap_adapter;
 
 struct net_device {
-	char	if_xname[IFNAMSIZ];			// external name (name + unit) 
+	char	name[IFNAMSIZ];			// external name (name + unit)
 	//        struct ifaltq if_snd;         /* output queue (includes altq) */
 	struct netmap_adapter	*na;
 	void	*pfilter;
@@ -333,7 +334,7 @@ struct mbuf *win_make_mbuf(struct net_device *, uint32_t, const char *);
 	// XXX do we also need the netmap_default_mbuf_destructor ?
 
 
-static inline void 
+static inline void
 win32_ndis_packet_freem(struct mbuf* m)
 {
 	if (m != NULL) {
@@ -345,7 +346,7 @@ win32_ndis_packet_freem(struct mbuf* m)
 		ExFreeToNPagedLookasideList(&m->dev->mbuf_pool, m);
 		//free(m, M_DEVBUF);
 
-	}	
+	}
 }
 
 /*
@@ -360,10 +361,13 @@ win32_ndis_packet_freem(struct mbuf* m)
 
 struct net_device* ifunit_ref(const char *name);
 void if_rele(struct net_device *ifp);
+void if_ref(struct net_device *ifp);
 
 PVOID send_up_to_stack(struct ifnet *ifp, struct mbuf *m, PVOID head);
 
-#define WNA(_ifp)		_ifp->na
+#define if_setnetmapadapter(_ifp, _na) do {				\
+	(_ifp)->na = _na;							\
+} while (0)
 #define NM_BNS_GET(b)	do { (void)(b); } while (0)
 #define NM_BNS_PUT(b)   do { (void)(b); } while (0)
 
diff --git a/apps/bridge/GNUmakefile b/apps/bridge/GNUmakefile
index 4e76fe3bb..042fc5db9 100644
--- a/apps/bridge/GNUmakefile
+++ b/apps/bridge/GNUmakefile
@@ -1,6 +1,6 @@
 # For multiple programs using a single source file each,
 # we can just define 'progs' and create custom targets.
-PROGS	=	bridge
+PROGS	=	bridge bridge-b
 LIBNETMAP =
 
 CLEANFILES = $(PROGS) *.o
@@ -11,15 +11,17 @@ VPATH = $(SRCDIR)/apps/bridge
 NO_MAN=
 CFLAGS = -O2 -pipe
 CFLAGS += -Werror -Wall -Wunused-function
-CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include
+CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include -I$(SRCDIR)/libnetmap
 CFLAGS += -Wextra
 
-LDLIBS += -lpthread
+LDFLAGS += -L $(BUILDDIR)/build-libnetmap
+LDLIBS += -lnetmap
 ifeq ($(shell uname),Linux)
 	LDLIBS += -lrt	# on linux
 endif
 
 PREFIX ?= /usr/local
+MAN_PREFIX = $(if $(filter-out /,$(PREFIX)),$(PREFIX),/usr)/share/man
 
 all: $(PROGS)
 
@@ -31,3 +33,9 @@ install: $(PROGS:%=install-%)
 
 install-%:
 	install -D $* $(DESTDIR)/$(PREFIX)/bin/$*
+	-install -D -m 644 $(SRCDIR)/apps/bridge/bridge.8 $(DESTDIR)/$(MAN_PREFIX)/man8/bridge.8
+
+bridge-b: bridge-b.o
+
+bridge-b.o: bridge.c
+	$(CC) $(CFLAGS) -DBUSYWAIT -c $^ -o $@
diff --git a/apps/bridge/bridge.8 b/apps/bridge/bridge.8
index d791d9456..d3720ff45 100644
--- a/apps/bridge/bridge.8
+++ b/apps/bridge/bridge.8
@@ -1,5 +1,4 @@
 .\" Copyright (c) 2016 Luigi Rizzo, Universita` di Pisa
-.\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
 .\" modification, are permitted provided that the following conditions
@@ -22,14 +21,12 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD$
-.\"
-.Dd December 16, 2016
-.Dt BRIDGE 1
+.Dd November 21, 2020
+.Dt BRIDGE 8
 .Os
 .Sh NAME
 .Nm bridge
-.Nd A netmap client to bridge two network interfaces (or one interface and the host stack).
+.Nd netmap client to bridge two netmap ports
 .Sh SYNOPSIS
 .Bk -words
 .Bl -tag -width "bridge"
@@ -39,21 +36,38 @@
 .Op Fl w Ar wait-link
 .Op Fl v
 .Op Fl c
+.El
+.Ek
 .Sh DESCRIPTION
 .Nm
-is a simple netmap application that bridges packets between two netmap ports. If the two netmap
-ports use the same netmap memory region
+is a simple netmap application that bridges packets between two netmap ports.
+If the two netmap ports use the same netmap memory region
 .Nm
-operates in zero copy (unless explicitly prevented by the
+forwards packets without copying the packets payload (zero-copy mode), unless
+explicitly prevented by the
 .Fl c
-flag).
-.El
+flag.
+.Pp
+When bridging two physical ports, it is necessary that both NICS are in
+promiscuous mode, otherwise unicast traffic directed to other hosts will
+be dropped by the hardware, and bridging will not work.
+.Pp
+When bridging the hardware rings of a physical port with the corresponding
+host rings, it is necessary to turn off the offloads, because netmap does
+not prepare the NIC rings with offload information.
+Example:
+.Bd -literal -offset indent
+ifconfig em0 -rxcsum -txcsum -tso4 -tso6 -lro
+.Ed
+.Pp
+Available options:
 .Bl -tag -width Ds
 .It Fl i Ar port
-Name of the netmap port. It can be supplied up to two times to identifiy
-the ports that must be bridged. Any netmap port type (physical interface, VALE switch, pipe, monitor port...)
-can be used. If the option is supplied only once, then it must be
-for a physical interface and, in that case,
+Name of the netmap port.
+It can be supplied up to two times to identify the ports that must be bridged.
+Any netmap port type (physical interface, VALE switch, pipe, monitor port...)
+can be used.
+If the option is supplied only once, then it must be for a physical interface and, in that case,
 .Nm
 will bridge the port and the host stack.
 .It Fl b Ar batch-size
@@ -67,6 +81,10 @@ Enable verbose mode
 .It Fl c
 Disable zero-copy mode.
 .El
+.Sh SEE ALSO
+.Xr netmap 4 ,
+.Xr lb 8 ,
+.Xr pkt-gen 8
 .Sh AUTHORS
 .An -nosplit
 .Nm
diff --git a/apps/bridge/bridge.c b/apps/bridge/bridge.c
index df47d5e0e..b8c6e786a 100644
--- a/apps/bridge/bridge.c
+++ b/apps/bridge/bridge.c
@@ -3,18 +3,23 @@
  *
  * BSD license
  *
- * A netmap client to bridge two network interfaces
- * (or one interface and the host stack).
- *
- * $FreeBSD: head/tools/tools/netmap/bridge.c 228975 2011-12-30 00:04:11Z uqs $
+ * A netmap application to bridge two network interfaces,
+ * or one interface and the host stack.
  */
 
+#include 
+#include 
 #include 
-#define NETMAP_WITH_LIBS
-#include 
 #include 
+#include 
+#include 
+#include 
+
+#if defined(_WIN32)
+#define BUSYWAIT
+#endif
 
-int verbose = 0;
+static int verbose = 0;
 
 static int do_abort = 0;
 static int zerocopy = 1; /* enable zerocopy if possible */
@@ -29,30 +34,39 @@ sigint_h(int sig)
 
 
 /*
- * how many packets on this set of queues ?
+ * How many slots do we (user application) have on this
+ * set of queues ?
  */
-int
-pkt_queued(struct nm_desc *d, int tx)
+static int
+rx_slots_avail(struct nmport_d *d)
 {
-        u_int i, tot = 0;
-
-        if (tx) {
-                for (i = d->first_tx_ring; i <= d->last_tx_ring; i++) {
-                        tot += nm_ring_space(NETMAP_TXRING(d->nifp, i));
-                }
-        } else {
-                for (i = d->first_rx_ring; i <= d->last_rx_ring; i++) {
-                        tot += nm_ring_space(NETMAP_RXRING(d->nifp, i));
-                }
-        }
-        return tot;
+	u_int i, tot = 0;
+
+	for (i = d->first_rx_ring; i <= d->last_rx_ring; i++) {
+		tot += nm_ring_space(NETMAP_RXRING(d->nifp, i));
+	}
+
+	return tot;
+}
+
+static int
+tx_slots_avail(struct nmport_d *d)
+{
+	u_int i, tot = 0;
+
+	for (i = d->first_tx_ring; i <= d->last_tx_ring; i++) {
+		tot += nm_ring_space(NETMAP_TXRING(d->nifp, i));
+	}
+
+	return tot;
 }
 
 /*
- * move up to 'limit' pkts from rxring to txring swapping buffers.
+ * Move up to 'limit' pkts from rxring to txring, swapping buffers
+ * if zerocopy is possible. Otherwise fall back on packet copying.
  */
 static int
-process_rings(struct netmap_ring *rxring, struct netmap_ring *txring,
+rings_move(struct netmap_ring *rxring, struct netmap_ring *txring,
 	      u_int limit, const char *msg)
 {
 	u_int j, k, m = 0;
@@ -60,9 +74,9 @@ process_rings(struct netmap_ring *rxring, struct netmap_ring *txring,
 	/* print a warning if any of the ring flags is set (e.g. NM_REINIT) */
 	if (rxring->flags || txring->flags)
 		D("%s rxflags %x txflags %x",
-			msg, rxring->flags, txring->flags);
-	j = rxring->cur; /* RX */
-	k = txring->cur; /* TX */
+		    msg, rxring->flags, txring->flags);
+	j = rxring->head; /* RX */
+	k = txring->head; /* TX */
 	m = nm_ring_space(rxring);
 	if (m < limit)
 		limit = m;
@@ -74,18 +88,19 @@ process_rings(struct netmap_ring *rxring, struct netmap_ring *txring,
 		struct netmap_slot *rs = &rxring->slot[j];
 		struct netmap_slot *ts = &txring->slot[k];
 
-		/* swap packets */
 		if (ts->buf_idx < 2 || rs->buf_idx < 2) {
-			D("wrong index rx[%d] = %d  -> tx[%d] = %d",
-				j, rs->buf_idx, k, ts->buf_idx);
+			RD(2, "wrong index rxr[%d] = %d  -> txr[%d] = %d",
+			    j, rs->buf_idx, k, ts->buf_idx);
 			sleep(2);
 		}
-		/* copy the packet length. */
-		if (rs->len > 2048) {
-			D("wrong len %d rx[%d] -> tx[%d]", rs->len, j, k);
+		/* Copy the packet length. */
+		if (rs->len > rxring->nr_buf_size) {
+			RD(2,  "%s: invalid len %u, rxr[%d] -> txr[%d]",
+			    msg, rs->len, j, k);
 			rs->len = 0;
 		} else if (verbose > 1) {
-			D("%s send len %d rx[%d] -> tx[%d]", msg, rs->len, j, k);
+			D("%s: fwd len %u, rx[%d] -> tx[%d]",
+			    msg, rs->len, j, k);
 		}
 		ts->len = rs->len;
 		if (zerocopy) {
@@ -100,30 +115,34 @@ process_rings(struct netmap_ring *rxring, struct netmap_ring *txring,
 			char *txbuf = NETMAP_BUF(txring, ts->buf_idx);
 			nm_pkt_copy(rxbuf, txbuf, ts->len);
 		}
+		/*
+		 * Copy the NS_MOREFRAG from rs to ts, leaving any
+		 * other flags unchanged.
+		 */
+		ts->flags = (ts->flags & ~NS_MOREFRAG) | (rs->flags & NS_MOREFRAG);
 		j = nm_ring_next(rxring, j);
 		k = nm_ring_next(txring, k);
 	}
 	rxring->head = rxring->cur = j;
 	txring->head = txring->cur = k;
 	if (verbose && m > 0)
-		D("%s sent %d packets to %p", msg, m, txring);
+		D("%s fwd %d packets: rxring %u --> txring %u",
+		    msg, m, rxring->ringid, txring->ringid);
 
 	return (m);
 }
 
-/* move packts from src to destination */
+/* Move packets from source port to destination port. */
 static int
-move(struct nm_desc *src, struct nm_desc *dst, u_int limit)
+ports_move(struct nmport_d *src, struct nmport_d *dst, u_int limit,
+	const char *msg)
 {
 	struct netmap_ring *txring, *rxring;
 	u_int m = 0, si = src->first_rx_ring, di = dst->first_tx_ring;
-	const char *msg = (src->req.nr_flags == NR_REG_SW) ?
-		"host->net" : "net->host";
 
 	while (si <= src->last_rx_ring && di <= dst->last_tx_ring) {
 		rxring = NETMAP_RXRING(src->nifp, si);
 		txring = NETMAP_TXRING(dst->nifp, di);
-		ND("txring %p rxring %p", txring, rxring);
 		if (nm_ring_empty(rxring)) {
 			si++;
 			continue;
@@ -132,7 +151,7 @@ move(struct nm_desc *src, struct nm_desc *dst, u_int limit)
 			di++;
 			continue;
 		}
-		m += process_rings(rxring, txring, limit, msg);
+		m += rings_move(rxring, txring, limit, msg);
 	}
 
 	return (m);
@@ -143,7 +162,25 @@ static void
 usage(void)
 {
 	fprintf(stderr,
-	    "usage: bridge [-v] [-i ifa] [-i ifb] [-b burst] [-w wait_time] [ifa [ifb [burst]]]\n");
+		"netmap bridge program: forward packets between two "
+			"netmap ports\n"
+		"    usage(1): bridge [-v] [-i ifa] [-i ifb] [-b burst] "
+			"[-w wait_time] [-L]\n"
+		"    usage(2): bridge [-v] [-w wait_time] [-L] "
+			"[ifa [ifb [burst]]]\n"
+		"\n"
+		"    ifa and ifb are specified using the nm_open() syntax.\n"
+		"    When ifb is missing (or is equal to ifa), bridge will\n"
+		"    forward between between ifa and the host stack if -L\n"
+		"    is not specified, otherwise loopback traffic on ifa.\n"
+		"\n"
+		"    example: bridge -w 10 -i netmap:eth3 -i netmap:eth1\n"
+		"\n"
+		"    If ifa and ifb are two interfaces, they must be in\n"
+		"    promiscuous mode. Otherwise, if bridging with the \n"
+		"    host stack, the interface must have the offloads \n"
+		"    disabled.\n"
+		);
 	exit(1);
 }
 
@@ -157,20 +194,22 @@ usage(void)
 int
 main(int argc, char **argv)
 {
+	char msg_a2b[256], msg_b2a[256];
 	struct pollfd pollfd[2];
-	int ch;
 	u_int burst = 1024, wait_link = 4;
-	struct nm_desc *pa = NULL, *pb = NULL;
+	struct nmport_d *pa = NULL, *pb = NULL;
 	char *ifa = NULL, *ifb = NULL;
 	char ifabuf[64] = { 0 };
+	int pa_sw_rings, pb_sw_rings;
+	int loopback = 0;
+	int ch;
 
-	fprintf(stderr, "%s built %s %s\n",
-		argv[0], __DATE__, __TIME__);
-
-	while ( (ch = getopt(argc, argv, "b:ci:vw:")) != -1) {
+	while ((ch = getopt(argc, argv, "hb:ci:vw:L")) != -1) {
 		switch (ch) {
 		default:
 			D("bad option %c %s", ch, optarg);
+			/* fallthrough */
+		case 'h':
 			usage();
 			break;
 		case 'b':	/* burst */
@@ -194,6 +233,9 @@ main(int argc, char **argv)
 		case 'w':
 			wait_link = atoi(optarg);
 			break;
+		case 'L':
+			loopback = 1;
+			break;
 		}
 
 	}
@@ -222,28 +264,32 @@ main(int argc, char **argv)
 		wait_link = 4;
 	}
 	if (!strcmp(ifa, ifb)) {
-		D("same interface, endpoint 0 goes to host");
-		snprintf(ifabuf, sizeof(ifabuf) - 1, "%s^", ifa);
-		ifa = ifabuf;
+		if (!loopback) {
+			D("same interface, endpoint 0 goes to host");
+			snprintf(ifabuf, sizeof(ifabuf) - 1, "%s^", ifa);
+			ifa = ifabuf;
+		} else {
+			D("same interface, loopbacking traffic");
+		}
 	} else {
 		/* two different interfaces. Take all rings on if1 */
 	}
-	pa = nm_open(ifa, NULL, 0, NULL);
+	pa = nmport_open(ifa);
 	if (pa == NULL) {
 		D("cannot open %s", ifa);
 		return (1);
 	}
 	/* try to reuse the mmap() of the first interface, if possible */
-	pb = nm_open(ifb, NULL, NM_OPEN_NO_MMAP, pa);
+	pb = nmport_open(ifb);
 	if (pb == NULL) {
 		D("cannot open %s", ifb);
-		nm_close(pa);
+		nmport_close(pa);
 		return (1);
 	}
 	zerocopy = zerocopy && (pa->mem == pb->mem);
 	D("------- zerocopy %ssupported", zerocopy ? "" : "NOT ");
 
-	/* setup poll(2) variables. */
+	/* setup poll(2) array */
 	memset(pollfd, 0, sizeof(pollfd));
 	pollfd[0].fd = pa->fd;
 	pollfd[1].fd = pb->fd;
@@ -251,8 +297,21 @@ main(int argc, char **argv)
 	D("Wait %d secs for link to come up...", wait_link);
 	sleep(wait_link);
 	D("Ready to go, %s 0x%x/%d <-> %s 0x%x/%d.",
-		pa->req.nr_name, pa->first_rx_ring, pa->req.nr_rx_rings,
-		pb->req.nr_name, pb->first_rx_ring, pb->req.nr_rx_rings);
+		pa->hdr.nr_name, pa->first_rx_ring, pa->reg.nr_rx_rings,
+		pb->hdr.nr_name, pb->first_rx_ring, pb->reg.nr_rx_rings);
+
+	pa_sw_rings = (pa->reg.nr_mode == NR_REG_SW ||
+	    pa->reg.nr_mode == NR_REG_ONE_SW);
+	pb_sw_rings = (pb->reg.nr_mode == NR_REG_SW ||
+	    pb->reg.nr_mode == NR_REG_ONE_SW);
+
+	snprintf(msg_a2b, sizeof(msg_a2b), "%s:%s --> %s:%s",
+			pa->hdr.nr_name, pa_sw_rings ? "host" : "nic",
+			pb->hdr.nr_name, pb_sw_rings ? "host" : "nic");
+
+	snprintf(msg_b2a, sizeof(msg_b2a), "%s:%s --> %s:%s",
+			pb->hdr.nr_name, pb_sw_rings ? "host" : "nic",
+			pa->hdr.nr_name, pa_sw_rings ? "host" : "nic");
 
 	/* main loop */
 	signal(SIGINT, sigint_h);
@@ -260,25 +319,21 @@ main(int argc, char **argv)
 		int n0, n1, ret;
 		pollfd[0].events = pollfd[1].events = 0;
 		pollfd[0].revents = pollfd[1].revents = 0;
-		n0 = pkt_queued(pa, 0);
-		n1 = pkt_queued(pb, 0);
-#if defined(_WIN32) || defined(BUSYWAIT)
-		if (n0){
-			ioctl(pollfd[1].fd, NIOCTXSYNC, NULL);
+		n0 = rx_slots_avail(pa);
+		n1 = rx_slots_avail(pb);
+#ifdef BUSYWAIT
+		if (n0) {
 			pollfd[1].revents = POLLOUT;
-		}
-		else {
+		} else {
 			ioctl(pollfd[0].fd, NIOCRXSYNC, NULL);
 		}
-		if (n1){
-			ioctl(pollfd[0].fd, NIOCTXSYNC, NULL);
+		if (n1) {
 			pollfd[0].revents = POLLOUT;
-		}
-		else {
+		} else {
 			ioctl(pollfd[1].fd, NIOCRXSYNC, NULL);
 		}
 		ret = 1;
-#else
+#else  /* !defined(BUSYWAIT) */
 		if (n0)
 			pollfd[1].events |= POLLOUT;
 		else
@@ -287,49 +342,58 @@ main(int argc, char **argv)
 			pollfd[0].events |= POLLOUT;
 		else
 			pollfd[1].events |= POLLIN;
+
+		/* poll() also cause kernel to txsync/rxsync the NICs */
 		ret = poll(pollfd, 2, 2500);
-#endif //defined(_WIN32) || defined(BUSYWAIT)
+#endif /* !defined(BUSYWAIT) */
 		if (ret <= 0 || verbose)
 		    D("poll %s [0] ev %x %x rx %d@%d tx %d,"
 			     " [1] ev %x %x rx %d@%d tx %d",
 				ret <= 0 ? "timeout" : "ok",
 				pollfd[0].events,
 				pollfd[0].revents,
-				pkt_queued(pa, 0),
-				NETMAP_RXRING(pa->nifp, pa->cur_rx_ring)->cur,
-				pkt_queued(pa, 1),
+				rx_slots_avail(pa),
+				NETMAP_RXRING(pa->nifp, pa->cur_rx_ring)->head,
+				tx_slots_avail(pa),
 				pollfd[1].events,
 				pollfd[1].revents,
-				pkt_queued(pb, 0),
-				NETMAP_RXRING(pb->nifp, pb->cur_rx_ring)->cur,
-				pkt_queued(pb, 1)
+				rx_slots_avail(pb),
+				NETMAP_RXRING(pb->nifp, pb->cur_rx_ring)->head,
+				tx_slots_avail(pb)
 			);
 		if (ret < 0)
 			continue;
 		if (pollfd[0].revents & POLLERR) {
 			struct netmap_ring *rx = NETMAP_RXRING(pa->nifp, pa->cur_rx_ring);
 			D("error on fd0, rx [%d,%d,%d)",
-				rx->head, rx->cur, rx->tail);
+			    rx->head, rx->cur, rx->tail);
 		}
 		if (pollfd[1].revents & POLLERR) {
 			struct netmap_ring *rx = NETMAP_RXRING(pb->nifp, pb->cur_rx_ring);
 			D("error on fd1, rx [%d,%d,%d)",
-				rx->head, rx->cur, rx->tail);
+			    rx->head, rx->cur, rx->tail);
 		}
 		if (pollfd[0].revents & POLLOUT) {
-			move(pb, pa, burst);
-			// XXX we don't need the ioctl */
-			// ioctl(me[0].fd, NIOCTXSYNC, NULL);
+			ports_move(pb, pa, burst, msg_b2a);
+#ifdef BUSYWAIT
+			ioctl(pollfd[0].fd, NIOCTXSYNC, NULL);
+#endif
 		}
+
 		if (pollfd[1].revents & POLLOUT) {
-			move(pa, pb, burst);
-			// XXX we don't need the ioctl */
-			// ioctl(me[1].fd, NIOCTXSYNC, NULL);
+			ports_move(pa, pb, burst, msg_a2b);
+#ifdef BUSYWAIT
+			ioctl(pollfd[1].fd, NIOCTXSYNC, NULL);
+#endif
 		}
+
+		/*
+		 * We don't need ioctl(NIOCTXSYNC) on the two file descriptors.
+		 * here. The kernel will txsync on next poll().
+		 */
 	}
-	D("exiting");
-	nm_close(pb);
-	nm_close(pa);
+	nmport_close(pb);
+	nmport_close(pa);
 
 	return (0);
 }
diff --git a/apps/dedup/GNUmakefile b/apps/dedup/GNUmakefile
new file mode 100644
index 000000000..071e0f176
--- /dev/null
+++ b/apps/dedup/GNUmakefile
@@ -0,0 +1,40 @@
+# For multiple programs using a single source file each,
+# we can just define 'progs' and create custom targets.
+PROGS	=	dedup
+LIBNETMAP =
+
+CLEANFILES = $(PROGS) *.o
+
+SRCDIR ?= ../..
+VPATH = $(SRCDIR)/apps/dedup
+
+NO_MAN=
+CFLAGS = -O2 -pipe -g
+CFLAGS += -Werror -Wall -Wunused-function
+CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include
+CFLAGS += -Wextra
+#CFLAGS += -DDEDUP_HASH_STAT
+
+LDLIBS += -lpthread
+ifeq ($(shell uname),Linux)
+	LDLIBS += -lrt	# on linux
+endif
+
+PREFIX ?= /usr/local
+MAN_PREFIX = $(if $(filter-out /,$(PREFIX)),$(PREFIX),/usr)/share/man
+
+all: $(PROGS)
+
+dedup: dedup.o dedup-main.o
+
+dedup.o: dedup.h
+
+clean:
+	-@rm -rf $(CLEANFILES)
+
+.PHONY: install
+install: $(PROGS:%=install-%)
+
+install-%:
+	install -D $* $(DESTDIR)/$(PREFIX)/bin/$*
+	-install -D -m 644 $(SRCDIR)/apps/lb/lb.8 $(DESTDIR)/$(MAN_PREFIX)/man8/lb.8
diff --git a/apps/dedup/dedup-main.c b/apps/dedup/dedup-main.c
new file mode 100644
index 000000000..5bfeb8e09
--- /dev/null
+++ b/apps/dedup/dedup-main.c
@@ -0,0 +1,232 @@
+/*
+ * (C) 2017	Giuseppe Lettieri
+ *
+ * BSD license
+ *
+ */
+
+#include 
+#define NETMAP_WITH_LIBS
+#include 
+#include 
+#include "dedup.h"
+
+int verbose = 0;
+
+static int do_abort = 0;
+static int zerocopy = 1; /* enable zerocopy if possible */
+
+static void
+sigint_h(int sig)
+{
+	(void)sig;	/* UNUSED */
+	do_abort = 1;
+	signal(SIGINT, SIG_DFL);
+}
+
+
+static void
+usage(void)
+{
+	fprintf(stderr,
+		"dedup\n"
+		);
+	exit(1);
+}
+
+struct dedup dedup;
+struct nm_desc *pa = NULL, *pb = NULL;
+
+static void
+free_buffers(void)
+{
+	struct netmap_ring *ring;
+
+	if (pa == NULL)
+		return;
+
+	ring = NETMAP_RXRING(pa->nifp, pa->first_rx_ring);
+
+	dedup_get_fifo_buffers(&dedup, ring, &pa->nifp->ni_bufs_head);
+	nm_close(pa);
+	nm_close(pb);
+}
+
+int
+main(int argc, char **argv)
+{
+	struct pollfd pollfd[2];
+	int ch;
+	char *ifa = NULL, *ifb = NULL;
+	int wait_link = 2;
+	int win_size_usec = 50;
+	unsigned int fifo_size = 10;
+	int n;
+	int hold = 0;
+	struct nmreq base_req;
+	uint32_t buf_head = 0;
+#ifdef DEDUP_HASH_STAT
+	time_t last_hash_output = 0;
+#endif
+
+	while ((ch = getopt(argc, argv, "hci:vw:W:F:H")) != -1) {
+		switch (ch) {
+		default:
+			D("bad option %c %s", ch, optarg);
+			/* fallthrough */
+		case 'h':
+			usage();
+			break;
+		case 'i':	/* interface */
+			if (ifa == NULL)
+				ifa = optarg;
+			else if (ifb == NULL)
+				ifb = optarg;
+			else
+				D("%s ignored, already have 2 interfaces",
+					optarg);
+			break;
+		case 'c':
+			zerocopy = 0; /* do not zerocopy */
+			break;
+		case 'v':
+			verbose++;
+			break;
+		case 'w':
+			wait_link = atoi(optarg);
+			break;
+		case 'W':
+			win_size_usec = atoi(optarg);
+			break;
+		case 'F':
+			fifo_size = atoi(optarg);
+			break;
+		case 'H':
+			hold = 1;
+			break;
+		}
+
+	}
+
+	if (!ifa || !ifb) {
+		D("missing interface");
+		usage();
+	}
+	memset(&base_req, 0, sizeof(base_req));
+	if (!hold) {
+		base_req.nr_arg3 = fifo_size;
+	}
+	pa = nm_open(ifa, &base_req, 0, NULL);
+	if (pa == NULL) {
+		D("cannot open %s", ifa);
+		return (1);
+	}
+	if (!hold) {
+	        if (base_req.nr_arg3 != fifo_size) {
+			D("failed to allocate %u extra buffers", fifo_size);
+			return (1); // XXX failover to copy?
+		} else {
+			buf_head = pa->nifp->ni_bufs_head;
+		}
+	}
+	if (pa->first_rx_ring != pa->last_rx_ring) {
+		D("%s: too many RX rings (%d)", pa->req.nr_name,
+				pa->last_rx_ring - pa->first_rx_ring + 1);
+		return (1);
+	}
+	/* try to reuse the mmap() of the first interface, if possible */
+	pb = nm_open(ifb, NULL, NM_OPEN_NO_MMAP, pa);
+	if (pb == NULL) {
+		D("cannot open %s", ifb);
+		nm_close(pa);
+		return (1);
+	}
+	if (pb->first_tx_ring != pb->last_tx_ring) {
+		D("%s: too many TX rings (%d)", pb->req.nr_name,
+				pb->last_rx_ring - pb->first_rx_ring + 1);
+		nm_close(pa);
+		return (1);
+	}
+
+	memset(&dedup, 0, sizeof(dedup));
+	dedup.out_slot = dedup.out_ring->slot;
+	if (dedup_init(&dedup, fifo_size, 
+			NETMAP_RXRING(pa->nifp, pa->first_rx_ring),
+			NETMAP_TXRING(pb->nifp, pb->first_tx_ring)) < 0) {
+		D("failed to initialize dedup with fifo_size %u", fifo_size);
+		return (1);
+	}
+	if (fifo_size >= dedup.out_ring->num_slots - 1) {
+		D("fifo_size %u too large (max %u)", fifo_size, dedup.out_ring->num_slots - 1);
+		return (1);
+	}
+	if (dedup_set_fifo_buffers(&dedup, NULL, buf_head) != 0) {
+		D("failed to set 'hold packets' option");
+		return (1);
+	}
+	pa->nifp->ni_bufs_head = 0;
+	atexit(free_buffers);
+
+	/* enable/disable zerocopy */
+	dedup.in_memid = pa->req.nr_arg2;
+	dedup.out_memid = (zerocopy ? pb->req.nr_arg2 : -1 );
+	dedup.fifo_memid = hold ? dedup.out_memid : dedup.in_memid;
+	D("memids: in %d out %d fifo %d", dedup.in_memid, dedup.out_memid,
+			dedup.fifo_memid);
+	dedup.win_size.tv_sec = win_size_usec / 1000000;
+	dedup.win_size.tv_usec = win_size_usec % 1000000;
+	D("win_size %lld+%lld", (long long) dedup.win_size.tv_sec,
+			(long long) dedup.win_size.tv_usec);
+
+	/* setup poll(2) array */
+	memset(pollfd, 0, sizeof(pollfd));
+	pollfd[0].fd = pa->fd;
+	pollfd[1].fd = pb->fd;
+
+	D("Wait %d secs for link to come up...", wait_link);
+	sleep(wait_link);
+	D("Ready to go, %s -> %s", pa->req.nr_name, pb->req.nr_name);
+
+	/* main loop */
+	signal(SIGINT, sigint_h);
+	n = 0;
+	while (!do_abort) {
+		int ret;
+		struct timeval now;
+
+		pollfd[0].events = pollfd[1].events = 0;
+		pollfd[0].revents = pollfd[1].revents = 0;
+		if (!n)
+			pollfd[0].events = POLLIN;
+		else
+			pollfd[1].events = POLLOUT;
+		/* poll() also cause kernel to txsync/rxsync the NICs */
+		ret = poll(pollfd, 2, 1000);
+		gettimeofday(&now, NULL);
+		if (ret <= 0 || verbose)
+		    D("poll %s [0] ev %x %x"
+			     " [1] ev %x %x",
+				ret <= 0 ? "timeout" : "ok",
+				pollfd[0].events,
+				pollfd[0].revents,
+				pollfd[1].events,
+				pollfd[1].revents
+			);
+		n = dedup_push_in(&dedup, &now);
+#ifdef DEDUP_HASH_STAT
+		if (now.tv_sec != last_hash_output) {
+			unsigned int i;
+
+			last_hash_output = now.tv_sec;
+			printf("buckets: ");
+			for (i = 0; i <= dedup.hashmap_mask; i++) {
+				if  (dedup.hashmap[i].bucket_size)
+					printf("%u: %u, ", i, dedup.hashmap[i].bucket_size);
+			}
+			printf("\n");
+		}
+#endif
+	}
+
+	return (0);
+}
diff --git a/apps/dedup/dedup.c b/apps/dedup/dedup.c
new file mode 100644
index 000000000..ffa6f4212
--- /dev/null
+++ b/apps/dedup/dedup.c
@@ -0,0 +1,344 @@
+#include 
+#include 
+#include 
+#define NETMAP_WITH_LIBS
+#include 
+#include "dedup.h"
+
+#include "mark-adler-hash.c"
+
+static int dedup_sse42;
+
+static inline int
+dedup_can_hold(struct dedup *d)
+{
+	return d->fifo_slot == d->out_slot;
+}
+
+static void
+dedup_ptr_init(struct dedup *d, struct dedup_ptr *p, unsigned long v)
+{
+	p->r = v;
+	p->o = v % d->out_ring->num_slots;
+	p->f = v % d->fifo_size;
+}
+
+
+int
+dedup_init(struct dedup *d, unsigned int fifo_size, struct netmap_ring *in, struct netmap_ring *out)
+{
+	unsigned int sh;
+
+	if (fifo_size == 0)
+		return -1;
+
+	d->fifo = calloc(fifo_size, sizeof(d->fifo[0]));
+	if (d->fifo == NULL)
+		return -1;
+
+	sh = (unsigned int)(sizeof(fifo_size) * CHAR_BIT - __builtin_clz(fifo_size - 1)) + 1;
+	D("sh %u size %lu", sh, 1UL << sh);
+	if (sh > sizeof(unsigned short) * CHAR_BIT - 1)
+		goto err;
+	d->hashmap = calloc(1UL << sh, sizeof(struct dedup_hashmap_entry));
+	if (d->hashmap == NULL)
+		goto err;
+	d->hashmap_mask = (1UL << sh) - 1;
+	d->fifo_size = fifo_size;
+	d->in_ring = in;
+	d->in_slot = in->slot;
+	d->out_ring = out;
+	d->out_slot = out->slot;
+	dedup_ptr_init(d, &d->fifo_out, out->head);
+	dedup_ptr_init(d, &d->fifo_in, out->head);
+	SSE42(dedup_sse42);
+	return 0;
+err:
+	free(d->fifo);
+	d->fifo = NULL;
+	return -1;
+}
+
+uint32_t
+dedup_set_fifo_buffers(struct dedup *d, struct netmap_ring *ring, uint32_t buf_head)
+{
+	uint32_t scan;
+	struct netmap_slot *s;
+	struct netmap_ring *r = ring ? ring : d->in_ring;
+
+	if (buf_head == 0) {
+		d->fifo_ring = d->out_ring;
+		d->fifo_slot = d->out_slot;
+		d->next_to_send = &d->fifo_out;
+		return 0;
+	}
+	d->fifo_slot = calloc(d->fifo_size, sizeof(struct netmap_slot));
+	if (d->fifo_slot == NULL)
+		return buf_head;
+	for (scan = buf_head, s = d->fifo_slot;
+	     scan != 0 && s != d->fifo_slot + d->fifo_size;
+	     scan = *(uint32_t *)NETMAP_BUF(r, scan), s++) {
+		s->len = r->nr_buf_size;
+		s->buf_idx = scan;
+	}
+	if (s != d->fifo_slot + d->fifo_size) {
+		free(d->fifo_slot);
+		d->fifo_slot = NULL;
+		return buf_head;
+	}
+	d->fifo_ring = d->in_ring;
+	d->next_to_send = &d->fifo_in;
+	return scan;
+}
+
+void
+dedup_get_fifo_buffers(struct dedup *d, struct netmap_ring *ring, uint32_t *buf_head)
+{
+	struct netmap_ring *r = ring ? ring : d->in_ring;
+	unsigned int i;
+
+	if (d->fifo_slot == NULL || dedup_can_hold(d))
+		return;
+
+	for (i = 0; i < d->fifo_size; i++) {
+		struct netmap_slot *s = d->fifo_slot + i;
+		uint32_t *new_head = (uint32_t *)NETMAP_BUF(r, s->buf_idx);
+
+		*new_head = *buf_head;
+		*buf_head = s->buf_idx;
+	}
+	free(d->fifo_slot);
+	d->fifo_slot = NULL;
+}
+
+void
+dedup_fini(struct dedup *d)
+{
+	if (d->fifo != NULL) {
+		free(d->fifo);
+		d->fifo = NULL;
+	}
+	if (d->fifo_slot != NULL && d->fifo_slot != d->out_slot) {
+		free(d->fifo_slot);
+		d->fifo_slot = NULL;
+	}
+	if (d->hashmap != NULL) {
+		free(d->hashmap);
+		d->hashmap = NULL;
+	}
+}
+
+static int
+dedup_fifo_full(const struct dedup *d)
+{
+	return (d->fifo_in.r - d->fifo_out.r >= d->fifo_size);
+}
+
+static int
+dedup_fifo_empty(const struct dedup *d)
+{
+	return (d->fifo_in.r == d->fifo_out.r);
+}
+
+static inline uint32_t
+dedup_hash(const char *data)
+{
+	return dedup_sse42 ? crc32c_hw(0, data, 64) : crc32c_sw(0, data, 64);
+}
+
+static void
+dedup_hashmap_insert(struct dedup *d, unsigned short h)
+{
+	struct dedup_hashmap_entry *he = d->hashmap + h;
+	struct dedup_fifo_entry *fe = d->fifo + d->fifo_in.f;
+	fe->bucket_next = (he->valid ? d->fifo_in.r - he->bucket_head : 0);
+	fe->hashmap_entry = h;
+	he->bucket_head = d->fifo_in.r;
+	he->valid = 1;
+#ifdef DEDUP_HASH_STAT
+	he->bucket_size++;
+#endif
+}
+
+static void
+dedup_hashmap_remove(struct dedup *d)
+{
+	struct dedup_fifo_entry *fe = d->fifo + d->fifo_out.f;
+	struct dedup_hashmap_entry *he = d->hashmap + fe->hashmap_entry;
+
+	ND("h %u bucket_head %lu fifo_out.r %lu fifo_out.f %u",
+			fe->hashmap_entry, he->bucket_head,
+			d->fifo_out.r, d->fifo_out.f);
+	if (he->bucket_head == d->fifo_out.r)
+		he->valid = 0;
+	fe->hashmap_entry = 0;
+	fe->bucket_next = 0;
+#ifdef DEDUP_HASH_STAT
+	he->bucket_size--;
+#endif
+}
+
+static long
+dedup_fresh_packet(struct dedup *d, const struct netmap_slot *s)
+{
+	const void *buf = NETMAP_BUF(d->in_ring, s->buf_idx);
+	unsigned int h = dedup_hash(buf);
+	unsigned short i = h & d->hashmap_mask;
+	struct dedup_hashmap_entry *he = d->hashmap + i;
+	unsigned long fi = he->bucket_head;
+	unsigned long fifo_win = d->fifo_in.r - d->fifo_out.r;
+
+	if (!he->valid)
+		return i;
+
+	while (d->fifo_in.r - fi > 0 && d->fifo_in.r - fi <= fifo_win) {
+		struct netmap_slot *fs;
+		const void *fbuf;
+		unsigned long rfi = fi - d->fifo_out.r + d->fifo_out.f;
+		unsigned int delta;
+
+		if (rfi >= d->fifo_size)
+			rfi -= d->fifo_size;
+
+		fs = d->fifo_slot + rfi;
+		ND("checking %lu %lu: lengths %u %u buf %d", fi, rfi, fs->len, s->len,
+				fs->buf_idx);
+
+		if (fs->len != s->len)
+			goto next;
+		fbuf = NETMAP_BUF(d->fifo_ring, fs->buf_idx);
+		if (memcmp(buf, fbuf, s->len))
+			goto next;
+		return -1;
+	next:
+		delta = d->fifo[rfi].bucket_next;
+		if (delta == 0)
+			break;
+		fi -= delta;
+	}
+	return i;
+}
+
+static inline void
+dedup_transfer_pkt(struct dedup *d,
+	struct netmap_ring *src_ring,
+	struct netmap_slot *src_slot,
+	struct netmap_ring *dst_ring,
+	struct netmap_slot *dst_slot,
+	int zcopy)
+{
+	(void)d;
+
+	if (zcopy) {
+		struct netmap_slot w = *dst_slot;
+		__builtin_prefetch(dst_slot + 1);
+		*dst_slot = *src_slot;
+		dst_slot->flags |= NS_BUF_CHANGED;
+		*src_slot = w;
+		src_slot->flags |= NS_BUF_CHANGED;
+	} else {
+		char *rxbuf = NETMAP_BUF(src_ring, src_slot->buf_idx);
+		char *txbuf = NETMAP_BUF(dst_ring, dst_slot->buf_idx);
+		nm_pkt_copy(rxbuf, txbuf, src_slot->len);
+		dst_slot->len = src_slot->len;
+		dst_slot->ptr = src_slot->ptr;
+	}
+}
+
+static void
+dedup_fifo_slide_win(struct dedup *d, const struct timeval* now)
+{
+	struct timeval winstart;
+
+	timersub(now, &d->win_size, &winstart);
+
+	while (!dedup_fifo_empty(d)) {
+		struct dedup_fifo_entry *e = &d->fifo[d->fifo_out.f];
+
+		ND("fifo %u: arrival %llu.%llu winstart %llu.%llu",
+				d->fifo_out.f,
+				(unsigned long long)e->arrival.tv_sec,
+				(unsigned long long)e->arrival.tv_usec,
+				(unsigned long long)winstart.tv_sec,
+				(unsigned long long)winstart.tv_usec);
+
+		if (timercmp(&winstart, &e->arrival, <=))
+			break;
+
+		ND("fifo %u: pushing out", d->fifo_out.f);
+		dedup_hashmap_remove(d);
+		dedup_ptr_inc(d, &d->fifo_out);
+	}
+}
+
+int
+dedup_push_in(struct dedup *d, const struct timeval *now)
+{
+	struct netmap_ring *ri = d->in_ring, *ro = d->out_ring;
+	uint32_t head;
+	int n, out_space;
+
+	dedup_fifo_slide_win(d, now);
+
+	/* packets to input */
+	n = ri->tail - ri->head;
+	if (n < 0)
+		n += ri->num_slots;
+	/* available space on the output ring */
+	out_space = nm_ring_space(ro);
+
+	for (head = ri->head; n; head = nm_ring_next(ri, head), n--) {
+		struct netmap_slot *src_slot, *dst_slot;
+		long h;
+
+		src_slot = d->in_slot + head;
+
+		h = dedup_fresh_packet(d, src_slot);
+		if (h < 0) { /* duplicate */
+			ND("dropping %u", head);
+			continue;
+		}
+
+		if (out_space == 0)
+			break;
+
+		/* if the FIFO is full, remove and possibly send
+		 * the oldest packet
+		 */
+		if (dedup_fifo_full(d)) {
+			dedup_hashmap_remove(d);
+			dedup_ptr_inc(d, &d->fifo_out);
+		}
+
+		/* move the new packet to out ring */
+		dst_slot = d->out_slot + d->fifo_in.o;
+		dedup_transfer_pkt(d,
+			d->in_ring,
+			src_slot,
+			d->out_ring,
+			dst_slot,
+			d->in_memid == d->out_memid);
+
+		/* hold/copy/swap the packet in the FIFO ring */
+		d->fifo[d->fifo_in.f].arrival = d->in_ring->ts;
+
+		if (!dedup_can_hold(d)) {
+			dedup_transfer_pkt(d,
+				(d->in_memid == d->out_memid ? d->out_ring : d->in_ring),
+				(d->in_memid == d->out_memid ? dst_slot : src_slot),
+				d->fifo_ring,
+				d->fifo_slot + d->fifo_in.f,
+				(d->in_memid != d->out_memid &&
+				 d->in_memid == d->fifo_memid));
+		}
+
+		dedup_hashmap_insert(d, h);
+		dedup_ptr_inc(d, &d->fifo_in);
+		out_space--;
+	}
+	ri->head = head;
+	ri->cur = ri->tail;
+	ro->head = d->next_to_send->o;
+	ro->cur = dedup_can_hold(d) ? d->fifo_in.o : ro->head;
+	return n;
+}
diff --git a/apps/dedup/dedup.h b/apps/dedup/dedup.h
new file mode 100644
index 000000000..d8e7d88e3
--- /dev/null
+++ b/apps/dedup/dedup.h
@@ -0,0 +1,82 @@
+#ifndef DEDUP_H_
+#define DEDUP_H_
+
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE
+#endif
+#include 
+#include 
+
+struct dedup_ptr {
+	unsigned long r; /* free running, wraps naturally */
+	unsigned short o;  /* wraps at out_ring-size */
+	unsigned short f;  /* wraps at fifo_size */
+};
+
+struct dedup_fifo_entry {
+	struct timeval arrival;
+	unsigned short hashmap_entry;
+	unsigned int bucket_next; /* collision chain */
+};
+
+struct dedup_hashmap_entry {
+	int valid;
+#ifdef DEDUP_HASH_STAT
+	unsigned int bucket_size;
+#endif
+	unsigned long bucket_head;
+};
+
+struct dedup {
+	/* input ring */
+	struct netmap_ring *in_ring;
+	struct netmap_slot *in_slot;
+	int in_memid;
+
+	/* output ring */
+	struct netmap_ring *out_ring;
+	struct netmap_slot *out_slot;
+	int out_memid;
+
+	/* fifo */
+	struct dedup_fifo_entry *fifo;
+	struct netmap_ring *fifo_ring;
+	struct netmap_slot *fifo_slot;
+	int fifo_memid;
+
+	/* pointers */
+	struct dedup_ptr *next_to_send;
+	struct dedup_ptr fifo_in;
+	struct dedup_ptr fifo_out;
+
+	/* hash map */
+	struct dedup_hashmap_entry *hashmap;
+	unsigned int hashmap_mask;
+
+	/* configuration */ 
+	unsigned int fifo_size;
+	struct timeval win_size;
+	int zcopy_in_out;
+};
+
+int dedup_init(struct dedup *d, unsigned int fifo_size, struct netmap_ring *in,
+		struct netmap_ring *out);
+uint32_t dedup_set_fifo_buffers(struct dedup *d, struct netmap_ring *ring, uint32_t buf_head);
+void dedup_get_fifo_buffers(struct dedup *d, struct netmap_ring *ring, uint32_t *buf_head);
+
+static inline void dedup_ptr_inc(struct dedup *d, struct dedup_ptr *p)
+{
+	p->r++;
+	p->o++;
+	if (unlikely(p->o >= d->out_ring->num_slots))
+			p->o = 0;
+	p->f++;
+	if (unlikely(p->f >= d->fifo_size))
+			p->f = 0;
+}
+
+int dedup_push_in(struct dedup *d, const struct timeval *now);
+
+void dedup_fini(struct dedup *d);
+
+#endif
diff --git a/apps/dedup/mark-adler-hash.c b/apps/dedup/mark-adler-hash.c
new file mode 100644
index 000000000..f9b3b6dd2
--- /dev/null
+++ b/apps/dedup/mark-adler-hash.c
@@ -0,0 +1,378 @@
+/* crc32c.c -- compute CRC-32C using the Intel crc32 instruction
+ * Copyright (C) 2013 Mark Adler
+ * Version 1.1  1 Aug 2013  Mark Adler
+ */
+
+/*
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the author be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Mark Adler
+  madler@alumni.caltech.edu
+ */
+
+/* Use hardware CRC instruction on Intel SSE 4.2 processors.  This computes a
+   CRC-32C, *not* the CRC-32 used by Ethernet and zip, gzip, etc.  A software
+   version is provided as a fall-back, as well as for speed comparisons. */
+
+/* Version history:
+   1.0  10 Feb 2013  First version
+   1.1   1 Aug 2013  Correct comments on why three crc instructions in parallel
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/* CRC-32C (iSCSI) polynomial in reversed bit order. */
+#define POLY 0x82f63b78
+
+/* Table for a quadword-at-a-time software crc. */
+static pthread_once_t crc32c_once_sw = PTHREAD_ONCE_INIT;
+static uint32_t crc32c_table[8][256];
+
+/* Construct table for software CRC-32C calculation. */
+static void crc32c_init_sw(void)
+{
+    uint32_t n, crc, k;
+
+    for (n = 0; n < 256; n++) {
+        crc = n;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
+        crc32c_table[0][n] = crc;
+    }
+    for (n = 0; n < 256; n++) {
+        crc = crc32c_table[0][n];
+        for (k = 1; k < 8; k++) {
+            crc = crc32c_table[0][crc & 0xff] ^ (crc >> 8);
+            crc32c_table[k][n] = crc;
+        }
+    }
+}
+
+/* Table-driven software version as a fall-back.  This is about 15 times slower
+   than using the hardware instructions.  This assumes little-endian integers,
+   as is the case on Intel processors that the assembler code here is for. */
+static uint32_t crc32c_sw(uint32_t crci, const void *buf, size_t len)
+{
+    const unsigned char *next = buf;
+    uint64_t crc;
+
+    pthread_once(&crc32c_once_sw, crc32c_init_sw);
+    crc = crci ^ 0xffffffff;
+    while (len && ((uintptr_t)next & 7) != 0) {
+        crc = crc32c_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
+        len--;
+    }
+    while (len >= 8) {
+        crc ^= *(uint64_t *)next;
+        crc = crc32c_table[7][crc & 0xff] ^
+              crc32c_table[6][(crc >> 8) & 0xff] ^
+              crc32c_table[5][(crc >> 16) & 0xff] ^
+              crc32c_table[4][(crc >> 24) & 0xff] ^
+              crc32c_table[3][(crc >> 32) & 0xff] ^
+              crc32c_table[2][(crc >> 40) & 0xff] ^
+              crc32c_table[1][(crc >> 48) & 0xff] ^
+              crc32c_table[0][crc >> 56];
+        next += 8;
+        len -= 8;
+    }
+    while (len) {
+        crc = crc32c_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
+        len--;
+    }
+    return (uint32_t)crc ^ 0xffffffff;
+}
+
+/* Multiply a matrix times a vector over the Galois field of two elements,
+   GF(2).  Each element is a bit in an unsigned integer.  mat must have at
+   least as many entries as the power of two for most significant one bit in
+   vec. */
+static inline uint32_t gf2_matrix_times(uint32_t *mat, uint32_t vec)
+{
+    uint32_t sum;
+
+    sum = 0;
+    while (vec) {
+        if (vec & 1)
+            sum ^= *mat;
+        vec >>= 1;
+        mat++;
+    }
+    return sum;
+}
+
+/* Multiply a matrix by itself over GF(2).  Both mat and square must have 32
+   rows. */
+static inline void gf2_matrix_square(uint32_t *square, uint32_t *mat)
+{
+    int n;
+
+    for (n = 0; n < 32; n++)
+        square[n] = gf2_matrix_times(mat, mat[n]);
+}
+
+/* Construct an operator to apply len zeros to a crc.  len must be a power of
+   two.  If len is not a power of two, then the result is the same as for the
+   largest power of two less than len.  The result for len == 0 is the same as
+   for len == 1.  A version of this routine could be easily written for any
+   len, but that is not needed for this application. */
+static void crc32c_zeros_op(uint32_t *even, size_t len)
+{
+    int n;
+    uint32_t row;
+    uint32_t odd[32];       /* odd-power-of-two zeros operator */
+
+    /* put operator for one zero bit in odd */
+    odd[0] = POLY;              /* CRC-32C polynomial */
+    row = 1;
+    for (n = 1; n < 32; n++) {
+        odd[n] = row;
+        row <<= 1;
+    }
+
+    /* put operator for two zero bits in even */
+    gf2_matrix_square(even, odd);
+
+    /* put operator for four zero bits in odd */
+    gf2_matrix_square(odd, even);
+
+    /* first square will put the operator for one zero byte (eight zero bits),
+       in even -- next square puts operator for two zero bytes in odd, and so
+       on, until len has been rotated down to zero */
+    do {
+        gf2_matrix_square(even, odd);
+        len >>= 1;
+        if (len == 0)
+            return;
+        gf2_matrix_square(odd, even);
+        len >>= 1;
+    } while (len);
+
+    /* answer ended up in odd -- copy to even */
+    for (n = 0; n < 32; n++)
+        even[n] = odd[n];
+}
+
+/* Take a length and build four lookup tables for applying the zeros operator
+   for that length, byte-by-byte on the operand. */
+static void crc32c_zeros(uint32_t zeros[][256], size_t len)
+{
+    uint32_t n;
+    uint32_t op[32];
+
+    crc32c_zeros_op(op, len);
+    for (n = 0; n < 256; n++) {
+        zeros[0][n] = gf2_matrix_times(op, n);
+        zeros[1][n] = gf2_matrix_times(op, n << 8);
+        zeros[2][n] = gf2_matrix_times(op, n << 16);
+        zeros[3][n] = gf2_matrix_times(op, n << 24);
+    }
+}
+
+/* Apply the zeros operator table to crc. */
+static inline uint32_t crc32c_shift(uint32_t zeros[][256], uint32_t crc)
+{
+    return zeros[0][crc & 0xff] ^ zeros[1][(crc >> 8) & 0xff] ^
+           zeros[2][(crc >> 16) & 0xff] ^ zeros[3][crc >> 24];
+}
+
+/* Block sizes for three-way parallel crc computation.  LONG and SHORT must
+   both be powers of two.  The associated string constants must be set
+   accordingly, for use in constructing the assembler instructions. */
+#define LONG 8192
+#define LONGx1 "8192"
+#define LONGx2 "16384"
+#define SHORT 256
+#define SHORTx1 "256"
+#define SHORTx2 "512"
+
+/* Tables for hardware crc that shift a crc by LONG and SHORT zeros. */
+static pthread_once_t crc32c_once_hw = PTHREAD_ONCE_INIT;
+static uint32_t crc32c_long[4][256];
+static uint32_t crc32c_short[4][256];
+
+/* Initialize tables for shifting crcs. */
+static void crc32c_init_hw(void)
+{
+    crc32c_zeros(crc32c_long, LONG);
+    crc32c_zeros(crc32c_short, SHORT);
+}
+
+/* Compute CRC-32C using the Intel hardware instruction. */
+static uint32_t crc32c_hw(uint32_t crc, const void *buf, size_t len)
+{
+    const unsigned char *next = buf;
+    const unsigned char *end;
+    uint64_t crc0, crc1, crc2;      /* need to be 64 bits for crc32q */
+
+    /* populate shift tables the first time through */
+    pthread_once(&crc32c_once_hw, crc32c_init_hw);
+
+    /* pre-process the crc */
+    crc0 = crc ^ 0xffffffff;
+
+    /* compute the crc for up to seven leading bytes to bring the data pointer
+       to an eight-byte boundary */
+    while (len && ((uintptr_t)next & 7) != 0) {
+        __asm__("crc32b\t" "(%1), %0"
+                : "=r"(crc0)
+                : "r"(next), "0"(crc0));
+        next++;
+        len--;
+    }
+
+    /* compute the crc on sets of LONG*3 bytes, executing three independent crc
+       instructions, each on LONG bytes -- this is optimized for the Nehalem,
+       Westmere, Sandy Bridge, and Ivy Bridge architectures, which have a
+       throughput of one crc per cycle, but a latency of three cycles */
+    while (len >= LONG*3) {
+        crc1 = 0;
+        crc2 = 0;
+        end = next + LONG;
+        do {
+            __asm__("crc32q\t" "(%3), %0\n\t"
+                    "crc32q\t" LONGx1 "(%3), %1\n\t"
+                    "crc32q\t" LONGx2 "(%3), %2"
+                    : "=r"(crc0), "=r"(crc1), "=r"(crc2)
+                    : "r"(next), "0"(crc0), "1"(crc1), "2"(crc2));
+            next += 8;
+        } while (next < end);
+        crc0 = crc32c_shift(crc32c_long, crc0) ^ crc1;
+        crc0 = crc32c_shift(crc32c_long, crc0) ^ crc2;
+        next += LONG*2;
+        len -= LONG*3;
+    }
+
+    /* do the same thing, but now on SHORT*3 blocks for the remaining data less
+       than a LONG*3 block */
+    while (len >= SHORT*3) {
+        crc1 = 0;
+        crc2 = 0;
+        end = next + SHORT;
+        do {
+            __asm__("crc32q\t" "(%3), %0\n\t"
+                    "crc32q\t" SHORTx1 "(%3), %1\n\t"
+                    "crc32q\t" SHORTx2 "(%3), %2"
+                    : "=r"(crc0), "=r"(crc1), "=r"(crc2)
+                    : "r"(next), "0"(crc0), "1"(crc1), "2"(crc2));
+            next += 8;
+        } while (next < end);
+        crc0 = crc32c_shift(crc32c_short, crc0) ^ crc1;
+        crc0 = crc32c_shift(crc32c_short, crc0) ^ crc2;
+        next += SHORT*2;
+        len -= SHORT*3;
+    }
+
+    /* compute the crc on the remaining eight-byte units less than a SHORT*3
+       block */
+    end = next + (len - (len & 7));
+    while (next < end) {
+        __asm__("crc32q\t" "(%1), %0"
+                : "=r"(crc0)
+                : "r"(next), "0"(crc0));
+        next += 8;
+    }
+    len &= 7;
+
+    /* compute the crc for up to seven trailing bytes */
+    while (len) {
+        __asm__("crc32b\t" "(%1), %0"
+                : "=r"(crc0)
+                : "r"(next), "0"(crc0));
+        next++;
+        len--;
+    }
+
+    /* return a post-processed crc */
+    return (uint32_t)crc0 ^ 0xffffffff;
+}
+
+/* Check for SSE 4.2.  SSE 4.2 was first supported in Nehalem processors
+   introduced in November, 2008.  This does not check for the existence of the
+   cpuid instruction itself, which was introduced on the 486SL in 1992, so this
+   will fail on earlier x86 processors.  cpuid works on all Pentium and later
+   processors. */
+#define SSE42(have) \
+    do { \
+        uint32_t eax, ecx; \
+        eax = 1; \
+        __asm__("cpuid" \
+                : "=c"(ecx) \
+                : "a"(eax) \
+                : "%ebx", "%edx"); \
+        (have) = (ecx >> 20) & 1; \
+    } while (0)
+
+/* Compute a CRC-32C.  If the crc32 instruction is available, use the hardware
+   version.  Otherwise, use the software version. */
+uint32_t crc32c(uint32_t crc, const void *buf, size_t len)
+{
+    int sse42;
+
+    SSE42(sse42);
+    return sse42 ? crc32c_hw(crc, buf, len) : crc32c_sw(crc, buf, len);
+}
+
+#ifdef TEST
+
+#define SIZE (262144*3)
+#define CHUNK SIZE
+
+int main(int argc, char **argv)
+{
+    char *buf;
+    ssize_t got;
+    size_t off, n;
+    uint32_t crc;
+
+    (void)argv;
+    crc = 0;
+    buf = malloc(SIZE);
+    if (buf == NULL) {
+        fputs("out of memory", stderr);
+        return 1;
+    }
+    while ((got = read(0, buf, SIZE)) > 0) {
+        off = 0;
+        do {
+            n = (size_t)got - off;
+            if (n > CHUNK)
+                n = CHUNK;
+            crc = argc > 1 ? crc32c_sw(crc, buf + off, n) :
+                             crc32c(crc, buf + off, n);
+            off += n;
+        } while (off < (size_t)got);
+    }
+    free(buf);
+    if (got == -1) {
+        fputs("read error\n", stderr);
+        return 1;
+    }
+    printf("%08x\n", crc);
+    return 0;
+}
+
+#endif /* TEST */
diff --git a/apps/include/ctrs.h b/apps/include/ctrs.h
index 40c6b3dfb..49fb6a652 100644
--- a/apps/include/ctrs.h
+++ b/apps/include/ctrs.h
@@ -1,34 +1,43 @@
 #ifndef CTRS_H_
 #define CTRS_H_
 
+#include 
+#include 
 #include 
 
 /* counters to accumulate statistics */
 struct my_ctrs {
-	uint64_t pkts, bytes, events, drop;
+	uint64_t pkts, bytes, events;
+	uint64_t drop, drop_bytes;
 	uint64_t min_space;
 	struct timeval t;
+	uint32_t oq_n; /* number of elements in overflow queue (used in lb) */
 };
 
 /* very crude code to print a number in normalized form.
  * Caller has to make sure that the buffer is large enough.
  */
 static const char *
-norm2(char *buf, double val, char *fmt)
+norm2(char *buf, double val, const char *fmt, int normalize)
 {
-	char *units[] = { "", "K", "M", "G", "T" };
+	const char *units[] = { "", "K", "M", "G", "T" };
 	u_int i;
-
-	for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *) - 1; i++)
-		val /= 1000;
+	if (normalize)
+		for (i = 0; val >=1000 && i < sizeof(units)/sizeof(const char *) - 1; i++)
+			val /= 1000;
+	else
+		i=0;
 	sprintf(buf, fmt, val, units[i]);
 	return buf;
 }
 
 static __inline const char *
-norm(char *buf, double val)
+norm(char *buf, double val, int normalize)
 {
-	return norm2(buf, val, "%.3f %s");
+	if (normalize)
+		return norm2(buf, val, "%.3f %s", normalize);
+	else
+		return norm2(buf, val, "%.0f %s", normalize);
 }
 
 static __inline int
@@ -87,7 +96,7 @@ timespec_sub(struct timespec a, struct timespec b)
 	return ret;
 }
 
-static uint64_t
+static __inline uint64_t
 wait_for_next_report(struct timeval *prev, struct timeval *cur,
 		int report_interval)
 {
@@ -104,3 +113,4 @@ wait_for_next_report(struct timeval *prev, struct timeval *cur,
 	return delta.tv_sec* 1000000 + delta.tv_usec;
 }
 #endif /* CTRS_H_ */
+
diff --git a/apps/lb/GNUmakefile b/apps/lb/GNUmakefile
index 5979ed6bb..347ab8bd1 100644
--- a/apps/lb/GNUmakefile
+++ b/apps/lb/GNUmakefile
@@ -11,10 +11,11 @@ VPATH = $(SRCDIR)/apps/lb
 NO_MAN=
 CFLAGS = -O2 -pipe
 CFLAGS += -Werror -Wall -Wunused-function
-CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include
+CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include -I $(SRCDIR)/libnetmap
 CFLAGS += -Wextra
 
-LDLIBS += -lpthread -lm
+LDFLAGS += -L $(BUILDDIR)/build-libnetmap
+LDLIBS += -lnetmap -lpthread -lm
 ifeq ($(shell uname),Linux)
 	LDLIBS += -lrt	# on linux
 endif
diff --git a/apps/lb/lb.8 b/apps/lb/lb.8
index b0019f07f..b0d674e8c 100644
--- a/apps/lb/lb.8
+++ b/apps/lb/lb.8
@@ -1,4 +1,4 @@
-.\" Copyright (c) 2016 Broala and Universita` di Pisa
+.\" Copyright (c) 2017 Corelight, Inc. and Universita` di Pisa
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -22,10 +22,8 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD$
-.\"
-.Dd December 3, 2016
-.Dt LB 1
+.Dd October 28, 2018
+.Dt LB 8
 .Os
 .Sh NAME
 .Nm lb
@@ -39,44 +37,57 @@
 .Op Fl B Ar extra-buffers
 .Op Fl b Ar batch-size
 .Op Fl w Ar wait-link
+.El
+.Ek
 .Sh DESCRIPTION
 .Nm
 reads packets from an input netmap port and sends them to a number of netmap pipes,
-trying to balance the packets received by each pipe. Packets belonging to the
-same flow will always be sent to the same pipe.
-.Pp
+trying to balance the packets received by each pipe.
+Packets belonging to the
+same connection will always be sent to the same pipe.
 .Pp
 Command line options are listed below.
 .Bl -tag -width Ds
 .It Fl i Ar port
-Name of a netmap port. It must be supplied exactly once to indentify
+Name of a netmap port.
+It must be supplied exactly once to identify
 the input port.
-Any netmap port type (physical interface, VALE switch, pipe, monitor port...)
-can be used.
-.It Fl p Ar name:number | number
-Add a new pipe group of the given number of pipes. 
+Any netmap port type (e.g., physical interface, VALE switch, pipe,
+monitor port) can be used.
+.It Fl p Ar name Ns Cm \&: Ns Ar number | number
+Add a new pipe group of the given number of pipes.
 The pipe group will receive all the packets read from the input port, balanced
-among the available pipes. The receiving ends of the pipes
-will be called name}0 to name}number-1. The name is optional and defaults to
-the name of the input port (stripped down of any netmap operator). If the name
-is omitted, also the colon can be omitted.
+among the available pipes.
+The receiving ends of the pipes
+will be called
+.Dq Ar name Ns Em }0
+to
+.Dq Ar name No Ns Em } Ns Aq Ar number No - 1 .
+The name is optional and defaults to
+the name of the input port (stripped down of any netmap operator).
+If the name is omitted, also the colon can be omitted.
 .Pp
 This option can be supplied multiple times to define a sequence of pipe groups,
 each group receiving all the packets in turn.
 .Pp
-If no -p option is given, a single group of two pipes with default name is assumed.
+If no
+.Fl p
+option is given, a single group of two pipes with default name is assumed.
 .Pp
-It is allowed to use the same name for several groups. The pipe numbering in each
+It is allowed to use the same name for several groups.
+The pipe numbering in each
 group will start from were the previous identically-named group had left.
 .It Fl B Ar extra-buffers
-Try to reserve the given number of extra buffers. Extra buffers are shared among
+Try to reserve the given number of extra buffers.
+Extra buffers are shared among
 all pipes in all groups and work as an extension of the pipe rings.
 If a pipe ring is full for whatever reason,
 .Nm
 tries to use extra buffers before dropping any packets directed to that pipe.
 .Pp
 If all extra buffers are busy, some are stolen from the pipe with the longest
-backlog. This gives preference to newer packets over old ones, and prevents a
+backlog.
+This gives preference to newer packets over old ones, and prevents a
 stalled pipe to deplete the pool of extra buffers.
 .It Fl b Ar batch-size
 Maximum number of packets processed between two read operations from the input port.
@@ -93,20 +104,25 @@ pipes are read-only: they must not modify the buffers or the pipe ring slots
 in any way.
 .Pp
 The group naming is currently implemented by creating a persistent VALE port
-with the given name. If
+with the given name.
+If
 .Nm
-does not exit cleanly the ports will not be removed. Please use
-.Xr vale-ctl 1
+does not exit cleanly the ports will not be removed.
+Please use
+.Xr vale-ctl 4
 to remove any stale persistent VALE port.
 .Sh SEE ALSO
-.Pa http://info.iet.unipi.it/~luigi/netmap/
+.Xr netmap 4 ,
+.Xr bridge 8 ,
+.Xr pkt-gen 8
 .Pp
+.Pa http://info.iet.unipi.it/~luigi/netmap/
 .Sh AUTHORS
 .An -nosplit
 .Nm
 has been written by
 .An Seth Hall
-at Broala, USA. The facilities related to extra buffers and pipe groups
-have been added by
+at Corelight, USA.
+The facilities related to extra buffers and pipe groups have been added by
 .An Giuseppe Lettieri
-at University of Pisa, Italy, under contract by Broala, USA.
+at University of Pisa, Italy, under contract by Corelight, USA.
diff --git a/apps/lb/lb.c b/apps/lb/lb.c
index 14ea8eed6..91affce58 100644
--- a/apps/lb/lb.c
+++ b/apps/lb/lb.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Broala and Universita` di Pisa. All rights reserved.
+ * Copyright (C) 2017 Corelight, Inc. and Universita` di Pisa. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -22,21 +22,21 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-
-#include 
-#include 
 #include 
-#include 
+#include 
 #include 
-#include 
-
-#define NETMAP_WITH_LIBS
-#include 
-#include 
-
+#include 
 #include 		/* htonl */
-
 #include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
 
 #include "pkt_hash.h"
 #include "ctrs.h"
@@ -80,30 +80,34 @@ struct compact_ipv6_hdr {
 };
 
 #define MAX_IFNAMELEN 	64
+#define MAX_PORTNAMELEN	(MAX_IFNAMELEN + 40)
 #define DEF_OUT_PIPES 	2
 #define DEF_EXTRA_BUFS 	0
 #define DEF_BATCH	2048
 #define DEF_WAIT_LINK	2
-#define DEF_SYSLOG_INT	600
-#define BUF_REVOKE	100
+#define DEF_STATS_INT	600
+#define BUF_REVOKE	150
+#define STAT_MSG_MAXSIZE 1024
 
-struct {
-	char ifname[MAX_IFNAMELEN];
-	char base_name[MAX_IFNAMELEN];
+static struct {
+	char ifname[MAX_IFNAMELEN + 1];
+	char base_name[MAX_IFNAMELEN + 1];
 	int netmap_fd;
 	uint16_t output_rings;
 	uint16_t num_groups;
 	uint32_t extra_bufs;
 	uint16_t batch;
+	int stdout_interval;
 	int syslog_interval;
 	int wait_link;
+	bool busy_wait;
 } glob_arg;
 
 /*
  * the overflow queue is a circular queue of buffers
  */
 struct overflow_queue {
-	char name[MAX_IFNAMELEN];
+	char name[MAX_IFNAMELEN + 16];
 	struct netmap_slot *slots;
 	uint32_t head;
 	uint32_t tail;
@@ -111,7 +115,7 @@ struct overflow_queue {
 	uint32_t size;
 };
 
-struct overflow_queue *freeq;
+static struct overflow_queue *freeq;
 
 static inline int
 oq_full(struct overflow_queue *q)
@@ -156,20 +160,25 @@ oq_deq(struct overflow_queue *q)
 
 static volatile int do_abort = 0;
 
-uint64_t dropped = 0;
-uint64_t forwarded = 0;
-uint64_t non_ip = 0;
+static uint64_t dropped = 0;
+static uint64_t forwarded = 0;
+static uint64_t received_bytes = 0;
+static uint64_t received_pkts = 0;
+static uint64_t non_ip = 0;
+static uint32_t freeq_n = 0;
 
 struct port_des {
+	char interface[MAX_PORTNAMELEN];
 	struct my_ctrs ctr;
 	unsigned int last_sync;
+	uint32_t last_tail;
 	struct overflow_queue *oq;
-	struct nm_desc *nmd;
+	struct nmport_d *nmd;
 	struct netmap_ring *ring;
 	struct group_des *group;
 };
 
-struct port_des *ports;
+static struct port_des *ports;
 
 /* each group of pipes receives all the packets */
 struct group_des {
@@ -181,7 +190,22 @@ struct group_des {
 	int custom_port;
 };
 
-struct group_des *groups;
+static struct group_des *groups;
+
+/* statistcs */
+struct counters {
+	struct timeval ts;
+	struct my_ctrs *ctrs;
+	uint64_t received_pkts;
+	uint64_t received_bytes;
+	uint64_t non_ip;
+	uint32_t freeq_n;
+	int status __attribute__((aligned(64)));
+#define COUNTERS_EMPTY	0
+#define COUNTERS_FULL	1
+};
+
+static struct counters counters_buf;
 
 static void *
 print_stats(void *arg)
@@ -190,7 +214,6 @@ print_stats(void *arg)
 	int sys_int = 0;
 	(void)arg;
 	struct my_ctrs cur, prev;
-	char b1[40], b2[40];
 	struct my_ctrs *pipe_prev;
 
 	pipe_prev = calloc(npipes, sizeof(struct my_ctrs));
@@ -199,60 +222,125 @@ print_stats(void *arg)
 		exit(1);
 	}
 
+	char stat_msg[STAT_MSG_MAXSIZE] = "";
+
 	memset(&prev, 0, sizeof(prev));
-	gettimeofday(&prev.t, NULL);
 	while (!do_abort) {
-		int j, dosyslog = 0;
-		uint64_t pps, dps, usec;
+		int j, dosyslog = 0, dostdout = 0, newdata;
+		uint64_t pps = 0, dps = 0, bps = 0, dbps = 0, usec = 0;
 		struct my_ctrs x;
 
+		counters_buf.status = COUNTERS_EMPTY;
+		newdata = 0;
 		memset(&cur, 0, sizeof(cur));
-		usec = wait_for_next_report(&prev.t, &cur.t, 1000);
-
-		if (++sys_int == glob_arg.syslog_interval) {
-			dosyslog = 1;
-			sys_int = 0;
+		sleep(1);
+		if (counters_buf.status == COUNTERS_FULL) {
+			__sync_synchronize();
+			newdata = 1;
+			cur.t = counters_buf.ts;
+			if (prev.t.tv_sec || prev.t.tv_usec) {
+				usec = (cur.t.tv_sec - prev.t.tv_sec) * 1000000 +
+					cur.t.tv_usec - prev.t.tv_usec;
+			}
 		}
 
-		for (j = 0; j < npipes; ++j) {
-			struct port_des *p = &ports[j];
-
-			cur.pkts += p->ctr.pkts;
-			cur.drop += p->ctr.drop;
+		++sys_int;
+		if (glob_arg.stdout_interval && sys_int % glob_arg.stdout_interval == 0)
+				dostdout = 1;
+		if (glob_arg.syslog_interval && sys_int % glob_arg.syslog_interval == 0)
+				dosyslog = 1;
 
-			x.pkts = p->ctr.pkts - pipe_prev[j].pkts;
-			x.drop = p->ctr.drop - pipe_prev[j].drop;
-			pps = (x.pkts*1000000 + usec/2) / usec;
-			dps = (x.drop*1000000 + usec/2) / usec;
-			printf("%s/%s|", norm(b1, pps), norm(b2, dps));
-			pipe_prev[j] = p->ctr;
-
-			if (dosyslog) {
-				syslog(LOG_INFO,
-					"{"
-						"\"interface\":\"%s\","
-						"\"output_ring\":%"PRIu16","
-						"\"packets_forwarded\":%"PRIu64","
-						"\"packets_dropped\":%"PRIu64
-					"}", glob_arg.ifname, j, p->ctr.pkts, p->ctr.drop);
+		for (j = 0; j < npipes; ++j) {
+			struct my_ctrs *c = &counters_buf.ctrs[j];
+			cur.pkts += c->pkts;
+			cur.drop += c->drop;
+			cur.drop_bytes += c->drop_bytes;
+			cur.bytes += c->bytes;
+
+			if (usec) {
+				x.pkts = c->pkts - pipe_prev[j].pkts;
+				x.drop = c->drop - pipe_prev[j].drop;
+				x.bytes = c->bytes - pipe_prev[j].bytes;
+				x.drop_bytes = c->drop_bytes - pipe_prev[j].drop_bytes;
+				pps = (x.pkts*1000000 + usec/2) / usec;
+				dps = (x.drop*1000000 + usec/2) / usec;
+				bps = ((x.bytes*1000000 + usec/2) / usec) * 8;
+				dbps = ((x.drop_bytes*1000000 + usec/2) / usec) * 8;
 			}
+			pipe_prev[j] = *c;
+
+			if ( (dosyslog || dostdout) && newdata )
+				snprintf(stat_msg, STAT_MSG_MAXSIZE,
+				       "{"
+				       "\"ts\":%.6f,"
+				       "\"interface\":\"%s\","
+				       "\"output_ring\":%" PRIu16 ","
+				       "\"packets_forwarded\":%" PRIu64 ","
+				       "\"packets_dropped\":%" PRIu64 ","
+				       "\"data_forward_rate_Mbps\":%.4f,"
+				       "\"data_drop_rate_Mbps\":%.4f,"
+				       "\"packet_forward_rate_kpps\":%.4f,"
+				       "\"packet_drop_rate_kpps\":%.4f,"
+				       "\"overflow_queue_size\":%" PRIu32
+				       "}", cur.t.tv_sec + (cur.t.tv_usec / 1000000.0),
+				            ports[j].interface,
+				            j,
+				            c->pkts,
+				            c->drop,
+				            (double)bps / 1024 / 1024,
+				            (double)dbps / 1024 / 1024,
+				            (double)pps / 1000,
+				            (double)dps / 1000,
+				            c->oq_n);
+
+			if (dosyslog && stat_msg[0])
+				syslog(LOG_INFO, "%s", stat_msg);
+			if (dostdout && stat_msg[0])
+				printf("%s\n", stat_msg);
 		}
-		printf("\n");
-		if (dosyslog) {
-			syslog(LOG_INFO,
-				"{"
-					"\"interface\":\"%s\","
-					"\"output_ring\":null,"
-					"\"packets_forwarded\":%"PRIu64","
-					"\"packets_dropped\":%"PRIu64","
-					"\"non_ip_packets\":%"PRIu64
-				"}", glob_arg.ifname, forwarded, dropped, non_ip);
+		if (usec) {
+			x.pkts = cur.pkts - prev.pkts;
+			x.drop = cur.drop - prev.drop;
+			x.bytes = cur.bytes - prev.bytes;
+			x.drop_bytes = cur.drop_bytes - prev.drop_bytes;
+			pps = (x.pkts*1000000 + usec/2) / usec;
+			dps = (x.drop*1000000 + usec/2) / usec;
+			bps = ((x.bytes*1000000 + usec/2) / usec) * 8;
+			dbps = ((x.drop_bytes*1000000 + usec/2) / usec) * 8;
 		}
-		x.pkts = cur.pkts - prev.pkts;
-		x.drop = cur.drop - prev.drop;
-		pps = (x.pkts*1000000 + usec/2) / usec;
-		dps = (x.drop*1000000 + usec/2) / usec;
-		printf("===> aggregate %spps %sdps\n", norm(b1, pps), norm(b2, dps));
+
+		if ( (dosyslog || dostdout) && newdata )
+			snprintf(stat_msg, STAT_MSG_MAXSIZE,
+			         "{"
+			         "\"ts\":%.6f,"
+			         "\"interface\":\"%s\","
+			         "\"output_ring\":null,"
+			         "\"packets_received\":%" PRIu64 ","
+			         "\"packets_forwarded\":%" PRIu64 ","
+			         "\"packets_dropped\":%" PRIu64 ","
+			         "\"non_ip_packets\":%" PRIu64 ","
+			         "\"data_forward_rate_Mbps\":%.4f,"
+			         "\"data_drop_rate_Mbps\":%.4f,"
+			         "\"packet_forward_rate_kpps\":%.4f,"
+			         "\"packet_drop_rate_kpps\":%.4f,"
+			         "\"free_buffer_slots\":%" PRIu32
+			         "}", cur.t.tv_sec + (cur.t.tv_usec / 1000000.0),
+			              glob_arg.ifname,
+			              received_pkts,
+			              cur.pkts,
+			              cur.drop,
+			              counters_buf.non_ip,
+			              (double)bps / 1024 / 1024,
+			              (double)dbps / 1024 / 1024,
+			              (double)pps / 1000,
+			              (double)dps / 1000,
+			              counters_buf.freeq_n);
+
+		if (dosyslog && stat_msg[0])
+			syslog(LOG_INFO, "%s", stat_msg);
+		if (dostdout && stat_msg[0])
+			printf("%s\n", stat_msg);
+
 		prev = cur;
 	}
 
@@ -287,7 +375,7 @@ free_buffers(void)
 	D("added %d buffers to netmap free list", tot);
 
 	for (i = 0; i < glob_arg.output_rings + 1; ++i) {
-		nm_close(ports[i].nmd);
+		nmport_close(ports[i].nmd);
 	}
 }
 
@@ -299,27 +387,29 @@ static void sigint_h(int sig)
 	signal(SIGINT, SIG_DFL);
 }
 
-void usage()
+static void usage()
 {
 	printf("usage: lb [options]\n");
 	printf("where options are:\n");
+	printf("  -h              	view help text\n");
 	printf("  -i iface        	interface name (required)\n");
 	printf("  -p [prefix:]npipes	add a new group of output pipes\n");
 	printf("  -B nbufs        	number of extra buffers (default: %d)\n", DEF_EXTRA_BUFS);
 	printf("  -b batch        	batch size (default: %d)\n", DEF_BATCH);
 	printf("  -w seconds        	wait for link up (default: %d)\n", DEF_WAIT_LINK);
-	printf("  -s seconds      	seconds between syslog messages (default: %d)\n",
-			DEF_SYSLOG_INT);
+	printf("  -W                    enable busy waiting. this will run your CPU at 100%%\n");
+	printf("  -s seconds      	seconds between syslog stats messages (default: 0)\n");
+	printf("  -o seconds      	seconds between stdout stats messages (default: 0)\n");
 	exit(0);
 }
 
 static int
-parse_pipes(char *spec)
+parse_pipes(const char *spec)
 {
-	char *end = index(spec, ':');
+	const char *end = index(spec, ':');
 	static int max_groups = 0;
 	struct group_des *g;
-       
+
 	ND("spec %s num_groups %d", spec, glob_arg.num_groups);
 	if (max_groups < glob_arg.num_groups + 1) {
 		size_t size = sizeof(*g) * (glob_arg.num_groups + 1);
@@ -368,7 +458,8 @@ parse_pipes(char *spec)
 }
 
 /* complete the initialization of the groups data structure */
-void init_groups(void)
+static void
+init_groups(void)
 {
 	int i, j, t = 0;
 	struct group_des *g = NULL;
@@ -389,58 +480,88 @@ void init_groups(void)
 	g->last = 1;
 }
 
+
+/* To support packets that span multiple slots (NS_MOREFRAG) we
+ * need to make sure of the following:
+ *
+ * - all fragments of the same packet must go to the same output pipe
+ * - when dropping, all fragments of the same packet must be dropped
+ *
+ * For the former point we remember and reuse the last hash computed
+ * in each input ring, and only update it when NS_MOREFRAG was not
+ * set in the last received slot (this marks the start of a new packet).
+ *
+ * For the latter point, we only update the output ring head pointer
+ * when an entire packet has been forwarded. We keep a shadow_head
+ * pointer to know where to put the next partial fragment and,
+ * when the need to drop arises, we roll it back to head.
+ */
+struct morefrag {
+	uint16_t last_flag;	/* for input rings */
+	uint32_t last_hash;	/* for input rings */
+	uint32_t shadow_head;	/* for output rings */
+};
+
 /* push the packet described by slot rs to the group g.
  * This may cause other buffers to be pushed down the
  * chain headed by g.
  * Return a free buffer.
  */
-uint32_t forward_packet(struct group_des *g, struct netmap_slot *rs)
+static uint32_t
+forward_packet(struct group_des *g, struct netmap_slot *rs)
 {
 	uint32_t hash = rs->ptr;
 	uint32_t output_port = hash % g->nports;
 	struct port_des *port = &g->ports[output_port];
 	struct netmap_ring *ring = port->ring;
 	struct overflow_queue *q = port->oq;
+	struct morefrag *mf = (struct morefrag *)ring->sem;
+	uint16_t curmf = rs->flags & NS_MOREFRAG;
 
 	/* Move the packet to the output pipe, unless there is
 	 * either no space left on the ring, or there is some
 	 * packet still in the overflow queue (since those must
 	 * take precedence over the new one)
 	*/
-	if (nm_ring_space(ring) && (q == NULL || oq_empty(q))) {
-		struct netmap_slot *ts = &ring->slot[ring->cur];
+	if (mf->shadow_head != ring->tail && (q == NULL || oq_empty(q))) {
+		struct netmap_slot *ts = &ring->slot[mf->shadow_head];
 		struct netmap_slot old_slot = *ts;
-		uint32_t free_buf;
 
 		ts->buf_idx = rs->buf_idx;
 		ts->len = rs->len;
-		ts->flags |= NS_BUF_CHANGED;
+		ts->flags = rs->flags | NS_BUF_CHANGED;
 		ts->ptr = rs->ptr;
-		ring->head = ring->cur = nm_ring_next(ring, ring->cur);
+		mf->shadow_head = nm_ring_next(ring, mf->shadow_head);
+		if (!curmf) {
+			ring->head = mf->shadow_head;
+		}
+		ND("curmf %2x ts->flags %2x shadow_head %3u head %3u tail %3u",
+				curmf, ts->flags, mf->shadow_head, ring->head, ring->tail);
+		port->ctr.bytes += rs->len;
 		port->ctr.pkts++;
 		forwarded++;
-		if (old_slot.ptr && !g->last) {
-			/* old slot not empty and we are not the last group:
-			 * push it further down the chain
-			 */
-			free_buf = forward_packet(g + 1, &old_slot);
-		} else {
-			/* just return the old slot buffer: it is
-			 * either empty or already seen by everybody
-			 */
-			free_buf = old_slot.buf_idx;
-		}
-
-		return free_buf;
+		return old_slot.buf_idx;
 	}
 
 	/* use the overflow queue, if available */
 	if (q == NULL || oq_full(q)) {
+		uint32_t scan;
 		/* no space left on the ring and no overflow queue
 		 * available: we are forced to drop the packet
 		 */
+
+		/* drop previous fragments, if any */
+		for (scan = ring->head; scan != mf->shadow_head;
+				scan = nm_ring_next(ring, scan)) {
+			struct netmap_slot *ts = &ring->slot[scan];
+			dropped++;
+			port->ctr.drop_bytes += ts->len;
+		}
+		mf->shadow_head = ring->head;
+
 		dropped++;
 		port->ctr.drop++;
+		port->ctr.drop_bytes += rs->len;
 		return rs->buf_idx;
 	}
 
@@ -469,16 +590,22 @@ uint32_t forward_packet(struct group_des *g, struct netmap_slot *rs)
 
 		/* move the oldest BUF_REVOKE buffers from the
 		 * lp queue to the free queue
+		 *
+		 * We cannot revoke a partially received packet.
+		 * To make thinks simple we make sure to leave
+		 * at least NETMAP_MAX_FRAGS slots in the queue.
 		 */
-		// XXX optimize this cycle
-		for (j = 0; lp->oq->n && j < BUF_REVOKE; j++) {
+		for (j = 0; lp->oq->n > NETMAP_MAX_FRAGS && j < BUF_REVOKE; j++) {
 			struct netmap_slot tmp = oq_deq(lp->oq);
+
+			dropped++;
+			lp->ctr.drop++;
+			lp->ctr.drop_bytes += tmp.len;
+
 			oq_enq(freeq, &tmp);
 		}
 
 		ND(1, "revoked %d buffers from %s", j, lq->name);
-		lp->ctr.drop += j;
-		dropped += j;
 	}
 
 	return oq_deq(freeq).buf_idx;
@@ -490,14 +617,17 @@ int main(int argc, char **argv)
 	uint32_t i;
 	int rv;
 	unsigned int iter = 0;
+	int poll_timeout = 10; /* default */
 
 	glob_arg.ifname[0] = '\0';
 	glob_arg.output_rings = 0;
 	glob_arg.batch = DEF_BATCH;
 	glob_arg.wait_link = DEF_WAIT_LINK;
-	glob_arg.syslog_interval = DEF_SYSLOG_INT;
+	glob_arg.busy_wait = false;
+	glob_arg.syslog_interval = 0;
+	glob_arg.stdout_interval = 0;
 
-	while ( (ch = getopt(argc, argv, "i:p:b:B:s:")) != -1) {
+	while ( (ch = getopt(argc, argv, "hi:p:b:B:s:o:w:W")) != -1) {
 		switch (ch) {
 		case 'i':
 			D("interface is %s", optarg);
@@ -529,16 +659,32 @@ int main(int argc, char **argv)
 			D("batch is %d", glob_arg.batch);
 			break;
 
+		case 'w':
+			glob_arg.wait_link = atoi(optarg);
+			D("link wait for up time is %d", glob_arg.wait_link);
+			break;
+
+		case 'W':
+			glob_arg.busy_wait = true;
+			break;
+
+		case 'o':
+			glob_arg.stdout_interval = atoi(optarg);
+			break;
+
 		case 's':
 			glob_arg.syslog_interval = atoi(optarg);
-			D("syslog interval is %d", glob_arg.syslog_interval);
+			break;
+
+		case 'h':
+			usage();
+			return 0;
 			break;
 
 		default:
 			D("bad option %c %s", ch, optarg);
 			usage();
 			return 1;
-
 		}
 	}
 
@@ -548,19 +694,13 @@ int main(int argc, char **argv)
 		return 1;
 	}
 
-	/* extract the base name */
-	char *nscan = strncmp(glob_arg.ifname, "netmap:", 7) ?
-			glob_arg.ifname : glob_arg.ifname + 7;
-	strncpy(glob_arg.base_name, nscan, MAX_IFNAMELEN);
-	for (nscan = glob_arg.base_name; *nscan && !index("-*^{}/@", *nscan); nscan++)
-		;
-	*nscan = '\0';	
-
 	if (glob_arg.num_groups == 0)
 		parse_pipes("");
 
-	setlogmask(LOG_UPTO(LOG_INFO));
-	openlog("lb", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
+	if (glob_arg.syslog_interval) {
+		setlogmask(LOG_UPTO(LOG_INFO));
+		openlog("lb", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
+	}
 
 	uint32_t npipes = glob_arg.output_rings;
 
@@ -573,32 +713,33 @@ int main(int argc, char **argv)
 		return 1;
 	}
 	struct port_des *rxport = &ports[npipes];
-	init_groups();
 
-	if (pthread_create(&stat_thread, NULL, print_stats, NULL) == -1) {
-		D("unable to create the stats thread: %s", strerror(errno));
-		return 1;
+	rxport->nmd = nmport_prepare(glob_arg.ifname);
+	if (rxport->nmd == NULL) {
+		D("cannot parse %s", glob_arg.ifname);
+		return (1);
 	}
+	/* extract the base name */
+	strncpy(glob_arg.base_name, rxport->nmd->hdr.nr_name, MAX_IFNAMELEN);
 
+	init_groups();
 
-	/* we need base_req to specify pipes and extra bufs */
-	struct nmreq base_req;
-	memset(&base_req, 0, sizeof(base_req));
-
-	base_req.nr_arg1 = npipes;
-	base_req.nr_arg3 = glob_arg.extra_bufs;
+	memset(&counters_buf, 0, sizeof(counters_buf));
+	counters_buf.ctrs = calloc(npipes, sizeof(struct my_ctrs));
+	if (!counters_buf.ctrs) {
+		D("failed to allocate the counters snapshot buffer");
+		return 1;
+	}
 
-	rxport->nmd = nm_open(glob_arg.ifname, &base_req, 0, NULL);
+	rxport->nmd->reg.nr_extra_bufs = glob_arg.extra_bufs;
 
-	if (rxport->nmd == NULL) {
+	if (nmport_open_desc(rxport->nmd) < 0) {
 		D("cannot open %s", glob_arg.ifname);
 		return (1);
-	} else {
-		D("successfully opened %s (tx rings: %u)", glob_arg.ifname,
-		  rxport->nmd->req.nr_tx_slots);
 	}
+	D("successfully opened %s", glob_arg.ifname);
 
-	uint32_t extra_bufs = rxport->nmd->req.nr_arg3;
+	uint32_t extra_bufs = rxport->nmd->reg.nr_extra_bufs;
 	struct overflow_queue *oq = NULL;
 	/* reference ring to access the buffers */
 	rxport->ring = NETMAP_RXRING(rxport->nmd->nifp, 0);
@@ -663,20 +804,34 @@ int main(int argc, char **argv)
 		int k;
 		for (k = 0; k < g->nports; ++k) {
 			struct port_des *p = &g->ports[k];
-			char interface[25];
-			sprintf(interface, "netmap:%s{%d/xT@%d", g->pipename, g->first_id + k,
-					rxport->nmd->req.nr_arg2);
-			D("opening pipe named %s", interface);
+			snprintf(p->interface, MAX_PORTNAMELEN, "%s%s{%d/xT@%d",
+					(strncmp(g->pipename, "vale", 4) ? "netmap:" : ""),
+					g->pipename, g->first_id + k,
+					rxport->nmd->reg.nr_mem_id);
+			D("opening pipe named %s", p->interface);
 
-			p->nmd = nm_open(interface, NULL, 0, rxport->nmd);
+			p->nmd = nmport_open(p->interface);
 
 			if (p->nmd == NULL) {
-				D("cannot open %s", interface);
+				D("cannot open %s", p->interface);
+				return (1);
+			} else if (p->nmd->mem != rxport->nmd->mem) {
+				D("failed to open pipe #%d in zero-copy mode, "
+					"please close any application that uses either pipe %s}%d, "
+				        "or %s{%d, and retry",
+					k + 1, g->pipename, g->first_id + k, g->pipename, g->first_id + k);
 				return (1);
 			} else {
+				struct morefrag *mf;
+
 				D("successfully opened pipe #%d %s (tx slots: %d)",
-				  k + 1, interface, p->nmd->req.nr_tx_slots);
+				  k + 1, p->interface, p->nmd->reg.nr_tx_slots);
 				p->ring = NETMAP_TXRING(p->nmd->nifp, 0);
+				p->last_tail = nm_ring_next(p->ring, p->ring->tail);
+				mf = (struct morefrag *)p->ring->sem;
+				mf->last_flag = 0;	/* unused */
+				mf->last_hash = 0;	/* unused */
+				mf->shadow_head = p->ring->head;
 			}
 			D("zerocopy %s",
 			  (rxport->nmd->mem == p->nmd->mem) ? "enabled" : "disabled");
@@ -690,7 +845,7 @@ int main(int argc, char **argv)
 					extra_bufs = 0;
 				}
 				q->size = extra_bufs;
-				snprintf(q->name, MAX_IFNAMELEN, "oq %s{%d", g->pipename, k);
+				snprintf(q->name, sizeof(q->name), "oq %s{%4d", g->pipename, k);
 				p->oq = q;
 			}
 		}
@@ -711,16 +866,48 @@ int main(int argc, char **argv)
 
 	sleep(glob_arg.wait_link);
 
+	/* start stats thread after wait_link */
+	if (pthread_create(&stat_thread, NULL, print_stats, NULL) == -1) {
+		D("unable to create the stats thread: %s", strerror(errno));
+		return 1;
+	}
+
 	struct pollfd pollfd[npipes + 1];
 	memset(&pollfd, 0, sizeof(pollfd));
 	signal(SIGINT, sigint_h);
+
+	/* make sure we wake up as often as needed, even when there are no
+	 * packets coming in
+	 */
+	if (glob_arg.syslog_interval > 0 && glob_arg.syslog_interval < poll_timeout)
+		poll_timeout = glob_arg.syslog_interval;
+	if (glob_arg.stdout_interval > 0 && glob_arg.stdout_interval < poll_timeout)
+		poll_timeout = glob_arg.stdout_interval;
+
+	/* initialize the morefrag structures for the input rings */
+	for (i = rxport->nmd->first_rx_ring; i <= rxport->nmd->last_rx_ring; i++) {
+		struct netmap_ring *rxring = NETMAP_RXRING(rxport->nmd->nifp, i);
+		struct morefrag *mf = (struct morefrag *)rxring->sem;
+
+		mf->last_flag = 0;
+		mf->last_hash = 0;
+		mf->shadow_head = 0; /* unused */
+	}
+
 	while (!do_abort) {
 		u_int polli = 0;
 		iter++;
 
 		for (i = 0; i < npipes; ++i) {
 			struct netmap_ring *ring = ports[i].ring;
-			if (nm_ring_next(ring, ring->tail) == ring->cur) {
+			int pending = nm_tx_pending(ring);
+
+			/* if there are packets pending, we want to be notified when
+			 * tail moves, so we let cur=tail
+			 */
+			ring->cur = pending ? ring->tail : ring->head;
+
+			if (!glob_arg.busy_wait && !pending) {
 				/* no need to poll, there are no packets pending */
 				continue;
 			}
@@ -735,14 +922,46 @@ int main(int argc, char **argv)
 		pollfd[polli].revents = 0;
 		++polli;
 
-		//RD(5, "polling %d file descriptors", polli+1);
-		rv = poll(pollfd, polli, 10);
+		ND(5, "polling %d file descriptors", polli);
+		rv = poll(pollfd, polli, poll_timeout);
 		if (rv <= 0) {
 			if (rv < 0 && errno != EAGAIN && errno != EINTR)
 				RD(1, "poll error %s", strerror(errno));
-			continue;
+			goto send_stats;
+		}
+
+		/* if there are several groups, try pushing released packets from
+		 * upstream groups to the downstream ones.
+		 *
+		 * It is important to do this before returned slots are reused
+		 * for new transmissions. For the same reason, this must be
+		 * done starting from the last group going backwards.
+		 */
+		for (i = glob_arg.num_groups - 1U; i > 0; i--) {
+			struct group_des *g = &groups[i - 1];
+
+			for (j = 0; j < g->nports; j++) {
+				struct port_des *p = &g->ports[j];
+				struct netmap_ring *ring = p->ring;
+				uint32_t last = p->last_tail,
+					 stop = nm_ring_next(ring, ring->tail);
+
+				/* slight abuse of the API here: we touch the slot
+				 * pointed to by tail
+				 */
+				for ( ; last != stop; last = nm_ring_next(ring, last)) {
+					struct netmap_slot *rs = &ring->slot[last];
+					// XXX less aggressive?
+					rs->buf_idx = forward_packet(g + 1, rs);
+					rs->flags = NS_BUF_CHANGED;
+					rs->ptr = 0;
+				}
+				p->last_tail = last;
+			}
 		}
 
+
+
 		if (oq) {
 			/* try to push packets from the overflow queues
 			 * to the corresponding pipes
@@ -750,71 +969,75 @@ int main(int argc, char **argv)
 			for (i = 0; i < npipes; i++) {
 				struct port_des *p = &ports[i];
 				struct overflow_queue *q = p->oq;
-				struct group_des *g = p->group;
-				uint32_t j, lim;
+				uint32_t k;
+				int64_t lim;
 				struct netmap_ring *ring;
 				struct netmap_slot *slot;
+				struct morefrag *mf;
 
 				if (oq_empty(q))
 					continue;
 				ring = p->ring;
-				lim = nm_ring_space(ring);
+				mf = (struct morefrag *)ring->sem;
+				lim = ring->tail - mf->shadow_head;
 				if (!lim)
 					continue;
+				if (lim < 0)
+					lim += ring->num_slots;
 				if (q->n < lim)
 					lim = q->n;
-				for (j = 0; j < lim; j++) {
+				for (k = 0; k < lim; k++) {
 					struct netmap_slot s = oq_deq(q), tmp;
 					tmp.ptr = 0;
-					slot = &ring->slot[ring->cur];
-					if (slot->ptr && !g->last) {
-						tmp.buf_idx = forward_packet(g + 1, slot);
-						/* the forwarding may have removed packets
-						 * from the current queue
-						 */
-						if (q->n < lim)
-							lim = q->n;
-					} else {
-						tmp.buf_idx = slot->buf_idx;
-					}
+					slot = &ring->slot[mf->shadow_head];
+					tmp.buf_idx = slot->buf_idx;
 					oq_enq(freeq, &tmp);
 					*slot = s;
 					slot->flags |= NS_BUF_CHANGED;
-					ring->cur = nm_ring_next(ring, ring->cur);
+					mf->shadow_head = nm_ring_next(ring, mf->shadow_head);
+					if (!(slot->flags & NS_MOREFRAG))
+						ring->head = mf->shadow_head;
 				}
-				ring->head = ring->cur;
-				forwarded += lim;
-				p->ctr.pkts += lim;
 			}
 		}
 
+		/* push any new packets from the input port to the first group */
 		int batch = 0;
 		for (i = rxport->nmd->first_rx_ring; i <= rxport->nmd->last_rx_ring; i++) {
 			struct netmap_ring *rxring = NETMAP_RXRING(rxport->nmd->nifp, i);
+			struct morefrag *mf = (struct morefrag *)rxring->sem;
 
 			//D("prepare to scan rings");
-			int next_cur = rxring->cur;
-			struct netmap_slot *next_slot = &rxring->slot[next_cur];
+			int next_head = rxring->head;
+			struct netmap_slot *next_slot = &rxring->slot[next_head];
 			const char *next_buf = NETMAP_BUF(rxring, next_slot->buf_idx);
 			while (!nm_ring_empty(rxring)) {
 				struct netmap_slot *rs = next_slot;
 				struct group_des *g = &groups[0];
+				++received_pkts;
+				received_bytes += rs->len;
 
 				// CHOOSE THE CORRECT OUTPUT PIPE
-				uint32_t hash = pkt_hdr_hash((const unsigned char *)next_buf, 4, 'B');
-				if (hash == 0) {
+				// If the previous slot had NS_MOREFRAG set, this is another
+				// fragment of the last packet and it should go to the same
+				// output pipe as before.
+				if (!mf->last_flag) {
+					// 'B' is just a hashing seed
+					mf->last_hash = pkt_hdr_hash((const unsigned char *)next_buf, 4, 'B');
+				}
+				mf->last_flag = rs->flags & NS_MOREFRAG;
+				rs->ptr = mf->last_hash;
+				if (rs->ptr == 0) {
 					non_ip++; // XXX ??
 				}
-				rs->ptr = hash | (1UL << 32);
 				// prefetch the buffer for the next round
-				next_cur = nm_ring_next(rxring, next_cur);
-				next_slot = &rxring->slot[next_cur];
+				next_head = nm_ring_next(rxring, next_head);
+				next_slot = &rxring->slot[next_head];
 				next_buf = NETMAP_BUF(rxring, next_slot->buf_idx);
 				__builtin_prefetch(next_buf);
-				// 'B' is just a hashing seed
 				rs->buf_idx = forward_packet(g, rs);
-				rs->flags |= NS_BUF_CHANGED;
-				rxring->head = rxring->cur = next_cur;
+				rs->flags = NS_BUF_CHANGED;
+				rxring->head = rxring->cur = next_head;
 
 				batch++;
 				if (unlikely(batch >= glob_arg.batch)) {
@@ -828,6 +1051,39 @@ int main(int argc, char **argv)
 			}
 
 		}
+
+	send_stats:
+		if (counters_buf.status == COUNTERS_FULL)
+			continue;
+		/* take a new snapshot of the counters */
+		gettimeofday(&counters_buf.ts, NULL);
+		for (i = 0; i < npipes; i++) {
+			struct my_ctrs *c = &counters_buf.ctrs[i];
+			*c = ports[i].ctr;
+			/*
+			 * If there are overflow queues, copy the number of them for each
+			 * port to the ctrs.oq_n variable for each port.
+			 */
+			if (ports[i].oq != NULL)
+				c->oq_n = ports[i].oq->n;
+		}
+		counters_buf.received_pkts = received_pkts;
+		counters_buf.received_bytes = received_bytes;
+		counters_buf.non_ip = non_ip;
+		if (freeq != NULL)
+			counters_buf.freeq_n = freeq->n;
+		__sync_synchronize();
+		counters_buf.status = COUNTERS_FULL;
+	}
+
+	/*
+	 * If freeq exists, copy the number to the freeq_n member of the
+	 * message struct, otherwise set it to 0.
+	 */
+	if (freeq != NULL) {
+		freeq_n = freeq->n;
+	} else {
+		freeq_n = 0;
 	}
 
 	pthread_join(stat_thread, NULL);
diff --git a/apps/lb/pkt_hash.c b/apps/lb/pkt_hash.c
index f775b2a3e..643f6398a 100644
--- a/apps/lb/pkt_hash.c
+++ b/apps/lb/pkt_hash.c
@@ -25,6 +25,7 @@
  ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  ** POSSIBILITY OF SUCH DAMAGE.
  **/
+
 /* for func prototypes */
 #include "pkt_hash.h"
 
@@ -48,6 +49,9 @@
 /* for memset */
 #include 
 
+#include 
+#include 
+
 //#include 
 /*---------------------------------------------------------------------*/
 /**
@@ -57,36 +61,49 @@
 static void
 build_sym_key_cache(uint32_t *cache, int cache_len)
 {
-	static const uint8_t key[] = {
-		0x50, 0x6d, 0x50, 0x6d,
-                0x50, 0x6d, 0x50, 0x6d,
-                0x50, 0x6d, 0x50, 0x6d,
-                0x50, 0x6d, 0x50, 0x6d,
-                0xcb, 0x2b, 0x5a, 0x5a,
-		0xb4, 0x30, 0x7b, 0xae,
-                0xa3, 0x2d, 0xcb, 0x77,
-                0x0c, 0xf2, 0x30, 0x80,
-                0x3b, 0xb7, 0x42, 0x6a,
-                0xfa, 0x01, 0xac, 0xbe};
+	static const uint8_t key[] = { 0x50, 0x6d };
 
         uint32_t result = (((uint32_t)key[0]) << 24) |
                 (((uint32_t)key[1]) << 16) |
-                (((uint32_t)key[2]) << 8)  |
-                ((uint32_t)key[3]);
+                (((uint32_t)key[0]) << 8)  |
+                ((uint32_t)key[1]);
 
         uint32_t idx = 32;
         int i;
 
         for (i = 0; i < cache_len; i++, idx++) {
-                uint8_t shift = (idx % (sizeof(uint8_t) * 8));
+                uint8_t shift = (idx % 8);
                 uint32_t bit;
 
                 cache[i] = result;
-                bit = ((key[idx/(sizeof(uint8_t) * 8)] << shift)
-		       & 0x80) ? 1 : 0;
+                bit = ((key[(idx/8) & 1] << shift) & 0x80) ? 1 : 0;
                 result = ((result << 1) | bit);
         }
 }
+
+static void
+build_byte_cache(uint32_t byte_cache[256][4])
+{
+#define KEY_CACHE_LEN			96
+	int i, j, k;
+	uint32_t key_cache[KEY_CACHE_LEN];
+
+	build_sym_key_cache(key_cache, KEY_CACHE_LEN);
+
+	for (i = 0; i < 4; i++) {
+		for (j = 0; j < 256; j++) {
+			uint8_t b = j;
+			byte_cache[j][i] = 0;
+			for (k = 0; k < 8; k++) {
+				if (b & 0x80)
+					byte_cache[j][i] ^= key_cache[8 * i + k];
+				b <<= 1U;
+			}
+		}
+	}
+}
+
+
 /*---------------------------------------------------------------------*/
 /**
  ** Computes symmetric hash based on the 4-tuple header data
@@ -94,71 +111,65 @@ build_sym_key_cache(uint32_t *cache, int cache_len)
 static uint32_t
 sym_hash_fn(uint32_t sip, uint32_t dip, uint16_t sp, uint32_t dp)
 {
-#define MSB32				0x80000000
-#define MSB16				0x8000
-#define KEY_CACHE_LEN			96
-
 	uint32_t rc = 0;
-	int i;
 	static int first_time = 1;
-	static uint32_t key_cache[KEY_CACHE_LEN] = {0};
+	static uint32_t byte_cache[256][4];
+	uint8_t *sip_b = (uint8_t *)&sip,
+		*dip_b = (uint8_t *)&dip,
+		*sp_b  = (uint8_t *)&sp,
+		*dp_b  = (uint8_t *)&dp;
 
 	if (first_time) {
-		build_sym_key_cache(key_cache, KEY_CACHE_LEN);
+		build_byte_cache(byte_cache);
 		first_time = 0;
 	}
 
-	for (i = 0; i < 32; i++) {
-                if (sip & MSB32)
-                        rc ^= key_cache[i];
-                sip <<= 1;
-        }
-        for (i = 0; i < 32; i++) {
-                if (dip & MSB32)
-			rc ^= key_cache[32+i];
-                dip <<= 1;
-        }
-        for (i = 0; i < 16; i++) {
-		if (sp & MSB16)
-                        rc ^= key_cache[64+i];
-                sp <<= 1;
-        }
-        for (i = 0; i < 16; i++) {
-                if (dp & MSB16)
-                        rc ^= key_cache[80+i];
-                dp <<= 1;
-        }
+	rc = byte_cache[sip_b[3]][0] ^
+	     byte_cache[sip_b[2]][1] ^
+	     byte_cache[sip_b[1]][2] ^
+	     byte_cache[sip_b[0]][3] ^
+	     byte_cache[dip_b[3]][0] ^
+	     byte_cache[dip_b[2]][1] ^
+	     byte_cache[dip_b[1]][2] ^
+	     byte_cache[dip_b[0]][3] ^
+	     byte_cache[sp_b[1]][0] ^
+	     byte_cache[sp_b[0]][1] ^
+	     byte_cache[dp_b[1]][2] ^
+	     byte_cache[dp_b[0]][3];
 
 	return rc;
 }
+static uint32_t decode_gre_hash(const uint8_t *, uint8_t, uint8_t);
 /*---------------------------------------------------------------------*/
 /**
  ** Parser + hash function for the IPv4 packet
  **/
 static uint32_t
-decode_ip_n_hash(struct ip *iph, uint8_t hash_split, uint8_t seed)
+decode_ip_n_hash(const struct ip *iph, uint8_t hash_split, uint8_t seed)
 {
 	uint32_t rc = 0;
 
-	if (hash_split == 2) {
+	if (iph->ip_hl < 5 || (iph->ip_hl<<2) > ntohs(iph->ip_len)) {
+		rc = 0;
+	} else if (hash_split == 2) {
 		rc = sym_hash_fn(ntohl(iph->ip_src.s_addr),
 			ntohl(iph->ip_dst.s_addr),
 			ntohs(0xFFFD) + seed,
 			ntohs(0xFFFE) + seed);
 	} else {
-		struct tcphdr *tcph = NULL;
-		struct udphdr *udph = NULL;
+		const struct tcphdr *tcph = NULL;
+		const struct udphdr *udph = NULL;
 
 		switch (iph->ip_p) {
 		case IPPROTO_TCP:
-			tcph = (struct tcphdr *)((uint8_t *)iph + (iph->ip_hl<<2));
+			tcph = (const struct tcphdr *)((const uint8_t *)iph + (iph->ip_hl<<2));
 			rc = sym_hash_fn(ntohl(iph->ip_src.s_addr),
 					 ntohl(iph->ip_dst.s_addr),
 					 ntohs(tcph->th_sport) + seed,
 					 ntohs(tcph->th_dport) + seed);
 			break;
 		case IPPROTO_UDP:
-			udph = (struct udphdr *)((uint8_t *)iph + (iph->ip_hl<<2));
+			udph = (const struct udphdr *)((const uint8_t *)iph + (iph->ip_hl<<2));
 			rc = sym_hash_fn(ntohl(iph->ip_src.s_addr),
 					 ntohl(iph->ip_dst.s_addr),
 					 ntohs(udph->uh_sport) + seed,
@@ -166,20 +177,26 @@ decode_ip_n_hash(struct ip *iph, uint8_t hash_split, uint8_t seed)
 			break;
 		case IPPROTO_IPIP:
 			/* tunneling */
-			rc = decode_ip_n_hash((struct ip *)((uint8_t *)iph + (iph->ip_hl<<2)),
+			rc = decode_ip_n_hash((const struct ip *)((const uint8_t *)iph + (iph->ip_hl<<2)),
 					      hash_split, seed);
 			break;
+		case IPPROTO_GRE:
+			rc = decode_gre_hash((const uint8_t *)iph + (iph->ip_hl<<2),
+					hash_split, seed);
+			break;
+		case IPPROTO_ICMP:
+		case IPPROTO_ESP:
+		case IPPROTO_PIM:
+		case IPPROTO_IGMP:
 		default:
 			/*
 			 ** the hash strength (although weaker but) should still hold
 			 ** even with 2 fields
+			 **/
 			rc = sym_hash_fn(ntohl(iph->ip_src.s_addr),
 					 ntohl(iph->ip_dst.s_addr),
 					 ntohs(0xFFFD) + seed,
 					 ntohs(0xFFFE) + seed);
-			 **/
-			// We return 0 to indicate that the packet couldn't be balanced.
-			return 0;
 			break;
 		}
 	}
@@ -190,7 +207,7 @@ decode_ip_n_hash(struct ip *iph, uint8_t hash_split, uint8_t seed)
  ** Parser + hash function for the IPv6 packet
  **/
 static uint32_t
-decode_ipv6_n_hash(struct ip6_hdr *ipv6h, uint8_t hash_split, uint8_t seed)
+decode_ipv6_n_hash(const struct ip6_hdr *ipv6h, uint8_t hash_split, uint8_t seed)
 {
 	uint32_t saddr, daddr;
 	uint32_t rc = 0;
@@ -211,19 +228,19 @@ decode_ipv6_n_hash(struct ip6_hdr *ipv6h, uint8_t hash_split, uint8_t seed)
 				 ntohs(0xFFFD) + seed,
 				 ntohs(0xFFFE) + seed);
 	} else {
-		struct tcphdr *tcph = NULL;
-		struct udphdr *udph = NULL;
+		const struct tcphdr *tcph = NULL;
+		const struct udphdr *udph = NULL;
 
-		switch(ntohs(ipv6h->ip6_ctlun.ip6_un1.ip6_un1_nxt)) {
+		switch(ipv6h->ip6_ctlun.ip6_un1.ip6_un1_nxt) {
 		case IPPROTO_TCP:
-			tcph = (struct tcphdr *)(ipv6h + 1);
+			tcph = (const struct tcphdr *)(ipv6h + 1);
 			rc = sym_hash_fn(ntohl(saddr),
 					 ntohl(daddr),
 					 ntohs(tcph->th_sport) + seed,
 					 ntohs(tcph->th_dport) + seed);
 			break;
 		case IPPROTO_UDP:
-			udph = (struct udphdr *)(ipv6h + 1);
+			udph = (const struct udphdr *)(ipv6h + 1);
 			rc = sym_hash_fn(ntohl(saddr),
 					 ntohl(daddr),
 					 ntohs(udph->uh_sport) + seed,
@@ -231,16 +248,18 @@ decode_ipv6_n_hash(struct ip6_hdr *ipv6h, uint8_t hash_split, uint8_t seed)
 			break;
 		case IPPROTO_IPIP:
 			/* tunneling */
-			rc = decode_ip_n_hash((struct ip *)(ipv6h + 1),
+			rc = decode_ip_n_hash((const struct ip *)(ipv6h + 1),
 					      hash_split, seed);
 			break;
 		case IPPROTO_IPV6:
 			/* tunneling */
-			rc = decode_ipv6_n_hash((struct ip6_hdr *)(ipv6h + 1),
+			rc = decode_ipv6_n_hash((const struct ip6_hdr *)(ipv6h + 1),
 						hash_split, seed);
 			break;
-		case IPPROTO_ICMP:
 		case IPPROTO_GRE:
+			rc = decode_gre_hash((const uint8_t *)(ipv6h + 1), hash_split, seed);
+			break;
+		case IPPROTO_ICMPV6:
 		case IPPROTO_ESP:
 		case IPPROTO_PIM:
 		case IPPROTO_IGMP:
@@ -263,7 +282,7 @@ decode_ipv6_n_hash(struct ip6_hdr *ipv6h, uint8_t hash_split, uint8_t seed)
  *   * (See decode_vlan_n_hash & pkt_hdr_hash functions).
  *    */
 static uint32_t
-decode_others_n_hash(struct ether_header *ethh, uint8_t seed)
+decode_others_n_hash(const struct ether_header *ethh, uint8_t seed)
 {
 	uint32_t saddr, daddr, rc;
 
@@ -288,18 +307,18 @@ decode_others_n_hash(struct ether_header *ethh, uint8_t seed)
  ** Parser + hash function for VLAN packet
  **/
 static inline uint32_t
-decode_vlan_n_hash(struct ether_header *ethh, uint8_t hash_split, uint8_t seed)
+decode_vlan_n_hash(const struct ether_header *ethh, uint8_t hash_split, uint8_t seed)
 {
 	uint32_t rc = 0;
-	struct vlanhdr *vhdr = (struct vlanhdr *)(ethh + 1);
+	const struct vlanhdr *vhdr = (const struct vlanhdr *)(ethh + 1);
 
 	switch (ntohs(vhdr->proto)) {
 	case ETHERTYPE_IP:
-		rc = decode_ip_n_hash((struct ip *)(vhdr + 1),
+		rc = decode_ip_n_hash((const struct ip *)(vhdr + 1),
 				      hash_split, seed);
 		break;
 	case ETHERTYPE_IPV6:
-		rc = decode_ipv6_n_hash((struct ip6_hdr *)(vhdr + 1),
+		rc = decode_ipv6_n_hash((const struct ip6_hdr *)(vhdr + 1),
 					hash_split, seed);
 		break;
 	case ETHERTYPE_ARP:
@@ -310,6 +329,7 @@ decode_vlan_n_hash(struct ether_header *ethh, uint8_t hash_split, uint8_t seed)
 	}
 	return rc;
 }
+
 /*---------------------------------------------------------------------*/
 /**
  ** General parser + hash function...
@@ -317,16 +337,16 @@ decode_vlan_n_hash(struct ether_header *ethh, uint8_t hash_split, uint8_t seed)
 uint32_t
 pkt_hdr_hash(const unsigned char *buffer, uint8_t hash_split, uint8_t seed)
 {
-	int rc = 0;
-	struct ether_header *ethh = (struct ether_header *)buffer;
+	uint32_t rc = 0;
+	const struct ether_header *ethh = (const struct ether_header *)buffer;
 
 	switch (ntohs(ethh->ether_type)) {
 	case ETHERTYPE_IP:
-		rc = decode_ip_n_hash((struct ip *)(ethh + 1),
+		rc = decode_ip_n_hash((const struct ip *)(ethh + 1),
 				      hash_split, seed);
 		break;
 	case ETHERTYPE_IPV6:
-		rc = decode_ipv6_n_hash((struct ip6_hdr *)(ethh + 1),
+		rc = decode_ipv6_n_hash((const struct ip6_hdr *)(ethh + 1),
 					hash_split, seed);
 		break;
 	case ETHERTYPE_VLAN:
@@ -341,5 +361,38 @@ pkt_hdr_hash(const unsigned char *buffer, uint8_t hash_split, uint8_t seed)
 
 	return rc;
 }
+
+/*---------------------------------------------------------------------*/
+/**
+ ** Parser + hash function for the GRE packet
+ **/
+static uint32_t
+decode_gre_hash(const uint8_t *grehdr, uint8_t hash_split, uint8_t seed)
+{
+	uint32_t rc = 0;
+	int len = 4 + 2 * (!!(*grehdr & 1) + /* Checksum */
+			   !!(*grehdr & 2) + /* Routing */
+			   !!(*grehdr & 4) + /* Key */
+			   !!(*grehdr & 8)); /* Sequence Number */
+	uint16_t proto = ntohs(*(const uint16_t *)(const void *)(grehdr + 2));
+
+	switch (proto) {
+	case ETHERTYPE_IP:
+		rc = decode_ip_n_hash((const struct ip *)(grehdr + len),
+				      hash_split, seed);
+		break;
+	case ETHERTYPE_IPV6:
+		rc = decode_ipv6_n_hash((const struct ip6_hdr *)(grehdr + len),
+					hash_split, seed);
+		break;
+	case 0x6558: /* Transparent Ethernet Bridging */
+		rc = pkt_hdr_hash(grehdr + len, hash_split, seed);
+		break;
+	default:
+		/* others */
+		break;
+	}
+	return rc;
+}
 /*---------------------------------------------------------------------*/
 
diff --git a/apps/nmreplay/GNUmakefile b/apps/nmreplay/GNUmakefile
index 72a75c9e5..0c2fc7839 100644
--- a/apps/nmreplay/GNUmakefile
+++ b/apps/nmreplay/GNUmakefile
@@ -11,10 +11,11 @@ VPATH = $(SRCDIR)/apps/nmreplay
 NO_MAN=
 CFLAGS = -O2 # -pipe -g
 CFLAGS += -Werror -Wall -Wunused-function
-CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include
+CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include -I $(SRCDIR)/libnetmap
 CFLAGS += -Wextra
 
-LDLIBS += -lpthread
+LDFLAGS += -L $(BUILDDIR)/build-libnetmap
+LDLIBS += -lnetmap -lpthread
 ifeq ($(shell uname),Linux)
 	LDLIBS += -lrt	# on linux
 endif
diff --git a/apps/nmreplay/nmreplay.8 b/apps/nmreplay/nmreplay.8
index 8e5ddb969..5307ec6f7 100644
--- a/apps/nmreplay/nmreplay.8
+++ b/apps/nmreplay/nmreplay.8
@@ -22,10 +22,8 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD$
-.\"
-.Dd February 16, 2016
-.Dt NMREPLAY 1
+.Dd December 21, 2018
+.Dt NMREPLAY 8
 .Os
 .Sh NAME
 .Nm nmreplay
@@ -43,6 +41,8 @@
 .Op Fl w Ar wait-link
 .Op Fl v
 .Op Fl C Ar cpu-placement
+.El
+.Ek
 .Sh DESCRIPTION
 .Nm
 works like
@@ -63,6 +63,9 @@ Command line options are as follows
 Name of the pcap file to replay.
 .It Fl i Ar interface
 Name of the netmap interface to use as output.
+See
+.Xr netmap 4
+for interface name format.
 .It Fl v
 Enable verbose mode
 .It Fl b Ar batch-size
@@ -71,7 +74,7 @@ Maximum batch size to use during transmissions.
 normally transmits packets one at a time, but it may use
 larger batches, up to the value specified with this option,
 when running at high rates.
-.It Fl B Ar bps | Cm constant, Ns Ar bps | Cm ether, Ns Ar bps | Cm real Ns Op , Ns Ar speedup
+.It Fl B Ar bps | Cm constant , Ns Ar bps | Cm ether , Ns Ar bps | Cm real Ns Op , Ns Ar speedup
 Bandwidth to be used for transmission.
 .Ar bps
 is a floating point number optionally follow by a character
@@ -85,11 +88,12 @@ indicates that the ethernet framing (160 bits) and CRC (32 bits)
 will be included in the computation of the packet size.
 .Cm real
 means transmission will occur according to the timestamps
-recorded in the trace. The optional
+recorded in the trace.
+The optional
 .Ar speedup
 multiplier (defaults to 1) indicates how much faster
 or slower than real time the trace should be replayed.
-.It Fl D Ar dt | Cm constant, Ns Ar dt | Cm uniform, Ns Ar dmin,dmax | Cm exp, Ar dmin,davg
+.It Fl D Ar dt | Cm constant , Ns Ar dt | Cm uniform , Ns Ar dmin,dmax | Cm exp , Ar dmin,davg
 Adds additional delay to the packet transmission, whose distribution
 can be constant, uniform or exponential.
 .Ar dt, dmin, dmax, avt
@@ -98,7 +102,7 @@ by a character (s, m, u, n) to indicate seconds, milliseconds,
 microseconds, nanoseconds.
 The delay is added to the transmit time and adjusted so that there is
 never packet reordering.
-.It Fl L Ar x | Cm plr, Ns Ar x | Cm ber, Ns Ar x
+.It Fl L Ar x | Cm plr , Ns Ar x | Cm ber , Ns Ar x
 Simulates packet or bit errors, causing offending packets to be dropped.
 .Ar x
 is a floating point number indicating the packet or bit error rate.
@@ -113,14 +117,7 @@ creates an in-memory schedule with all packets to be transmitted,
 and then launches a separate thread to take care of transmissions
 while the main thread reports statistics every second.
 .Sh SEE ALSO
-.Pa http://info.iet.unipi.it/~luigi/netmap/
-.Pp
-Luigi Rizzo, Revisiting network I/O APIs: the netmap framework,
-Communications of the ACM, 55 (3), pp.45-51, March 2012
-.Pp
-Luigi Rizzo, Giuseppe Lettieri,
-VALE, a switched ethernet for virtual machines,
-ACM CoNEXT'12, December 2012, Nice
+.Xr netmap 4
 .Sh AUTHORS
 .An -nosplit
 .Nm
diff --git a/apps/nmreplay/nmreplay.c b/apps/nmreplay/nmreplay.c
index f22209992..54e5dae1f 100644
--- a/apps/nmreplay/nmreplay.c
+++ b/apps/nmreplay/nmreplay.c
@@ -24,71 +24,68 @@
  */
 
 
-#if 0 /* COMMENT */
-
-This program implements NMREPLAY, a program to replay a pcap file
-enforcing the output rate and possibly random losses and delay
-distributions.
-It is meant to be run from the command line and implemented with a main
-control thread for monitoring, plus a thread to push packets out.
-
-The control thread parses command line arguments, prepares a
-schedule for transmission in a memory buffer and then sits
-in a loop where it periodically reads traffic statistics from
-the other threads and prints them out on the console.
-
-The transmit buffer contains headers and packets. Each header
-includes a timestamp that determines when the packet should be sent out.
-A "consumer" thread cons() reads from the queue and transmits packets
-on the output netmap port when their time has come.
-
-The program does CPU pinning and sets the scheduler and priority
-for the "cons" threads. Externally one should do the
-assignment of other threads (e.g. interrupt handlers) and
-make sure that network interfaces are configured properly.
-
---- Main functions of the program ---
-within each function, q is used as a pointer to the queue holding
-packets and parameters.
-
-pcap_prod()
-
-    reads from the pcap file and prepares packets to transmit.
-    After reading a packet from the pcap file, the following information
-    are extracted which can be used to determine the schedule:
-
-    	q->cur_pkt	points to the buffer containing the packet
-	q->cur_len	packet length, excluding CRC
-	q->cur_caplen	available packet length (may be shorter than cur_len)
-	q->cur_tt	transmission time for the packet, computed from the trace.
-
-    The following functions are then called in sequence:
-
-    q->c_loss (set with the -L command line option) decides
-    	whether the packet should be dropped before even queuing.
-	This is generally useful to emulate random loss.
-	The function is supposed to set q->c_drop = 1 if the
-	packet should be dropped, or leave it to 0 otherwise.
-
-    q->c_bw (set with the -B command line option) is used to
-        enforce the transmit bandwidth. The function must store
-	in q->cur_tt the transmission time (in nanoseconds) of
-	the packet, which is typically proportional to the length
-	of the packet, i.e. q->cur_tt = q->cur_len / 
-	Variants are possible, eg. to account for constant framing
-	bits as on the ethernet, or variable channel acquisition times,
-	etc.
-	This mechanism can also be used to simulate variable queueing
-	delay e.g. due to the presence of cross traffic.
-
-    q->c_delay (set with the -D option) implements delay emulation.
-	The function should set q->cur_delay to the additional
-	delay the packet is subject to. The framework will take care of
-	computing the actual exit time of a packet so that there is no
-	reordering.
-
-
-#endif /* COMMENT */
+/*
+ * This program implements NMREPLAY, a program to replay a pcap file
+ * enforcing the output rate and possibly random losses and delay
+ * distributions.
+ * It is meant to be run from the command line and implemented with a main
+ * control thread for monitoring, plus a thread to push packets out.
+ *
+ * The control thread parses command line arguments, prepares a
+ * schedule for transmission in a memory buffer and then sits
+ * in a loop where it periodically reads traffic statistics from
+ * the other threads and prints them out on the console.
+ *
+ * The transmit buffer contains headers and packets. Each header
+ * includes a timestamp that determines when the packet should be sent out.
+ * A "consumer" thread cons() reads from the queue and transmits packets
+ * on the output netmap port when their time has come.
+ *
+ * The program does CPU pinning and sets the scheduler and priority
+ * for the "cons" threads. Externally one should do the
+ * assignment of other threads (e.g. interrupt handlers) and
+ * make sure that network interfaces are configured properly.
+ *
+ * --- Main functions of the program ---
+ * within each function, q is used as a pointer to the queue holding
+ * packets and parameters.
+ *
+ * pcap_prod()
+ *
+ *	reads from the pcap file and prepares packets to transmit.
+ *	After reading a packet from the pcap file, the following information
+ *	are extracted which can be used to determine the schedule:
+ *
+ *   	q->cur_pkt	points to the buffer containing the packet
+ *	q->cur_len	packet length, excluding CRC
+ *	q->cur_caplen	available packet length (may be shorter than cur_len)
+ *	q->cur_tt	transmission time for the packet, computed from the trace.
+ *
+ *  The following functions are then called in sequence:
+ *
+ *  q->c_loss (set with the -L command line option) decides
+ *	whether the packet should be dropped before even queuing.
+ *	This is generally useful to emulate random loss.
+ *	The function is supposed to set q->c_drop = 1 if the
+ *	packet should be dropped, or leave it to 0 otherwise.
+ *
+ *   q->c_bw (set with the -B command line option) is used to
+ *      enforce the transmit bandwidth. The function must store
+ *	in q->cur_tt the transmission time (in nanoseconds) of
+ *	the packet, which is typically proportional to the length
+ *	of the packet, i.e. q->cur_tt = q->cur_len / 
+ *	Variants are possible, eg. to account for constant framing
+ *	bits as on the ethernet, or variable channel acquisition times,
+ *	etc.
+ *	This mechanism can also be used to simulate variable queueing
+ *	delay e.g. due to the presence of cross traffic.
+ *
+ *   q->c_delay (set with the -D option) implements delay emulation.
+ *	The function should set q->cur_delay to the additional
+ *	delay the packet is subject to. The framework will take care of
+ *	computing the actual exit time of a packet so that there is no
+ *	reordering.
+ */
 
 // debugging macros
 #define NED(_fmt, ...)	do {} while (0)
@@ -107,29 +104,43 @@ pcap_prod()
 #define DDD(_fmt, ...)	ED("--DDD-- " _fmt, ##__VA_ARGS__)
 
 #define _GNU_SOURCE	// for CPU_SET() etc
+#include 
+#include 
+#include 
+#include  /* log, exp etc. */
+#include 
+#ifdef __FreeBSD__
+#include  /* pthread w/ affinity */
+#include  /* cpu_set */
+#endif /* __FreeBSD__ */
+#include 
 #include 
-#define NETMAP_WITH_LIBS
-#include 
+#include 
+#include  /* memcpy */
+#include 
+#include 
+#include 
 #include 
-
+#include  // setpriority
+#include 
+#include 
 
 /*
  *
-A packet in the queue is q_pkt plus the payload.
-
-For the packet descriptor we need the following:
-
-    -	position of next packet in the queue (can go backwards).
-	We can reduce to 32 bits if we consider alignments,
-	or we just store the length to be added to the current
-	value and assume 0 as a special index.
-    -	actual packet length (16 bits may be ok)
-    -	queue output time, in nanoseconds (64 bits)
-    -	delay line output time, in nanoseconds
-	One of the two can be packed to a 32bit value
-
-A convenient coding uses 32 bytes per packet.
-
+ * A packet in the queue is q_pkt plus the payload.
+ *
+ * For the packet descriptor we need the following:
+ *
+ *  -	position of next packet in the queue (can go backwards).
+ *	We can reduce to 32 bits if we consider alignments,
+ *	or we just store the length to be added to the current
+ *	value and assume 0 as a special index.
+ *  -	actual packet length (16 bits may be ok)
+ *  -	queue output time, in nanoseconds (64 bits)
+ *  -	delay line output time, in nanoseconds
+ *	One of the two can be packed to a 32bit value
+ *
+ * A convenient coding uses 32 bytes per packet.
  */
 
 struct q_pkt {
@@ -244,15 +255,6 @@ static struct nm_pcap_file *readpcap(const char *fn);
 static void destroy_pcap(struct nm_pcap_file *file);
 
 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include  /* memcpy */
-
-#include 
-
 #define NS_SCALE 1000000000UL	/* nanoseconds in 1s */
 
 static void destroy_pcap(struct nm_pcap_file *pf)
@@ -433,22 +435,10 @@ readpcap(const char *fn)
 
 enum my_pcap_mode { PM_NONE, PM_FAST, PM_FIXED, PM_REAL };
 
-int verbose = 0;
+static int verbose = 0;
 
 static int do_abort = 0;
 
-#include 
-#include 
-#include 
-#include 
-
-#include  // setpriority
-
-#ifdef __FreeBSD__
-#include  /* pthread w/ affinity */
-#include  /* cpu_set */
-#endif /* __FreeBSD__ */
-
 #ifdef linux
 #define cpuset_t        cpu_set_t
 #endif
@@ -568,7 +558,7 @@ struct _qs { /* shared queue */
 	struct nm_pcap_file	*pcap;		/* the pcap struct */
 
 	/* parameters for reading from the netmap port */
-	struct nm_desc *src_port;		/* netmap descriptor */
+	struct nmport_d *src_port;		/* netmap descriptor */
 	const char *	prod_ifname;	/* interface name or pcap file */
 	struct netmap_ring *rxring;	/* current ring being handled */
 	uint32_t	si;		/* ring index */
@@ -642,8 +632,8 @@ struct pipe_args {
 	int		cons_core;	/* core for cons() */
 	int		prod_core;	/* core for prod() */
 
-	struct nm_desc *pa;		/* netmap descriptor */
-	struct nm_desc *pb;
+	struct nmport_d *pa;		/* netmap descriptor */
+	struct nmport_d *pb;
 
 	struct _qs	q;
 };
@@ -771,7 +761,7 @@ pcap_prod(void *_pa)
     need = loops * pf->tot_bytes_rounded + sizeof(struct q_pkt);
     q->buf = calloc(1, need);
     if (q->buf == NULL) {
-	D("alloc %ld bytes for queue failed, exiting",(_P64)need);
+	D("alloc %lld bytes for queue failed, exiting",(long long)need);
 	goto fail;
     }
     q->prod_head = q->prod_tail = 0;
@@ -845,7 +835,7 @@ pcap_prod(void *_pa)
     if (q->buf != NULL) {
 	free(q->buf);
     }
-    nm_close(pa->pb);
+    nmport_close(pa->pb);
     return (NULL);
 }
 
@@ -879,6 +869,7 @@ cons(void *_pa)
 	     * add to q->t0 the time for the last packet
 	     */
 	    q->t0 += last_ts;
+	    set_tns_now(&q->cons_now, q->t0);
 	    q->cons_head = 0;	//restart from beginning of the queue
 	    continue;
 	}
@@ -894,9 +885,7 @@ cons(void *_pa)
 	    continue;
 	}
 	/* XXX copy is inefficient but simple */
-	pending++;
-	if (nm_inject(pa->pb, (char *)(p + 1), p->pktlen) == 0 ||
-		pending > q->burst) {
+	if (nmport_inject(pa->pb, (char *)(p + 1), p->pktlen) == 0) {
 	    RD(1, "inject failed len %d now %ld tx %ld h %ld t %ld next %ld",
 		(int)p->pktlen, (u_long)q->cons_now, (u_long)p->pt_tx,
 		(u_long)q->_head, (u_long)q->_tail, (u_long)p->next);
@@ -904,6 +893,12 @@ cons(void *_pa)
 	    pending = 0;
 	    continue;
 	}
+	pending++;
+	if (pending > q->burst) {
+	    ioctl(pa->pb->fd, NIOCTXSYNC, 0);
+	    pending = 0;
+	}
+
 	q->cons_head = p->next;
 	/* drain packets from the queue */
 	q->rx++;
@@ -936,7 +931,7 @@ nmreplay_main(void *_a)
     pcap_prod((void*)a);
     destroy_pcap(q->pcap);
     q->pcap = NULL;
-    a->pb = nm_open(q->cons_ifname, NULL, 0, NULL);
+    a->pb = nmport_open(q->cons_ifname);
     if (a->pb == NULL) {
 	EEE("cannot open netmap on %s", q->cons_ifname);
 	do_abort = 1; // XXX any better way ?
@@ -971,7 +966,7 @@ usage(void)
 {
 	fprintf(stderr,
 	    "usage: nmreplay [-v] [-D delay] [-B {[constant,]bps|ether,bps|real,speedup}] [-L loss]\n"
-	    "\t[-b burst] -i ifa-or-pcap-file -i ifb\n");
+	    "\t[-b burst] -f pcap-file -i \n");
 	exit(1);
 }
 
@@ -985,7 +980,8 @@ usage(void)
 static char **
 split_arg(const char *src, int *_ac)
 {
-    char *my = NULL, **av = NULL, *seps = " \t\r\n,";
+    char *my = NULL, **av = NULL;
+    const char *seps = " \t\r\n,";
     int l, i, ac; /* number of entries */
 
     if (!src)
@@ -1124,15 +1120,15 @@ main(int argc, char **argv)
 
 	/* set default values */
 	for (i = 0; i < N_OPTS; i++) {
-	    struct _qs *q = &bp[i].q;
-
-	    q->burst = 128;
-	    q->c_delay.optarg = "0";
-	    q->c_delay.run = null_run_fn;
-	    q->c_loss.optarg = "0";
-	    q->c_loss.run = null_run_fn;
-	    q->c_bw.optarg = "0";
-	    q->c_bw.run = null_run_fn;
+	    struct _qs *qs = &bp[i].q;
+
+	    qs->burst = 128;
+	    qs->c_delay.optarg = "0";
+	    qs->c_delay.run = null_run_fn;
+	    qs->c_loss.optarg = "0";
+	    qs->c_loss.run = null_run_fn;
+	    qs->c_bw.optarg = "0";
+	    qs->c_bw.run = null_run_fn;
 	}
 
 	// Options:
@@ -1247,10 +1243,10 @@ main(int argc, char **argv)
 
 	/* apply commands */
 	for (i = 0; i < N_OPTS; i++) { /* once per queue */
-		struct _qs *q = &bp[i].q;
-		err += cmd_apply(delay_cfg, d[i], q, &q->c_delay);
-		err += cmd_apply(bw_cfg, b[i], q, &q->c_bw);
-		err += cmd_apply(loss_cfg, l[i], q, &q->c_loss);
+		struct _qs *qs = &bp[i].q;
+		err += cmd_apply(delay_cfg, d[i], qs, &qs->c_delay);
+		err += cmd_apply(bw_cfg, b[i], qs, &qs->c_bw);
+		err += cmd_apply(loss_cfg, l[i], qs, &qs->c_loss);
 	}
 
 	pthread_create(&bp[0].cons_tid, NULL, nmreplay_main, (void*)&bp[0]);
@@ -1261,9 +1257,9 @@ main(int argc, char **argv)
 	    struct _qs *q0 = &bp[0].q;
 
 	    sleep(1);
-	    ED("%ld -> %ld maxq %d round %ld",
-		(_P64)(q0->rx - olda.rx), (_P64)(q0->tx - olda.tx),
-		q0->rx_qmax, (_P64)q0->prod_max_gap
+	    ED("%lld -> %lld maxq %d round %lld",
+		(long long)(q0->rx - olda.rx), (long long)(q0->tx - olda.tx),
+		q0->rx_qmax, (long long)q0->prod_max_gap
 		);
 	    ED("plr nominal %le actual %le",
 		(double)(q0->c_loss.d[0])/(1<<24),
@@ -1275,7 +1271,7 @@ main(int argc, char **argv)
 	D("exiting on abort");
 	sleep(1);
 
-	return (0);
+	return (err);
 }
 
 /* conversion factor for numbers.
@@ -1284,7 +1280,7 @@ main(int argc, char **argv)
  * the final entry has s = NULL.
  */
 struct _sm {	/* string and multiplier */
-	char *s;
+	const char *s;
 	double m;
 };
 
@@ -1368,7 +1364,6 @@ parse_bw(const char *arg)
  * 24 useful random bits.
  */
 
-#include  /* log, exp etc. */
 static inline uint64_t
 my_random24(void)	/* 24 useful bits */
 {
@@ -1529,7 +1524,7 @@ uniform_delay_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
 	dmax = parse_time(av[2]);
 	if (dmin == U_PARSE_ERR || dmax == U_PARSE_ERR || dmin > dmax)
 		return 1;
-	D("dmin %ld dmax %ld", (_P64)dmin, (_P64)dmax);
+	D("dmin %lld dmax %lld", (long long)dmin, (long long)dmax);
 	dst->d[0] = dmin;
 	dst->d[1] = dmax;
 	dst->d[2] = dmax - dmin;
@@ -1592,7 +1587,7 @@ exp_delay_run(struct _qs *q, struct _cfg *arg)
 {
 	uint64_t *t = (uint64_t *)arg->arg;
         q->cur_delay = t[my_random24() & (PTS_D_EXP - 1)];
-	RD(5, "delay %lu", (_P64)q->cur_delay);
+	RD(5, "delay %llu", (unsigned long long)q->cur_delay);
         return 0;
 }
 
diff --git a/apps/pkt-gen/GNUmakefile b/apps/pkt-gen/GNUmakefile
index e65709b81..5a206d821 100644
--- a/apps/pkt-gen/GNUmakefile
+++ b/apps/pkt-gen/GNUmakefile
@@ -11,10 +11,11 @@ VPATH = $(SRCDIR)/apps/pkt-gen
 NO_MAN=
 CFLAGS = -O2 -pipe
 CFLAGS += -Werror -Wall -Wunused-function
-CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include
-CFLAGS += -Wextra
+CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include -I $(SRCDIR)/libnetmap
+CFLAGS += -Wextra -Wno-address-of-packed-member
 
-LDLIBS += -lpthread -lm
+LDFLAGS += -L $(BUILDDIR)/build-libnetmap
+LDLIBS += -lpthread -lm -lnetmap
 ifeq ($(shell uname),Linux)
 	LDLIBS += -lrt	# on linux
 endif
@@ -26,6 +27,7 @@ CFLAGS += -DNO_PCAP
 endif
 
 PREFIX ?= /usr/local
+MAN_PREFIX = $(if $(filter-out /,$(PREFIX)),$(PREFIX),/usr)/share/man
 
 all: $(PROGS)
 
@@ -37,6 +39,7 @@ install: $(PROGS:%=install-%)
 
 install-%:
 	install -D $* $(DESTDIR)/$(PREFIX)/bin/$*
+	-install -D -m 644 $(SRCDIR)/apps/pkt-gen/pkt-gen.8 $(DESTDIR)/$(MAN_PREFIX)/man8/pkt-gen.8
 
 pkt-gen-b: pkt-gen-b.o
 
diff --git a/apps/pkt-gen/pkt-gen.8 b/apps/pkt-gen/pkt-gen.8
new file mode 100644
index 000000000..83643d749
--- /dev/null
+++ b/apps/pkt-gen/pkt-gen.8
@@ -0,0 +1,297 @@
+.\" Copyright (c) 2016, George V. Neville-Neil
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\"    this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd April 21, 2023
+.Dt PKT-GEN 8
+.Os
+.Sh NAME
+.Nm pkt-gen
+.Nd Packet generator for use with
+.Xr netmap 4
+.Sh SYNOPSIS
+.Bl -item -compact
+.It
+.Nm
+.Op Fl h46XzZNIWvrAB
+.Op Fl i Ar interface
+.Op Fl f Ar function
+.Op Fl n Ar count
+.Op Fl l Ar pkt_size
+.Op Fl b Ar burst_size
+.Op Fl d Ar dst_ip[:port[-dst_ip:port]]
+.Op Fl s Ar src_ip[:port[-src_ip:port]]
+.Op Fl D Ar dst_mac
+.Op Fl S Ar src_mac
+.Op Fl a Ar cpu_id
+.Op Fl c Ar cpus
+.Op Fl p Ar threads
+.Op Fl T Ar report_ms
+.Op Fl P Ar file
+.Op Fl w Ar wait_for_link_time
+.Op Fl R Ar rate
+.Op Fl H Ar len
+.Op Fl F Ar num_frags
+.Op Fl M Ar frag_size
+.Op Fl C Ar port_config
+.El
+.Sh DESCRIPTION
+.Nm
+leverages
+.Xr netmap 4
+to generate and receive raw network packets in batches.
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl h
+Show program usage and exit.
+.It Fl i Ar interface
+Name of the network interface that
+.Nm
+operates on.
+It can be a system network interface (e.g., em0),
+the name of a
+.Xr vale 4
+port (e.g., valeSSS:PPP), the name of a netmap pipe or monitor,
+or any valid netmap port name accepted by the
+.Ar nm_open
+library function, as documented in
+.Xr netmap 4
+(NIOCREGIF section).
+.It Fl f Ar function
+The function to be executed by
+.Nm .
+Specify
+.Cm tx
+for transmission,
+.Cm rx
+for reception,
+.Cm ping
+for client-side ping-pong operation, and
+.Cm pong
+for server-side ping-pong operation.
+.It Fl n Ar count
+Number of iterations of the
+.Nm
+function (with 0 meaning infinite).
+In case of
+.Cm tx
+or
+.Cm rx ,
+.Ar count
+is the number of packets to receive or transmit.
+In case of
+.Cm ping
+or
+.Cm pong ,
+.Ar count
+is the number of ping-pong transactions.
+.It Fl l Ar pkt_size
+Packet size in bytes excluding CRC.
+If passed a second time, use random sizes larger or equal than the
+second one and lower than the first one.
+.It Fl b Ar burst_size
+Transmit or receive up to
+.Ar burst_size
+packets at a time.
+.It Fl 4
+Use IPv4 addresses.
+.It Fl 6
+Use IPv6 addresses.
+.It Fl d Ar dst_ip[:port[-dst_ip:port]]
+Destination IPv4/IPv6 address and port, single or range.
+.It Fl s Ar src_ip[:port[-src_ip:port]]
+Source IPv4/IPv6 address and port, single or range.
+.It Fl D Ar dst_mac
+Destination MAC address in colon notation (e.g., aa:bb:cc:dd:ee:00).
+.It Fl S Ar src_mac
+Source MAC address in colon notation.
+.It Fl a Ar cpu_id
+Pin the first thread of
+.Nm
+to a particular CPU using
+.Xr pthread_setaffinity_np 3 .
+If more threads are used, they are pinned to the subsequent CPUs,
+one per thread.
+.It Fl c Ar cpus
+Maximum number of CPUs to use (0 means to use all the available ones).
+.It Fl p Ar threads
+Number of threads to use.
+By default, only a single thread is used
+to handle all the netmap rings.
+If
+.Ar threads
+is larger than one, each thread handles a single TX ring (in
+.Cm tx
+mode), a single RX ring (in
+.Cm rx
+mode), or a TX/RX ring pair.
+The number of
+.Ar threads
+must be less than or equal to the number of TX (or RX) rings available
+in the device specified by
+.Ar interface .
+.It Fl T Ar report_ms
+Number of milliseconds between reports.
+.It Fl w Ar wait_for_link_time
+Number of seconds to wait before starting the
+.Nm
+function, useful to make sure that the network link is up.
+A network device driver may take some time to enter netmap mode, or
+to create a new transmit/receive ring pair when
+.Xr netmap 4
+requests one.
+.It Fl R Ar rate
+Packet transmission rate.
+Not setting the packet transmission rate tells
+.Nm
+to transmit packets as quickly as possible.
+On servers from 2010 onward
+.Xr netmap 4
+is able to completely use all of the bandwidth of a 10 or 40Gbps link,
+so this option should be used unless your intention is to saturate the link.
+.It Fl X
+Dump payload of each packet transmitted or received.
+.It Fl H Ar len
+Add empty virtio-net-header with size
+.Ar len .
+Valid sizes are 0, 10 and 12.
+This option is only used with Virtual Machine technologies that use virtio
+as a network interface.
+.It Fl P Ar file
+Load the packet to be transmitted from a pcap file rather than constructing
+it within
+.Nm .
+.It Fl z
+Use random IPv4/IPv6 src address/port.
+.It Fl Z
+Use random IPv4/IPv6 dst address/port.
+.It Fl N
+Do not normalize units (i.e., use bps, pps instead of Mbps, Kpps, etc.).
+.It Fl F Ar num_frags
+Send multi-slot packets, each one with
+.Ar num_frags
+fragments.
+A multi-slot packet is represented by two or more consecutive netmap slots
+with the
+.Ar NS_MOREFRAG
+flag set (except for the last slot).
+This is useful to transmit or receive packets larger than the netmap
+buffer size.
+.It Fl M Ar frag_size
+In multi-slot mode,
+.Ar frag_size
+specifies the size of each fragment, if smaller than the packet length
+divided by
+.Ar num_frags .
+.It Fl I
+Use indirect buffers.
+It is only valid for transmitting on VALE ports,
+and it is implemented by setting the
+.Ar NS_INDIRECT
+flag in the netmap slots.
+.It Fl W
+Exit immediately if all the RX rings are empty the first time they are
+examined.
+.It Fl v
+Increase the verbosity level.
+.It Fl r
+In
+.Cm tx
+mode, do not initialize packets, but send whatever the content of
+the uninitialized netmap buffers is (rubbish mode).
+.It Fl A
+Compute mean and standard deviation (over a sliding window) for the
+transmit or receive rate.
+.It Fl B
+Take Ethernet framing and CRC into account when computing the average bps.
+This adds 4 bytes of CRC and 20 bytes of framing to each packet.
+.It Fl C Ar tx_slots Ns Oo Cm \&, Ns Ar rx_slots Ns Oo Cm \&, Ns Ar tx_rings Ns Oo Cm \&, Ns Ar rx_rings Oc Oc Oc
+Configuration in terms of number of rings and slots to be used when
+opening the netmap port.
+Such configuration has an effect on software ports
+created on the fly, such as VALE ports and netmap pipes.
+The configuration may consist of 1 to 4 numbers separated by commas:
+.Dq tx_slots,rx_slots,tx_rings,rx_rings .
+Missing numbers or zeroes stand for default values.
+As an additional convenience, if exactly one number is specified,
+then this is assigned to both
+.Ar tx_slots
+and
+.Ar rx_slots .
+If there is no fourth number, then the third one is assigned to both
+.Ar tx_rings
+and
+.Ar rx_rings .
+.El
+.Pp
+.Nm
+is a raw packet generator that can utilize either
+.Xr netmap 4
+or
+.Xr bpf 4
+but which is most often used with
+.Xr netmap 4 .
+The
+.Ar interface name
+used depends upon how the underlying Ethernet driver exposes its
+transmit and receive rings to
+.Xr netmap 4 .
+Most modern network interfaces that support 10Gbps and higher speeds
+have several transmit and receive rings that are used by the operating
+system to balance traffic across the interface.
+.Nm
+can peel off one or more of the transmit or receive rings for its own
+use without interfering with packets that might otherwise be destined
+for the host.
+For example on a system with a Chelsio Network
+Interface Card (NIC) the interface specification of
+.Ar -i netmap:ncxl0
+gives
+.Nm
+access to a pair of transmit and receive rings that are separate from
+the more commonly known cxl0 interface, which is used by the operating
+system's TCP/IP stack.
+.Sh EXAMPLES
+Capture and count all packets arriving on the operating system's cxl0
+interface.
+Using this will block packets from reaching the operating
+system's network stack.
+.Bd -literal -offset indent
+pkt-gen -i cxl0 -f rx
+.Ed
+.Pp
+Send a stream of fake DNS packets between two hosts with a packet
+length of 128 bytes.
+You must set the destination MAC address for
+packets to be received by the target host.
+.Bd -literal -offset indent
+pkt-gen -i netmap:ncxl0 -f tx -s 172.16.0.1:53 -d 172.16.1.3:53 \e
+-D 00:07:43:29:2a:e0
+.Ed
+.Sh SEE ALSO
+.Xr netmap 4 ,
+.Xr bridge 8
+.Sh AUTHORS
+This manual page was written by
+.An George V. Neville-Neil Aq gnn@FreeBSD.org .
diff --git a/apps/pkt-gen/pkt-gen.c b/apps/pkt-gen/pkt-gen.c
index 725f831bb..c17579a14 100644
--- a/apps/pkt-gen/pkt-gen.c
+++ b/apps/pkt-gen/pkt-gen.c
@@ -25,7 +25,6 @@
  */
 
 /*
- * $FreeBSD$
  * $Id: pkt-gen.c 12346 2013-06-12 17:36:25Z luigi $
  *
  * Example program to show how to build a multithreaded packet
@@ -38,51 +37,54 @@
  */
 
 #define _GNU_SOURCE	/* for CPU_SET() */
-#include 
-#define NETMAP_WITH_LIBS
-#include 
-
-
-#include 	// isprint()
-#include 	// sysconf()
-#include 
 #include 	/* ntohs */
-#ifndef _WIN32
-#include 	/* sysctl */
-#endif
+#include 
+#include 	// isprint()
+#include 
+#include 
 #include 	/* getifaddrs */
+#include 
+#include 
 #include 
 #include 
 #include 
-#include 
 #include 
+#include 
+#ifndef NO_PCAP
+#include 
+#endif
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#if !defined(_WIN32) && !defined(linux)
+#include 	/* sysctl */
+#endif
+#include 
+#include 	// sysconf()
 #ifdef linux
 #define IPV6_VERSION	0x60
 #define IPV6_DEFHLIM	64
 #endif
-#include 
-#include 
-
-#include 
-
-#ifndef NO_PCAP
-#include 
-#endif
 
 #include "ctrs.h"
 
-static void usage(void);
+static void usage(int);
 
 #ifdef _WIN32
 #define cpuset_t        DWORD_PTR   //uint64_t
 static inline void CPU_ZERO(cpuset_t *p)
 {
-        *p = 0;
+	*p = 0;
 }
 
 static inline void CPU_SET(uint32_t i, cpuset_t *p)
 {
-        *p |= 1<< (i & 0x3f);
+	*p |= 1<< (i & 0x3f);
 }
 
 #define pthread_setaffinity_np(a, b, c) !SetThreadAffinityMask(a, *c)    //((void)a, 0)
@@ -162,12 +164,12 @@ ether_ntoa(const struct ether_addr *n)
 #define cpuset_t        uint64_t        // XXX
 static inline void CPU_ZERO(cpuset_t *p)
 {
-        *p = 0;
+	*p = 0;
 }
 
 static inline void CPU_SET(uint32_t i, cpuset_t *p)
 {
-        *p |= 1<< (i & 0x3f);
+	*p |= 1<< (i & 0x3f);
 }
 
 #define pthread_setaffinity_np(a, b, c) ((void)a, 0)
@@ -176,19 +178,17 @@ static inline void CPU_SET(uint32_t i, cpuset_t *p)
 #define IFF_PPROMISC   IFF_PROMISC
 #include   /* LLADDR */
 #define clock_gettime(a,b)      \
-        do {struct timespec t0 = {0,0}; *(b) = t0; } while (0)
+	do {struct timespec t0 = {0,0}; *(b) = t0; } while (0)
 #endif  /* __APPLE__ */
 
-const char *default_payload="netmap pkt-gen DIRECT payload\n"
+static const char *default_payload = "netmap pkt-gen DIRECT payload\n"
 	"http://info.iet.unipi.it/~luigi/netmap/ ";
 
-const char *indirect_payload="netmap pkt-gen indirect payload\n"
+static const char *indirect_payload = "netmap pkt-gen indirect payload\n"
 	"http://info.iet.unipi.it/~luigi/netmap/ ";
 
-int verbose = 0;
-
-#define SKIP_PAYLOAD 1 /* do not check payload. XXX unused */
-
+static int verbose = 0;
+static int normalize = 1;
 
 #define VIRT_HDR_1	10	/* length of a base vnet-hdr */
 #define VIRT_HDR_2	12	/* length of the extenede vnet-hdr */
@@ -197,7 +197,7 @@ struct virt_header {
 	uint8_t fields[VIRT_HDR_MAX];
 };
 
-#define MAX_BODYSIZE	16384
+#define MAX_BODYSIZE	65536
 
 struct pkt {
 	struct virt_header vh;
@@ -206,12 +206,12 @@ struct pkt {
 		struct {
 			struct ip ip;
 			struct udphdr udp;
-			uint8_t body[MAX_BODYSIZE];	// XXX hardwired
+			uint8_t body[MAX_BODYSIZE];	/* hardwired */
 		} ipv4;
 		struct {
 			struct ip6_hdr ip;
 			struct udphdr udp;
-			uint8_t body[MAX_BODYSIZE];	// XXX hardwired
+			uint8_t body[MAX_BODYSIZE];	/* hardwired */
 		} ipv6;
 	};
 } __attribute__((__packed__));
@@ -220,7 +220,7 @@ struct pkt {
     ((af) == AF_INET ? (p)->ipv4.f: (p)->ipv6.f)
 
 struct ip_range {
-	char *name;
+	const char *name;
 	union {
 		struct {
 			uint32_t start, end; /* same as struct in_addr */
@@ -234,12 +234,12 @@ struct ip_range {
 };
 
 struct mac_range {
-	char *name;
+	const char *name;
 	struct ether_addr start, end;
 };
 
 /* ifname can be netmap:foo-xxxx */
-#define MAX_IFNAMELEN	64	/* our buffer for ifname */
+#define MAX_IFNAMELEN	512	/* our buffer for ifname */
 //#define MAX_PKTSIZE	1536
 #define MAX_PKTSIZE	MAX_BODYSIZE	/* XXX: + IP_HDR + ETH_HDR */
 
@@ -260,10 +260,12 @@ struct glob_arg {
 	struct mac_range dst_mac;
 	struct mac_range src_mac;
 	int pkt_size;
+	int pkt_min_size;
 	int burst;
 	int forever;
 	uint64_t npackets;	/* total packets to send */
-	int frags;	/* fragments per packet */
+	int frags;		/* fragments per packet */
+	u_int frag_size;	/* size of each fragment */
 	int nthreads;
 	int cpus;	/* cpus used for running */
 	int system_cpus;	/* cpus on the system */
@@ -276,10 +278,11 @@ struct glob_arg {
 #define OPT_TS		16	/* add a timestamp */
 #define OPT_INDIRECT	32	/* use indirect buffers, tx only */
 #define OPT_DUMP	64	/* dump rx/tx traffic */
-#define OPT_RUBBISH	256	/* send wathever the buffers contain */
+#define OPT_RUBBISH	256	/* send whatever the buffers contain */
 #define OPT_RANDOM_SRC  512
 #define OPT_RANDOM_DST  1024
 #define OPT_PPS_STATS   2048
+#define OPT_UPDATE_CSUM 4096
 	int dev_type;
 #ifndef NO_PCAP
 	pcap_t *p;
@@ -290,25 +293,30 @@ struct glob_arg {
 
 	int affinity;
 	int main_fd;
-	struct nm_desc *nmd;
+	struct nmport_d *nmd;
+	uint32_t orig_mode;
 	int report_interval;		/* milliseconds between prints */
 	void *(*td_body)(void *);
 	int td_type;
 	void *mmap_addr;
 	char ifname[MAX_IFNAMELEN];
-	char *nmr_config;
+	const char *nmr_config;
 	int dummy_send;
 	int virt_header;	/* send also the virt_header */
-	int extra_bufs;		/* goes in nr_arg3 */
-	int extra_pipes;	/* goes in nr_arg1 */
 	char *packet_file;	/* -P option */
 #define	STATS_WIN	15
 	int win_idx;
 	int64_t win[STATS_WIN];
 	int wait_link;
+	int framing;		/* #bits of framing (for bw output) */
 };
 enum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP };
 
+enum {
+	TD_TYPE_SENDER = 1,
+	TD_TYPE_RECEIVER,
+	TD_TYPE_OTHER,
+};
 
 /*
  * Arguments for a new thread. The same structure is used by
@@ -320,7 +328,7 @@ struct targ {
 	int completed;
 	int cancel;
 	int fd;
-	struct nm_desc *nmd;
+	struct nmport_d *nmd;
 	/* these ought to be volatile, but they are
 	 * only sampled and errors should not accumulate
 	 */
@@ -333,6 +341,9 @@ struct targ {
 
 	struct pkt pkt;
 	void *frame;
+	uint16_t seed[3];
+	u_int frags;
+	u_int frag_size;
 };
 
 static __inline uint16_t
@@ -411,7 +422,7 @@ extract_ip_range(struct ip_range *r, int af)
 	name = strdup(r->name);
 	if (name == NULL) {
 		D("strdup failed");
-		usage();
+		usage(-1);
 	}
 	/* the first - splits start/end of range */
 	ap = strchr(name, '-');
@@ -507,6 +518,48 @@ extract_mac_range(struct mac_range *r)
 	return 0;
 }
 
+static int
+get_if_mtu(const struct glob_arg *g)
+{
+	struct ifreq ifreq;
+	int s, ret;
+	const char *ifname = g->nmd->hdr.nr_name;
+	size_t len;
+
+	if (!strncmp(g->ifname, "netmap:", 7) && !strchr(ifname, '{')
+			&& !strchr(ifname, '}')) {
+
+		len = strlen(ifname);
+
+		if (len > IFNAMSIZ) {
+			D("'%s' too long, cannot ask for MTU", ifname);
+			return -1;
+		}
+
+		s = socket(AF_INET, SOCK_DGRAM, 0);
+		if (s < 0) {
+			D("socket() failed: %s", strerror(errno));
+			return s;
+		}
+
+		memset(&ifreq, 0, sizeof(ifreq));
+		memcpy(ifreq.ifr_name, ifname, len);
+
+		ret = ioctl(s, SIOCGIFMTU, &ifreq);
+		if (ret) {
+			D("ioctl(SIOCGIFMTU) failed: %s", strerror(errno));
+		}
+
+		close(s);
+
+		return ifreq.ifr_mtu;
+	}
+
+	/* This is a pipe or a VALE port, where the MTU is very large,
+	 * so we use some practical limit. */
+	return 65536;
+}
+
 static struct targ *targs;
 static int global_nthreads;
 
@@ -570,7 +623,7 @@ system_ncpus(void)
 /*
  * parse the vale configuration in conf and put it in nmr.
  * Return the flag set if necessary.
- * The configuration may consist of 0 to 4 numbers separated
+ * The configuration may consist of 1 to 4 numbers separated
  * by commas: #tx-slots,#rx-slots,#tx-rings,#rx-rings.
  * Missing numbers or zeroes stand for default values.
  * As an additional convenience, if exactly one number
@@ -578,16 +631,16 @@ system_ncpus(void)
  * If there is no 4th number, then the 3rd is assigned to both #tx-rings
  * and #rx-rings.
  */
-int
-parse_nmr_config(const char* conf, struct nmreq *nmr)
+static int
+parse_nmr_config(const char* conf, struct nmreq_register *nmr)
 {
 	char *w, *tok;
 	int i, v;
 
-	nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
-	nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
 	if (conf == NULL || ! *conf)
 		return 0;
+	nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
+	nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
 	w = strdup(conf);
 	for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
 		v = atoi(tok);
@@ -613,9 +666,7 @@ parse_nmr_config(const char* conf, struct nmreq *nmr)
 			nmr->nr_tx_rings, nmr->nr_tx_slots,
 			nmr->nr_rx_rings, nmr->nr_rx_slots);
 	free(w);
-	return (nmr->nr_tx_rings || nmr->nr_tx_slots ||
-                        nmr->nr_rx_rings || nmr->nr_rx_slots) ?
-		NM_OPEN_RING_CFG : 0;
+	return 0;
 }
 
 
@@ -633,6 +684,10 @@ source_hwaddr(const char *ifname, char *buf)
 		return (-1);
 	}
 
+	/* remove 'netmap:' prefix before comparing interfaces */
+	if (!strncmp(ifname, "netmap:", 7))
+		ifname = &ifname[7];
+
 	for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
 		struct sockaddr_dl *sdl =
 			(struct sockaddr_dl *)ifap->ifa_addr;
@@ -680,15 +735,15 @@ setaffinity(pthread_t me, int i)
 static uint32_t
 checksum(const void *data, uint16_t len, uint32_t sum)
 {
-        const uint8_t *addr = data;
+	const uint8_t *addr = data;
 	uint32_t i;
 
-        /* Checksum all the pairs of bytes first... */
-        for (i = 0; i < (len & ~1U); i += 2) {
-                sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i)));
-                if (sum > 0xFFFF)
-                        sum -= 0xFFFF;
-        }
+	/* Checksum all the pairs of bytes first... */
+	for (i = 0; i < (len & ~1U); i += 2) {
+		sum += (uint16_t)ntohs(*((const uint16_t *)(addr + i)));
+		if (sum > 0xFFFF)
+			sum -= 0xFFFF;
+	}
 	/*
 	 * If there's a single byte left over, checksum it, too.
 	 * Network byte order is big-endian, so the remaining byte is
@@ -726,7 +781,7 @@ dump_payload(const char *_p, int len, struct netmap_ring *ring, int cur)
 		ring->slot[cur].flags, len);
 	/* hexdump routine */
 	for (i = 0; i < len; ) {
-		memset(buf, sizeof(buf), ' ');
+		memset(buf, ' ', sizeof(buf));
 		sprintf(buf, "%5d: ", i);
 		i0 = i;
 		for (j=0; j < 16 && i < len; i++, j++)
@@ -751,133 +806,150 @@ dump_payload(const char *_p, int len, struct netmap_ring *ring, int cur)
 #define uh_sum check
 #endif /* linux */
 
+static uint16_t
+new_ip_sum(uint16_t ip_sum, uint32_t oaddr, uint32_t naddr)
+{
+	ip_sum = cksum_add(ip_sum, ~oaddr >> 16);
+	ip_sum = cksum_add(ip_sum, ~oaddr & 0xffff);
+	ip_sum = cksum_add(ip_sum, naddr >> 16);
+	ip_sum = cksum_add(ip_sum, naddr & 0xffff);
+	return ip_sum;
+}
+
+static uint16_t
+new_udp_sum(uint16_t udp_sum, uint16_t oport, uint16_t nport)
+{
+	udp_sum = cksum_add(udp_sum, ~oport);
+	udp_sum = cksum_add(udp_sum, nport);
+	return udp_sum;
+}
+
+
 static void
-update_ip(struct pkt *pkt, struct glob_arg *g)
+update_ip(struct pkt *pkt, struct targ *t)
 {
-	struct ip *ip;
-	struct udphdr *udp;
+	struct glob_arg *g = t->g;
+	struct ip ip;
+	struct udphdr udp;
 	uint32_t oaddr, naddr;
 	uint16_t oport, nport;
-	uint16_t ip_sum, udp_sum;
+	uint16_t ip_sum = 0, udp_sum = 0;
 
-	ip = &pkt->ipv4.ip;
-	udp = &pkt->ipv4.udp;
+	memcpy(&ip, &pkt->ipv4.ip, sizeof(ip));
+	memcpy(&udp, &pkt->ipv4.udp, sizeof(udp));
 	do {
 		ip_sum = udp_sum = 0;
-		naddr = oaddr = ntohl(ip->ip_src.s_addr);
-		nport = oport = ntohs(udp->uh_sport);
+		naddr = oaddr = ntohl(ip.ip_src.s_addr);
+		nport = oport = ntohs(udp.uh_sport);
 		if (g->options & OPT_RANDOM_SRC) {
-			naddr = ip->ip_src.s_addr = random();
-			nport = udp->uh_sport = random();
-			break;
-		}
-		if (oport < g->src_ip.port1) {
-			nport = oport + 1;
-			udp->uh_sport = htons(nport);
-			break;
-		}
-		nport = g->src_ip.port0;
-		udp->uh_sport = htons(nport);
-		if (oaddr < g->src_ip.ipv4.end) {
-			naddr = oaddr + 1;
-			ip->ip_src.s_addr = htonl(naddr);
-			break;
-		}
-		naddr = g->src_ip.ipv4.start;
-		ip->ip_src.s_addr = htonl(naddr);
-
-		/* update checksums if needed */
-		if (oaddr != naddr) {
-			ip_sum = cksum_add(ip_sum, ~oaddr >> 16);
-			ip_sum = cksum_add(ip_sum, ~oaddr & 0xffff);
-			ip_sum = cksum_add(ip_sum, naddr >> 16);
-			ip_sum = cksum_add(ip_sum, naddr & 0xffff);
-		}
-		if (oport != nport) {
-			udp_sum = cksum_add(udp_sum, ~oport);
-			udp_sum = cksum_add(udp_sum, nport);
+			ip.ip_src.s_addr = nrand48(t->seed);
+			udp.uh_sport = nrand48(t->seed);
+			naddr = ntohl(ip.ip_src.s_addr);
+			nport = ntohs(udp.uh_sport);
+			ip_sum = new_ip_sum(ip_sum, oaddr, naddr);
+			udp_sum = new_udp_sum(udp_sum, oport, nport);
+		} else {
+			if (oport < g->src_ip.port1) {
+				nport = oport + 1;
+				udp.uh_sport = htons(nport);
+				udp_sum = new_udp_sum(udp_sum, oport, nport);
+				break;
+			}
+			nport = g->src_ip.port0;
+			udp.uh_sport = htons(nport);
+			if (oaddr < g->src_ip.ipv4.end) {
+				naddr = oaddr + 1;
+				ip.ip_src.s_addr = htonl(naddr);
+				ip_sum = new_ip_sum(ip_sum, oaddr, naddr);
+				break;
+			}
+			naddr = g->src_ip.ipv4.start;
+			ip.ip_src.s_addr = htonl(naddr);
+			ip_sum = new_ip_sum(ip_sum, oaddr, naddr);
 		}
 
-		naddr = oaddr = ntohl(ip->ip_dst.s_addr);
-		nport = oport = ntohs(udp->uh_dport);
+		naddr = oaddr = ntohl(ip.ip_dst.s_addr);
+		nport = oport = ntohs(udp.uh_dport);
 		if (g->options & OPT_RANDOM_DST) {
-			naddr = ip->ip_dst.s_addr = random();
-			nport = udp->uh_dport = random();
-			break;
-		}
-		if (oport < g->dst_ip.port1) {
-			nport = oport + 1;
-			udp->uh_dport = htons(nport);
-			break;
-		}
-		nport = g->dst_ip.port0;
-		udp->uh_dport = htons(nport);
-		if (oaddr < g->dst_ip.ipv4.end) {
-			naddr = oaddr + 1;
-			ip->ip_dst.s_addr = htonl(naddr);
-			break;
+			ip.ip_dst.s_addr = nrand48(t->seed);
+			udp.uh_dport = nrand48(t->seed);
+			naddr = ntohl(ip.ip_dst.s_addr);
+			nport = ntohs(udp.uh_dport);
+			ip_sum = new_ip_sum(ip_sum, oaddr, naddr);
+			udp_sum = new_udp_sum(udp_sum, oport, nport);
+		} else {
+			if (oport < g->dst_ip.port1) {
+				nport = oport + 1;
+				udp.uh_dport = htons(nport);
+				udp_sum = new_udp_sum(udp_sum, oport, nport);
+				break;
+			}
+			nport = g->dst_ip.port0;
+			udp.uh_dport = htons(nport);
+			if (oaddr < g->dst_ip.ipv4.end) {
+				naddr = oaddr + 1;
+				ip.ip_dst.s_addr = htonl(naddr);
+				ip_sum = new_ip_sum(ip_sum, oaddr, naddr);
+				break;
+			}
+			naddr = g->dst_ip.ipv4.start;
+			ip.ip_dst.s_addr = htonl(naddr);
+			ip_sum = new_ip_sum(ip_sum, oaddr, naddr);
 		}
-		naddr = g->dst_ip.ipv4.start;
-		ip->ip_dst.s_addr = htonl(naddr);
 	} while (0);
 	/* update checksums */
-	if (oaddr != naddr) {
-		ip_sum = cksum_add(ip_sum, ~oaddr >> 16);
-		ip_sum = cksum_add(ip_sum, ~oaddr & 0xffff);
-		ip_sum = cksum_add(ip_sum, naddr >> 16);
-		ip_sum = cksum_add(ip_sum, naddr & 0xffff);
-	}
-	if (oport != nport) {
-		udp_sum = cksum_add(udp_sum, ~oport);
-		udp_sum = cksum_add(udp_sum, nport);
-	}
 	if (udp_sum != 0)
-		udp->uh_sum = cksum_add(udp->uh_sum, ~htons(udp_sum));
+		udp.uh_sum = ~cksum_add(~udp.uh_sum, htons(udp_sum));
 	if (ip_sum != 0) {
-		ip->ip_sum = cksum_add(ip->ip_sum, ~htons(ip_sum));
-		udp->uh_sum = cksum_add(udp->uh_sum, ~htons(ip_sum));
+		ip.ip_sum = ~cksum_add(~ip.ip_sum, htons(ip_sum));
+		udp.uh_sum = ~cksum_add(~udp.uh_sum, htons(ip_sum));
 	}
+	memcpy(&pkt->ipv4.ip, &ip, sizeof(ip));
+	memcpy(&pkt->ipv4.udp, &udp, sizeof(udp));
 }
 
 #ifndef s6_addr16
 #define	s6_addr16	__u6_addr.__u6_addr16
 #endif
 static void
-update_ip6(struct pkt *pkt, struct glob_arg *g)
+update_ip6(struct pkt *pkt, struct targ *t)
 {
-	struct ip6_hdr *ip6;
-	struct udphdr *udp;
+	struct glob_arg *g = t->g;
+	struct ip6_hdr ip6;
+	struct udphdr udp;
 	uint16_t udp_sum;
 	uint16_t oaddr, naddr;
 	uint16_t oport, nport;
 	uint8_t group;
 
-	ip6 = &pkt->ipv6.ip;
-	udp = &pkt->ipv6.udp;
+	memcpy(&ip6, &pkt->ipv6.ip, sizeof(ip6));
+	memcpy(&udp, &pkt->ipv6.udp, sizeof(udp));
 	do {
 		udp_sum = 0;
 		group = g->src_ip.ipv6.sgroup;
-		naddr = oaddr = ntohs(ip6->ip6_src.s6_addr16[group]);
-		nport = oport = ntohs(udp->uh_sport);
+		naddr = oaddr = ntohs(ip6.ip6_src.s6_addr16[group]);
+		nport = oport = ntohs(udp.uh_sport);
 		if (g->options & OPT_RANDOM_SRC) {
-			naddr = ip6->ip6_src.s6_addr16[group] = random();
-			nport = udp->uh_sport = random();
+			ip6.ip6_src.s6_addr16[group] = nrand48(t->seed);
+			udp.uh_sport = nrand48(t->seed);
+			naddr = ntohs(ip6.ip6_src.s6_addr16[group]);
+			nport = ntohs(udp.uh_sport);
 			break;
 		}
 		if (oport < g->src_ip.port1) {
 			nport = oport + 1;
-			udp->uh_sport = htons(nport);
+			udp.uh_sport = htons(nport);
 			break;
 		}
 		nport = g->src_ip.port0;
-		udp->uh_sport = htons(nport);
+		udp.uh_sport = htons(nport);
 		if (oaddr < ntohs(g->src_ip.ipv6.end.s6_addr16[group])) {
 			naddr = oaddr + 1;
-			ip6->ip6_src.s6_addr16[group] = htons(naddr);
+			ip6.ip6_src.s6_addr16[group] = htons(naddr);
 			break;
 		}
 		naddr = ntohs(g->src_ip.ipv6.start.s6_addr16[group]);
-		ip6->ip6_src.s6_addr16[group] = htons(naddr);
+		ip6.ip6_src.s6_addr16[group] = htons(naddr);
 
 		/* update checksums if needed */
 		if (oaddr != naddr)
@@ -887,27 +959,29 @@ update_ip6(struct pkt *pkt, struct glob_arg *g)
 			    cksum_add(~oport, nport));
 
 		group = g->dst_ip.ipv6.egroup;
-		naddr = oaddr = ntohs(ip6->ip6_dst.s6_addr16[group]);
-		nport = oport = ntohs(udp->uh_dport);
+		naddr = oaddr = ntohs(ip6.ip6_dst.s6_addr16[group]);
+		nport = oport = ntohs(udp.uh_dport);
 		if (g->options & OPT_RANDOM_DST) {
-			naddr = ip6->ip6_dst.s6_addr16[group] = random();
-			nport = udp->uh_dport = random();
+			ip6.ip6_dst.s6_addr16[group] = nrand48(t->seed);
+			udp.uh_dport = nrand48(t->seed);
+			naddr = ntohs(ip6.ip6_dst.s6_addr16[group]);
+			nport = ntohs(udp.uh_dport);
 			break;
 		}
 		if (oport < g->dst_ip.port1) {
 			nport = oport + 1;
-			udp->uh_dport = htons(nport);
+			udp.uh_dport = htons(nport);
 			break;
 		}
 		nport = g->dst_ip.port0;
-		udp->uh_dport = htons(nport);
+		udp.uh_dport = htons(nport);
 		if (oaddr < ntohs(g->dst_ip.ipv6.end.s6_addr16[group])) {
 			naddr = oaddr + 1;
-			ip6->ip6_dst.s6_addr16[group] = htons(naddr);
+			ip6.ip6_dst.s6_addr16[group] = htons(naddr);
 			break;
 		}
 		naddr = ntohs(g->dst_ip.ipv6.start.s6_addr16[group]);
-		ip6->ip6_dst.s6_addr16[group] = htons(naddr);
+		ip6.ip6_dst.s6_addr16[group] = htons(naddr);
 	} while (0);
 	/* update checksums */
 	if (oaddr != naddr)
@@ -917,18 +991,99 @@ update_ip6(struct pkt *pkt, struct glob_arg *g)
 		udp_sum = cksum_add(udp_sum,
 		    cksum_add(~oport, nport));
 	if (udp_sum != 0)
-		udp->uh_sum = cksum_add(udp->uh_sum, ~htons(udp_sum));
+		udp.uh_sum = ~cksum_add(~udp.uh_sum, udp_sum);
+	memcpy(&pkt->ipv6.ip, &ip6, sizeof(ip6));
+	memcpy(&pkt->ipv6.udp, &udp, sizeof(udp));
 }
 
 static void
-update_addresses(struct pkt *pkt, struct glob_arg *g)
+update_addresses(struct pkt *pkt, struct targ *t)
 {
 
-	if (g->af == AF_INET)
-		update_ip(pkt, g);
+	if (t->g->af == AF_INET)
+		update_ip(pkt, t);
 	else
-		update_ip6(pkt, g);
+		update_ip6(pkt, t);
+}
+
+static void
+update_ip_size(struct pkt *pkt, int size)
+{
+	struct ip ip;
+	struct udphdr udp;
+	uint16_t oiplen, niplen;
+	uint16_t nudplen;
+	uint16_t ip_sum = 0;
+
+	memcpy(&ip, &pkt->ipv4.ip, sizeof(ip));
+	memcpy(&udp, &pkt->ipv4.udp, sizeof(udp));
+
+	oiplen = ntohs(ip.ip_len);
+	niplen = size - sizeof(struct ether_header);
+	ip.ip_len = htons(niplen);
+	nudplen = niplen - sizeof(struct ip);
+	udp.uh_ulen = htons(nudplen);
+	ip_sum = new_udp_sum(ip_sum, oiplen, niplen);
+
+	/* update checksums */
+	if (ip_sum != 0)
+		ip.ip_sum = ~cksum_add(~ip.ip_sum, htons(ip_sum));
+
+	udp.uh_sum = 0;
+	/* Magic: taken from sbin/dhclient/packet.c */
+	udp.uh_sum = wrapsum(
+		checksum(&udp, sizeof(udp),	/* udp header */
+		checksum(pkt->ipv4.body,	/* udp payload */
+		nudplen - sizeof(udp),
+		checksum(&ip.ip_src, /* pseudo header */
+		2 * sizeof(ip.ip_src),
+		IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
+
+	memcpy(&pkt->ipv4.ip, &ip, sizeof(ip));
+	memcpy(&pkt->ipv4.udp, &udp, sizeof(udp));
+}
+
+static void
+update_ip6_size(struct pkt *pkt, int size)
+{
+	struct ip6_hdr ip6;
+	struct udphdr udp;
+	uint16_t niplen, nudplen;
+	uint32_t csum;
+
+	memcpy(&ip6, &pkt->ipv6.ip, sizeof(ip6));
+	memcpy(&udp, &pkt->ipv6.udp, sizeof(udp));
+
+	nudplen = niplen = size - sizeof(struct ether_header) - sizeof(ip6);
+	ip6.ip6_plen = htons(niplen);
+	udp.uh_ulen = htons(nudplen);
+
+	/* Save part of pseudo header checksum into csum */
+	udp.uh_sum = 0;
+	csum = IPPROTO_UDP << 24;
+	csum = checksum(&csum, sizeof(csum), nudplen);
+	udp.uh_sum = wrapsum(
+		checksum(&udp, sizeof(udp),	/* udp header */
+		checksum(pkt->ipv6.body,	/* udp payload */
+		nudplen - sizeof(udp),
+		checksum(&pkt->ipv6.ip.ip6_src, /* pseudo header */
+		2 * sizeof(pkt->ipv6.ip.ip6_src), csum))));
+
+	memcpy(&pkt->ipv6.ip, &ip6, sizeof(ip6));
+	memcpy(&pkt->ipv6.udp, &udp, sizeof(udp));
+}
+
+static void
+update_size(struct pkt *pkt, struct targ *t, int size)
+{
+	if (t->g->options & OPT_UPDATE_CSUM) {
+		if (t->g->af == AF_INET)
+			update_ip_size(pkt, size);
+		else
+			update_ip6_size(pkt, size);
+	}
 }
+
 /*
  * initialize one packet and prepare for the next one.
  * The copy could be done better instead of repeating it each time.
@@ -938,11 +1093,12 @@ initialize_packet(struct targ *targ)
 {
 	struct pkt *pkt = &targ->pkt;
 	struct ether_header *eh;
-	struct ip6_hdr *ip6;
-	struct ip *ip;
-	struct udphdr *udp;
+	struct ip6_hdr ip6;
+	struct ip ip;
+	struct udphdr udp;
+	void *udp_ptr;
 	uint16_t paylen;
-	uint32_t csum;
+	uint32_t csum = 0;
 	const char *payload = targ->g->options & OPT_INDIRECT ?
 		indirect_payload : default_payload;
 	int i, l0 = strlen(payload);
@@ -972,7 +1128,7 @@ initialize_packet(struct targ *targ)
 #endif
 
 	paylen = targ->g->pkt_size - sizeof(*eh) -
-	    (targ->g->af == AF_INET ? sizeof(*ip): sizeof(*ip6));
+	    (targ->g->af == AF_INET ? sizeof(ip): sizeof(ip6));
 
 	/* create a nice NUL-terminated string */
 	for (i = 0; i < paylen; i += l0) {
@@ -989,56 +1145,61 @@ initialize_packet(struct targ *targ)
 
 	if (targ->g->af == AF_INET) {
 		eh->ether_type = htons(ETHERTYPE_IP);
-		ip = &pkt->ipv4.ip;
-		udp = &pkt->ipv4.udp;
-		ip->ip_v = IPVERSION;
-		ip->ip_hl = sizeof(*ip) >> 2;
-		ip->ip_id = 0;
-		ip->ip_tos = IPTOS_LOWDELAY;
-		ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh));
-		ip->ip_id = 0;
-		ip->ip_off = htons(IP_DF); /* Don't fragment */
-		ip->ip_ttl = IPDEFTTL;
-		ip->ip_p = IPPROTO_UDP;
-		ip->ip_dst.s_addr = htonl(targ->g->dst_ip.ipv4.start);
-		ip->ip_src.s_addr = htonl(targ->g->src_ip.ipv4.start);
-		ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0));
+		memcpy(&ip, &pkt->ipv4.ip, sizeof(ip));
+		udp_ptr = &pkt->ipv4.udp;
+		ip.ip_v = IPVERSION;
+		ip.ip_hl = sizeof(ip) >> 2;
+		ip.ip_id = 0;
+		ip.ip_tos = IPTOS_LOWDELAY;
+		ip.ip_len = htons(targ->g->pkt_size - sizeof(*eh));
+		ip.ip_id = 0;
+		ip.ip_off = htons(IP_DF); /* Don't fragment */
+		ip.ip_ttl = IPDEFTTL;
+		ip.ip_p = IPPROTO_UDP;
+		ip.ip_dst.s_addr = htonl(targ->g->dst_ip.ipv4.start);
+		ip.ip_src.s_addr = htonl(targ->g->src_ip.ipv4.start);
+		ip.ip_sum = wrapsum(checksum(&ip, sizeof(ip), 0));
+		memcpy(&pkt->ipv4.ip, &ip, sizeof(ip));
 	} else {
 		eh->ether_type = htons(ETHERTYPE_IPV6);
-		ip6 = &pkt->ipv6.ip;
-		udp = &pkt->ipv6.udp;
-		ip6->ip6_flow = 0;
-		ip6->ip6_plen = htons(paylen);
-		ip6->ip6_vfc = IPV6_VERSION;
-		ip6->ip6_nxt = IPPROTO_UDP;
-		ip6->ip6_hlim = IPV6_DEFHLIM;
-		ip6->ip6_src = targ->g->src_ip.ipv6.start;
-		ip6->ip6_dst = targ->g->dst_ip.ipv6.start;
-	}
-
-	udp->uh_sport = htons(targ->g->src_ip.port0);
-	udp->uh_dport = htons(targ->g->dst_ip.port0);
-	udp->uh_ulen = htons(paylen);
+		memcpy(&ip6, &pkt->ipv4.ip, sizeof(ip6));
+		udp_ptr = &pkt->ipv6.udp;
+		ip6.ip6_flow = 0;
+		ip6.ip6_plen = htons(paylen);
+		ip6.ip6_vfc = IPV6_VERSION;
+		ip6.ip6_nxt = IPPROTO_UDP;
+		ip6.ip6_hlim = IPV6_DEFHLIM;
+		ip6.ip6_src = targ->g->src_ip.ipv6.start;
+		ip6.ip6_dst = targ->g->dst_ip.ipv6.start;
+	}
+	memcpy(&udp, udp_ptr, sizeof(udp));
+
+	udp.uh_sport = htons(targ->g->src_ip.port0);
+	udp.uh_dport = htons(targ->g->dst_ip.port0);
+	udp.uh_ulen = htons(paylen);
 	if (targ->g->af == AF_INET) {
 		/* Magic: taken from sbin/dhclient/packet.c */
-		udp->uh_sum = wrapsum(
-		    checksum(udp, sizeof(*udp),	/* udp header */
-                    checksum(pkt->ipv4.body,	/* udp payload */
-		    paylen - sizeof(*udp),
+		udp.uh_sum = wrapsum(
+		    checksum(&udp, sizeof(udp),	/* udp header */
+		    checksum(pkt->ipv4.body,	/* udp payload */
+		    paylen - sizeof(udp),
 		    checksum(&pkt->ipv4.ip.ip_src, /* pseudo header */
 			2 * sizeof(pkt->ipv4.ip.ip_src),
-			IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)))));
+			IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
+		memcpy(&pkt->ipv4.ip, &ip, sizeof(ip));
 	} else {
 		/* Save part of pseudo header checksum into csum */
 		csum = IPPROTO_UDP << 24;
 		csum = checksum(&csum, sizeof(csum), paylen);
-		udp->uh_sum = wrapsum(
-		    checksum(udp, sizeof(*udp),	/* udp header */
+		udp.uh_sum = wrapsum(
+		    checksum(udp_ptr, sizeof(udp),	/* udp header */
 		    checksum(pkt->ipv6.body,	/* udp payload */
-		    paylen - sizeof(*udp),
+		    paylen - sizeof(udp),
 		    checksum(&pkt->ipv6.ip.ip6_src, /* pseudo header */
 			2 * sizeof(pkt->ipv6.ip.ip6_src), csum))));
+		memcpy(&pkt->ipv6.ip, &ip6, sizeof(ip6));
 	}
+	memcpy(udp_ptr, &udp, sizeof(udp));
 
 	bzero(&pkt->vh, sizeof(pkt->vh));
 	// dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0);
@@ -1047,20 +1208,22 @@ initialize_packet(struct targ *targ)
 static void
 get_vnet_hdr_len(struct glob_arg *g)
 {
-	struct nmreq req;
+	struct nmreq_header hdr;
+	struct nmreq_port_hdr ph;
 	int err;
 
-	memset(&req, 0, sizeof(req));
-	bcopy(g->nmd->req.nr_name, req.nr_name, sizeof(req.nr_name));
-	req.nr_version = NETMAP_API;
-	req.nr_cmd = NETMAP_VNET_HDR_GET;
-	err = ioctl(g->main_fd, NIOCREGIF, &req);
+	hdr = g->nmd->hdr; /* copy name and version */
+	hdr.nr_reqtype = NETMAP_REQ_PORT_HDR_GET;
+	hdr.nr_options = 0;
+	memset(&ph, 0, sizeof(ph));
+	hdr.nr_body = (uintptr_t)&ph;
+	err = ioctl(g->main_fd, NIOCCTRL, &hdr);
 	if (err) {
 		D("Unable to get virtio-net header length");
 		return;
 	}
 
-	g->virt_header = req.nr_arg1;
+	g->virt_header = ph.nr_hdr_len;
 	if (g->virt_header) {
 		D("Port requires virtio-net header, length = %d",
 		  g->virt_header);
@@ -1071,23 +1234,23 @@ static void
 set_vnet_hdr_len(struct glob_arg *g)
 {
 	int err, l = g->virt_header;
-	struct nmreq req;
+	struct nmreq_header hdr;
+	struct nmreq_port_hdr ph;
 
 	if (l == 0)
 		return;
 
-	memset(&req, 0, sizeof(req));
-	bcopy(g->nmd->req.nr_name, req.nr_name, sizeof(req.nr_name));
-	req.nr_version = NETMAP_API;
-	req.nr_cmd = NETMAP_BDG_VNET_HDR;
-	req.nr_arg1 = l;
-	err = ioctl(g->main_fd, NIOCREGIF, &req);
+	hdr = g->nmd->hdr; /* copy name and version */
+	hdr.nr_reqtype = NETMAP_REQ_PORT_HDR_SET;
+	hdr.nr_options = 0;
+	memset(&ph, 0, sizeof(ph));
+	hdr.nr_body = (uintptr_t)&ph;
+	err = ioctl(g->main_fd, NIOCCTRL, &hdr);
 	if (err) {
 		D("Unable to set virtio-net header length %d", l);
 	}
 }
 
-
 /*
  * create and enqueue a batch of packets on a ring.
  * On the last one set NS_REPORT to tell the driver to generate
@@ -1095,35 +1258,34 @@ set_vnet_hdr_len(struct glob_arg *g)
  */
 static int
 send_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame,
-		int size, struct glob_arg *g, u_int count, int options,
-		u_int nfrags)
+		int size, struct targ *t, u_int count, int options)
 {
-	u_int n, sent, cur = ring->cur;
-	u_int fcnt;
+	u_int n, sent, head = ring->head;
+	u_int frags = t->frags;
+	u_int frag_size = t->frag_size;
+	struct netmap_slot *slot = &ring->slot[head];
 
 	n = nm_ring_space(ring);
-	if (n < count)
-		count = n;
-	if (count < nfrags) {
-		D("truncating packet, no room for frags %d %d",
-				count, nfrags);
-	}
 #if 0
 	if (options & (OPT_COPY | OPT_PREFETCH) ) {
 		for (sent = 0; sent < count; sent++) {
-			struct netmap_slot *slot = &ring->slot[cur];
+			struct netmap_slot *slot = &ring->slot[head];
 			char *p = NETMAP_BUF(ring, slot->buf_idx);
 
 			__builtin_prefetch(p);
-			cur = nm_ring_next(ring, cur);
+			head = nm_ring_next(ring, head);
 		}
-		cur = ring->cur;
+		head = ring->head;
 	}
 #endif
-	for (fcnt = nfrags, sent = 0; sent < count; sent++) {
-		struct netmap_slot *slot = &ring->slot[cur];
-		char *p = NETMAP_BUF(ring, slot->buf_idx);
-		int buf_changed = slot->flags & NS_BUF_CHANGED;
+	for (sent = 0; sent < count && n >= frags; sent++, n--) {
+		char *p;
+		int buf_changed;
+		u_int tosend = size;
+
+		slot = &ring->slot[head];
+		p = NETMAP_BUF(ring, slot->buf_idx);
+		buf_changed = slot->flags & NS_BUF_CHANGED;
 
 		slot->flags = 0;
 		if (options & OPT_RUBBISH) {
@@ -1131,31 +1293,49 @@ send_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame,
 		} else if (options & OPT_INDIRECT) {
 			slot->flags |= NS_INDIRECT;
 			slot->ptr = (uint64_t)((uintptr_t)frame);
-		} else if ((options & OPT_COPY) || buf_changed) {
-			nm_pkt_copy(frame, p, size);
-			if (fcnt == nfrags)
-				update_addresses(pkt, g);
-		} else if (options & OPT_MEMCPY) {
-			memcpy(p, frame, size);
-			if (fcnt == nfrags)
-				update_addresses(pkt, g);
+		} else if (frags > 1) {
+			u_int i;
+			const char *f = frame;
+			char *fp = p;
+			for (i = 0; i < frags - 1; i++) {
+				memcpy(fp, f, frag_size);
+				slot->len = frag_size;
+				slot->flags = NS_MOREFRAG;
+				if (options & OPT_DUMP)
+					dump_payload(fp, frag_size, ring, head);
+				tosend -= frag_size;
+				f += frag_size;
+				head = nm_ring_next(ring, head);
+				slot = &ring->slot[head];
+				fp = NETMAP_BUF(ring, slot->buf_idx);
+			}
+			n -= (frags - 1);
+			p = fp;
+			slot->flags = 0;
+			memcpy(p, f, tosend);
+			update_addresses(pkt, t);
+		} else if ((options & (OPT_COPY | OPT_MEMCPY)) || buf_changed) {
+			if (options & OPT_COPY)
+				nm_pkt_copy(frame, p, size);
+			else
+				memcpy(p, frame, size);
+			update_addresses(pkt, t);
 		} else if (options & OPT_PREFETCH) {
 			__builtin_prefetch(p);
 		}
+		slot->len = tosend;
 		if (options & OPT_DUMP)
-			dump_payload(p, size, ring, cur);
-		slot->len = size;
-		if (--fcnt > 0)
-			slot->flags |= NS_MOREFRAG;
-		else
-			fcnt = nfrags;
-		if (sent == count - 1) {
-			slot->flags &= ~NS_MOREFRAG;
-			slot->flags |= NS_REPORT;
-		}
-		cur = nm_ring_next(ring, cur);
+			dump_payload(p, tosend, ring, head);
+		head = nm_ring_next(ring, head);
+	}
+	if (sent) {
+		slot->flags |= NS_REPORT;
+		ring->head = ring->cur = head;
+	}
+	if (sent < count) {
+		/* tell netmap that we need more slots */
+		ring->cur = ring->tail;
 	}
-	ring->head = ring->cur = cur;
 
 	return (sent);
 }
@@ -1163,7 +1343,7 @@ send_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame,
 /*
  * Index of the highest bit set
  */
-uint32_t
+static uint32_t
 msb64(uint64_t x)
 {
 	uint64_t m = 1ULL << 63;
@@ -1175,29 +1355,48 @@ msb64(uint64_t x)
 	return 0;
 }
 
+/*
+ * wait until ts, either busy or sleeping if more than 1ms.
+ * Return wakeup time.
+ */
+static struct timespec
+wait_time(struct timespec ts)
+{
+	for (;;) {
+		struct timespec w, cur;
+		clock_gettime(CLOCK_REALTIME_PRECISE, &cur);
+		w = timespec_sub(ts, cur);
+		if (w.tv_sec < 0)
+			return cur;
+		else if (w.tv_sec > 0 || w.tv_nsec > 1000000)
+			poll(NULL, 0, 1);
+	}
+}
+
 /*
  * Send a packet, and wait for a response.
  * The payload (after UDP header, ofs 42) has a 4-byte sequence
  * followed by a struct timeval (or bintime?)
  */
-#define	PAY_OFS	42	/* where in the pkt... */
 
 static void *
-pinger_body(void *data)
+ping_body(void *data)
 {
 	struct targ *targ = (struct targ *) data;
 	struct pollfd pfd = { .fd = targ->fd, .events = POLLIN };
 	struct netmap_if *nifp = targ->nmd->nifp;
-	int i, rx = 0;
+	int i, m;
 	void *frame;
 	int size;
 	struct timespec ts, now, last_print;
+	struct timespec nexttime = {0, 0}; /* silence compiler */
 	uint64_t sent = 0, n = targ->g->npackets;
 	uint64_t count = 0, t_cur, t_min = ~0, av = 0;
+	uint64_t g_min = ~0, g_av = 0;
 	uint64_t buckets[64];	/* bins for delays, ns */
+	int rate_limit = targ->g->tx_rate, tosend = 0;
 
-	frame = &targ->pkt;
-	frame += sizeof(targ->pkt.vh) - targ->g->virt_header;
+	frame = (char*)&targ->pkt + sizeof(targ->pkt.vh) - targ->g->virt_header;
 	size = targ->g->pkt_size + targ->g->virt_header;
 
 
@@ -1206,48 +1405,91 @@ pinger_body(void *data)
 		return NULL;
 	}
 
+	if (targ->g->af == AF_INET6) {
+		D("Warning: ping-pong with IPv6 not supported");
+	}
+
 	bzero(&buckets, sizeof(buckets));
 	clock_gettime(CLOCK_REALTIME_PRECISE, &last_print);
 	now = last_print;
+	if (rate_limit) {
+		targ->tic = timespec_add(now, (struct timespec){2,0});
+		targ->tic.tv_nsec = 0;
+		wait_time(targ->tic);
+		nexttime = targ->tic;
+	}
 	while (!targ->cancel && (n == 0 || sent < n)) {
-		struct netmap_ring *ring = NETMAP_TXRING(nifp, 0);
+		struct netmap_ring *ring = NETMAP_TXRING(nifp, targ->nmd->first_tx_ring);
 		struct netmap_slot *slot;
 		char *p;
-	    for (i = 0; i < 1; i++) { /* XXX why the loop for 1 pkt ? */
-		slot = &ring->slot[ring->cur];
-		slot->len = size;
-		p = NETMAP_BUF(ring, slot->buf_idx);
+		int rv;
+		uint64_t limit, event = 0;
 
-		if (nm_ring_empty(ring)) {
-			D("-- ouch, cannot send");
-		} else {
-			struct tstamp *tp;
-			nm_pkt_copy(frame, p, size);
-			clock_gettime(CLOCK_REALTIME_PRECISE, &ts);
-			bcopy(&sent, p+42, sizeof(sent));
-			tp = (struct tstamp *)(p+46);
-			tp->sec = (uint32_t)ts.tv_sec;
-			tp->nsec = (uint32_t)ts.tv_nsec;
-			sent++;
-			ring->head = ring->cur = nm_ring_next(ring, ring->cur);
+		if (rate_limit && tosend <= 0) {
+			tosend = targ->g->burst;
+			nexttime = timespec_add(nexttime, targ->g->tx_period);
+			wait_time(nexttime);
 		}
-	    }
-		/* should use a parameter to decide how often to send */
-		if (poll(&pfd, 1, 3000) <= 0) {
-			D("poll error/timeout on queue %d: %s", targ->me,
+
+		limit = rate_limit ? tosend : targ->g->burst;
+		if (n > 0 && n - sent < limit)
+			limit = n - sent;
+		for (m = 0; (unsigned)m < limit; m++) {
+			slot = &ring->slot[ring->head];
+			slot->len = size;
+			p = NETMAP_BUF(ring, slot->buf_idx);
+
+			if (nm_ring_empty(ring)) {
+				D("-- ouch, cannot send");
+				break;
+			} else {
+				struct tstamp *tp;
+				nm_pkt_copy(frame, p, size);
+				clock_gettime(CLOCK_REALTIME_PRECISE, &ts);
+				bcopy(&sent, p+42, sizeof(sent));
+				tp = (struct tstamp *)(p+46);
+				tp->sec = (uint32_t)ts.tv_sec;
+				tp->nsec = (uint32_t)ts.tv_nsec;
+				sent++;
+				ring->head = ring->cur = nm_ring_next(ring, ring->head);
+			}
+		}
+		if (m > 0)
+			event++;
+		targ->ctr.pkts = sent;
+		targ->ctr.bytes = sent*size;
+		targ->ctr.events = event;
+		if (rate_limit)
+			tosend -= m;
+#ifdef BUSYWAIT
+		rv = ioctl(pfd.fd, NIOCTXSYNC, NULL);
+		if (rv < 0) {
+			D("TXSYNC error on queue %d: %s", targ->me,
 				strerror(errno));
+		}
+	again:
+		ioctl(pfd.fd, NIOCRXSYNC, NULL);
+#else
+		/* should use a parameter to decide how often to send */
+		if ( (rv = poll(&pfd, 1, 3000)) <= 0) {
+			D("poll error on queue %d: %s", targ->me,
+				(rv ? strerror(errno) : "timeout"));
 			continue;
 		}
+#endif /* BUSYWAIT */
 		/* see what we got back */
-		for (i = targ->nmd->first_tx_ring;
-			i <= targ->nmd->last_tx_ring; i++) {
+#ifdef BUSYWAIT
+		int rx = 0;
+#endif
+		for (i = targ->nmd->first_rx_ring;
+			i <= targ->nmd->last_rx_ring; i++) {
 			ring = NETMAP_RXRING(nifp, i);
 			while (!nm_ring_empty(ring)) {
 				uint32_t seq;
 				struct tstamp *tp;
 				int pos;
 
-				slot = &ring->slot[ring->cur];
+				slot = &ring->slot[ring->head];
 				p = NETMAP_BUF(ring, slot->buf_idx);
 
 				clock_gettime(CLOCK_REALTIME_PRECISE, &now);
@@ -1261,7 +1503,8 @@ pinger_body(void *data)
 					ts.tv_nsec += 1000000000;
 					ts.tv_sec--;
 				}
-				if (0) D("seq %d/%lu delta %d.%09d", seq, sent,
+				if (0) D("seq %d/%llu delta %d.%09d", seq,
+					(unsigned long long)sent,
 					(int)ts.tv_sec, (int)ts.tv_nsec);
 				t_cur = ts.tv_sec * 1000000000UL + ts.tv_nsec;
 				if (t_cur < t_min)
@@ -1271,8 +1514,10 @@ pinger_body(void *data)
 				pos = msb64(t_cur);
 				buckets[pos]++;
 				/* now store it in a bucket */
-				ring->head = ring->cur = nm_ring_next(ring, ring->cur);
+				ring->head = ring->cur = nm_ring_next(ring, ring->head);
+#ifdef BUSYWAIT
 				rx++;
+#endif
 			}
 		}
 		//D("tx %d rx %d", sent, rx);
@@ -1286,7 +1531,7 @@ pinger_body(void *data)
 		if (ts.tv_sec >= 1) {
 			D("count %d RTT: min %d av %d ns",
 				(int)count, (int)t_min, (int)(av/count));
-			int k, j, kmin;
+			int k, j, kmin, off;
 			char buf[512];
 
 			for (kmin = 0; kmin < 64; kmin ++)
@@ -1296,17 +1541,33 @@ pinger_body(void *data)
 				if (buckets[k])
 					break;
 			buf[0] = '\0';
-			for (j = kmin; j <= k; j++)
-				sprintf(buf, "%s %5d", buf, (int)buckets[j]);
+			off = 0;
+			for (j = kmin; j <= k; j++) {
+				off += sprintf(buf + off, " %5d", (int)buckets[j]);
+			}
 			D("k: %d .. %d\n\t%s", 1<cancel)
+			goto again;
+#endif /* BUSYWAIT */
 	}
 
+	if (sent > 0) {
+		D("RTT over %llu packets: min %d av %d ns",
+			(long long unsigned)sent, (int)g_min,
+			(int)((double)g_av/sent));
+	}
+	targ->completed = 1;
+
 	/* reset the ``used`` flag. */
 	targ->used = 0;
 
@@ -1318,76 +1579,92 @@ pinger_body(void *data)
  * reply to ping requests
  */
 static void *
-ponger_body(void *data)
+pong_body(void *data)
 {
 	struct targ *targ = (struct targ *) data;
 	struct pollfd pfd = { .fd = targ->fd, .events = POLLIN };
 	struct netmap_if *nifp = targ->nmd->nifp;
 	struct netmap_ring *txring, *rxring;
-	int i, rx = 0;
+	int i;
 	uint64_t sent = 0, n = targ->g->npackets;
 
 	if (targ->g->nthreads > 1) {
 		D("can only reply ping with 1 thread");
 		return NULL;
 	}
-	D("understood ponger %lu but don't know how to do it", n);
+	if (n > 0)
+		D("understood ponger %llu but don't know how to do it",
+			(unsigned long long)n);
+
+	if (targ->g->af == AF_INET6) {
+		D("Warning: ping-pong with IPv6 not supported");
+	}
+
 	while (!targ->cancel && (n == 0 || sent < n)) {
-		uint32_t txcur, txavail;
+		uint32_t txhead, txavail;
 //#define BUSYWAIT
 #ifdef BUSYWAIT
 		ioctl(pfd.fd, NIOCRXSYNC, NULL);
 #else
-		if (poll(&pfd, 1, 1000) <= 0) {
-			D("poll error/timeout on queue %d: %s", targ->me,
-				strerror(errno));
+		int rv;
+		if ( (rv = poll(&pfd, 1, 1000)) <= 0) {
+			D("poll error on queue %d: %s", targ->me,
+				rv ? strerror(errno) : "timeout");
 			continue;
 		}
 #endif
-		txring = NETMAP_TXRING(nifp, 0);
-		txcur = txring->cur;
+		txring = NETMAP_TXRING(nifp, targ->nmd->first_tx_ring);
+		txhead = txring->head;
 		txavail = nm_ring_space(txring);
 		/* see what we got back */
 		for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) {
 			rxring = NETMAP_RXRING(nifp, i);
 			while (!nm_ring_empty(rxring)) {
 				uint16_t *spkt, *dpkt;
-				uint32_t cur = rxring->cur;
-				struct netmap_slot *slot = &rxring->slot[cur];
+				uint32_t head = rxring->head;
+				struct netmap_slot *slot = &rxring->slot[head];
 				char *src, *dst;
 				src = NETMAP_BUF(rxring, slot->buf_idx);
 				//D("got pkt %p of size %d", src, slot->len);
-				rxring->head = rxring->cur = nm_ring_next(rxring, cur);
-				rx++;
+				rxring->head = rxring->cur = nm_ring_next(rxring, head);
 				if (txavail == 0)
 					continue;
 				dst = NETMAP_BUF(txring,
-				    txring->slot[txcur].buf_idx);
+				    txring->slot[txhead].buf_idx);
 				/* copy... */
 				dpkt = (uint16_t *)dst;
 				spkt = (uint16_t *)src;
 				nm_pkt_copy(src, dst, slot->len);
+				/* swap source and destination MAC */
 				dpkt[0] = spkt[3];
 				dpkt[1] = spkt[4];
 				dpkt[2] = spkt[5];
 				dpkt[3] = spkt[0];
 				dpkt[4] = spkt[1];
 				dpkt[5] = spkt[2];
-				txring->slot[txcur].len = slot->len;
-				/* XXX swap src dst mac */
-				txcur = nm_ring_next(txring, txcur);
+				/* swap source and destination IPv4 */
+				if (spkt[6] == htons(ETHERTYPE_IP)) {
+					dpkt[13] = spkt[15];
+					dpkt[14] = spkt[16];
+					dpkt[15] = spkt[13];
+					dpkt[16] = spkt[14];
+				}
+				txring->slot[txhead].len = slot->len;
+				//dump_payload(dst, slot->len, txring, txhead);
+				txhead = nm_ring_next(txring, txhead);
 				txavail--;
 				sent++;
 			}
 		}
-		txring->head = txring->cur = txcur;
+		txring->head = txring->cur = txhead;
 		targ->ctr.pkts = sent;
 #ifdef BUSYWAIT
 		ioctl(pfd.fd, NIOCTXSYNC, NULL);
 #endif
-		//D("tx %d rx %d", sent, rx);
 	}
 
+	targ->completed = 1;
+
 	/* reset the ``used`` flag. */
 	targ->used = 0;
 
@@ -1395,24 +1672,6 @@ ponger_body(void *data)
 }
 
 
-/*
- * wait until ts, either busy or sleeping if more than 1ms.
- * Return wakeup time.
- */
-static struct timespec
-wait_time(struct timespec ts)
-{
-	for (;;) {
-		struct timespec w, cur;
-		clock_gettime(CLOCK_REALTIME_PRECISE, &cur);
-		w = timespec_sub(ts, cur);
-		if (w.tv_sec < 0)
-			return cur;
-		else if (w.tv_sec > 0 || w.tv_nsec > 1000000)
-			poll(NULL, 0, 1);
-	}
-}
-
 static void *
 sender_body(void *data)
 {
@@ -1424,7 +1683,7 @@ sender_body(void *data)
 	uint64_t n = targ->g->npackets / targ->g->nthreads;
 	uint64_t sent = 0;
 	uint64_t event = 0;
-	int options = targ->g->options | OPT_COPY;
+	int options = targ->g->options;
 	struct timespec nexttime = { 0, 0}; // XXX silence compiler
 	int rate_limit = targ->g->tx_rate;
 	struct pkt *pkt = &targ->pkt;
@@ -1432,8 +1691,7 @@ sender_body(void *data)
 	int size;
 
 	if (targ->frame == NULL) {
-		frame = pkt;
-		frame += sizeof(pkt->vh) - targ->g->virt_header;
+		frame = (char *)pkt + sizeof(pkt->vh) - targ->g->virt_header;
 		size = targ->g->pkt_size + targ->g->virt_header;
 	} else {
 		frame = targ->frame;
@@ -1452,13 +1710,13 @@ sender_body(void *data)
 		wait_time(targ->tic);
 		nexttime = targ->tic;
 	}
-        if (targ->g->dev_type == DEV_TAP) {
+	if (targ->g->dev_type == DEV_TAP) {
 	    D("writing to file desc %d", targ->g->main_fd);
 
 	    for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
 		if (write(targ->g->main_fd, frame, size) != -1)
 			sent++;
-		update_addresses(pkt, targ->g);
+		update_addresses(pkt, targ);
 		if (i > 10000) {
 			targ->ctr.pkts = sent;
 			targ->ctr.bytes = sent*size;
@@ -1473,7 +1731,7 @@ sender_body(void *data)
 	    for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
 		if (pcap_inject(p, frame, size) != -1)
 			sent++;
-		update_addresses(pkt, targ->g);
+		update_addresses(pkt, targ);
 		if (i > 10000) {
 			targ->ctr.pkts = sent;
 			targ->ctr.bytes = sent*size;
@@ -1484,10 +1742,36 @@ sender_body(void *data)
 #endif /* NO_PCAP */
     } else {
 	int tosend = 0;
-	int frags = targ->g->frags;
+	u_int bufsz, frag_size = targ->g->frag_size;
 
 	nifp = targ->nmd->nifp;
+	txring = NETMAP_TXRING(nifp, targ->nmd->first_tx_ring);
+	bufsz = txring->nr_buf_size;
+	if (bufsz < frag_size)
+		frag_size = bufsz;
+	targ->frag_size = targ->g->pkt_size / targ->frags;
+	if (targ->frag_size > frag_size) {
+		targ->frags = targ->g->pkt_size / frag_size;
+		targ->frag_size = frag_size;
+		if (targ->g->pkt_size % frag_size != 0)
+			targ->frags++;
+	}
+	D("frags %u frag_size %u", targ->frags, targ->frag_size);
+
+	/* mark all slots of all rings as changed so initial copy will be done */
+	for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) {
+		uint32_t j;
+		struct netmap_slot *slot;
+
+		txring = NETMAP_TXRING(nifp, i);
+		for (j = 0; j < txring->num_slots; j++) {
+			slot = &txring->slot[j];
+			slot->flags = NS_BUF_CHANGED;
+		}
+	}
+
 	while (!targ->cancel && (n == 0 || sent < n)) {
+		int rv;
 
 		if (rate_limit && tosend <= 0) {
 			tosend = targ->g->burst;
@@ -1499,17 +1783,18 @@ sender_body(void *data)
 		 * wait for available room in the send queue(s)
 		 */
 #ifdef BUSYWAIT
+		(void)rv;
 		if (ioctl(pfd.fd, NIOCTXSYNC, NULL) < 0) {
 			D("ioctl error on queue %d: %s", targ->me,
 					strerror(errno));
 			goto quit;
 		}
 #else /* !BUSYWAIT */
-		if (poll(&pfd, 1, 2000) <= 0) {
+		if ( (rv = poll(&pfd, 1, 2000)) <= 0) {
 			if (targ->cancel)
 				break;
-			D("poll error/timeout on queue %d: %s", targ->me,
-				strerror(errno));
+			D("poll error on queue %d: %s", targ->me,
+				rv ? strerror(errno) : "timeout");
 			// goto quit;
 		}
 		if (pfd.revents & POLLERR) {
@@ -1521,30 +1806,34 @@ sender_body(void *data)
 		/*
 		 * scan our queues and send on those with room
 		 */
-		if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) {
-			D("drop copy");
-			options &= ~OPT_COPY;
-		}
 		for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) {
 			int m;
 			uint64_t limit = rate_limit ?  tosend : targ->g->burst;
+
+			if (n > 0 && n == sent)
+				break;
+
 			if (n > 0 && n - sent < limit)
 				limit = n - sent;
 			txring = NETMAP_TXRING(nifp, i);
 			if (nm_ring_empty(txring))
 				continue;
-			if (frags > 1)
-				limit = ((limit + frags - 1) / frags) * frags;
 
-			m = send_packets(txring, pkt, frame, size, targ->g,
-					 limit, options, frags);
-			ND("limit %d tail %d frags %d m %d",
-				limit, txring->tail, frags, m);
+			if (targ->g->pkt_min_size > 0) {
+				size = nrand48(targ->seed) %
+					(targ->g->pkt_size - targ->g->pkt_min_size) +
+					targ->g->pkt_min_size;
+				update_size(pkt, targ, size);
+			}
+			m = send_packets(txring, pkt, frame, size, targ,
+					 limit, options);
+			ND("limit %lu tail %d m %d",
+				limit, txring->tail, m);
 			sent += m;
 			if (m > 0) //XXX-ste: can m be 0?
 				event++;
 			targ->ctr.pkts = sent;
-			targ->ctr.bytes = sent*size;
+			targ->ctr.bytes += m*size;
 			targ->ctr.events = event;
 			if (rate_limit) {
 				tosend -= m;
@@ -1554,10 +1843,12 @@ sender_body(void *data)
 		}
 	}
 	/* flush any remaining packets */
-	D("flush tail %d head %d on thread %p",
-		txring->tail, txring->head,
-		(void *)pthread_self());
-	ioctl(pfd.fd, NIOCTXSYNC, NULL);
+	if (txring != NULL) {
+		D("flush tail %d head %d on thread %p",
+			txring->tail, txring->head,
+			(void *)pthread_self());
+		ioctl(pfd.fd, NIOCTXSYNC, NULL);
+	}
 
 	/* final part: wait all the TX queues to be empty. */
 	for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) {
@@ -1600,29 +1891,32 @@ receive_pcap(u_char *user, const struct pcap_pkthdr * h,
 static int
 receive_packets(struct netmap_ring *ring, u_int limit, int dump, uint64_t *bytes)
 {
-	u_int cur, rx, n;
+	u_int head, rx, n;
 	uint64_t b = 0;
+	u_int complete = 0;
 
 	if (bytes == NULL)
 		bytes = &b;
 
-	cur = ring->cur;
+	head = ring->head;
 	n = nm_ring_space(ring);
 	if (n < limit)
 		limit = n;
 	for (rx = 0; rx < limit; rx++) {
-		struct netmap_slot *slot = &ring->slot[cur];
+		struct netmap_slot *slot = &ring->slot[head];
 		char *p = NETMAP_BUF(ring, slot->buf_idx);
 
 		*bytes += slot->len;
 		if (dump)
-			dump_payload(p, slot->len, ring, cur);
+			dump_payload(p, slot->len, ring, head);
+		if (!(slot->flags & NS_MOREFRAG))
+			complete++;
 
-		cur = nm_ring_next(ring, cur);
+		head = nm_ring_next(ring, head);
 	}
-	ring->head = ring->cur = cur;
+	ring->head = ring->cur = head;
 
-	return (rx);
+	return (complete);
 }
 
 static void *
@@ -1634,9 +1928,9 @@ receiver_body(void *data)
 	struct netmap_ring *rxring;
 	int i;
 	struct my_ctrs cur;
+	uint64_t n = targ->g->npackets / targ->g->nthreads;
 
-	cur.pkts = cur.bytes = cur.events = cur.drop = cur.min_space = 0;
-	cur.t.tv_usec = cur.t.tv_sec = 0; //  unused, just silence the compiler
+	memset(&cur, 0, sizeof(cur));
 
 	if (setaffinity(targ->thread, targ->affinity))
 		goto quit;
@@ -1662,7 +1956,7 @@ receiver_body(void *data)
 	/* main loop, exit after 1s silence */
 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
     if (targ->g->dev_type == DEV_TAP) {
-	while (!targ->cancel) {
+	while (!targ->cancel && (n == 0 || targ->ctr.pkts < n)) {
 		char buf[MAX_BODYSIZE];
 		/* XXX should we poll ? */
 		i = read(targ->g->main_fd, buf, sizeof(buf));
@@ -1674,18 +1968,18 @@ receiver_body(void *data)
 	}
 #ifndef NO_PCAP
     } else if (targ->g->dev_type == DEV_PCAP) {
-	while (!targ->cancel) {
+	while (!targ->cancel && (n == 0 || targ->ctr.pkts < n)) {
 		/* XXX should we poll ? */
 		pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap,
 			(u_char *)&targ->ctr);
-                targ->ctr.events++;
+		targ->ctr.events++;
 	}
 #endif /* !NO_PCAP */
     } else {
 	int dump = targ->g->options & OPT_DUMP;
 
 	nifp = targ->nmd->nifp;
-	while (!targ->cancel) {
+	while (!targ->cancel && (n == 0 || targ->ctr.pkts < n)) {
 		/* Once we started to receive packets, wait at most 1 seconds
 		   before quitting. */
 #ifdef BUSYWAIT
@@ -1721,7 +2015,7 @@ receiver_body(void *data)
 
 			m = receive_packets(rxring, targ->g->burst, dump, &cur.bytes);
 			cur.pkts += m;
-			if (m > 0) //XXX-ste: can m be 0?
+			if (m > 0)
 				cur.events++;
 		}
 		cur.min_space = targ->ctr.min_space;
@@ -1773,8 +2067,7 @@ txseq_body(void *data)
 		D("Ignoring -n argument");
 	}
 
-	frame = pkt;
-	frame += sizeof(pkt->vh) - targ->g->virt_header;
+	frame = (char *)pkt + sizeof(pkt->vh) - targ->g->virt_header;
 	size = targ->g->pkt_size + targ->g->virt_header;
 
 	D("start, fd %d main_fd %d", targ->fd, targ->g->main_fd);
@@ -1797,6 +2090,8 @@ txseq_body(void *data)
 		unsigned int space;
 		unsigned int head;
 		int fcnt;
+		uint16_t sum = 0;
+		int rv;
 
 		if (!rate_limit) {
 			budget = targ->g->burst;
@@ -1808,17 +2103,27 @@ txseq_body(void *data)
 		}
 
 		/* wait for available room in the send queue */
-		if (poll(&pfd, 1, 2000) <= 0) {
+#ifdef BUSYWAIT
+		(void)rv;
+		if (ioctl(pfd.fd, NIOCTXSYNC, NULL) < 0) {
+			D("ioctl error on queue %d: %s", targ->me,
+					strerror(errno));
+			goto quit;
+		}
+#else /* !BUSYWAIT */
+		if ( (rv = poll(&pfd, 1, 2000)) <= 0) {
 			if (targ->cancel)
 				break;
-			D("poll error/timeout on queue %d: %s", targ->me,
-				strerror(errno));
+			D("poll error on queue %d: %s", targ->me,
+				rv ? strerror(errno) : "timeout");
+			// goto quit;
 		}
 		if (pfd.revents & POLLERR) {
 			D("poll error on %d ring %d-%d", pfd.fd,
 				targ->nmd->first_tx_ring, targ->nmd->last_tx_ring);
 			goto quit;
 		}
+#endif /* !BUSYWAIT */
 
 		/* If no room poll() again. */
 		space = nm_ring_space(ring);
@@ -1843,15 +2148,23 @@ txseq_body(void *data)
 				sent < limit; sent++, sequence++) {
 			struct netmap_slot *slot = &ring->slot[head];
 			char *p = NETMAP_BUF(ring, slot->buf_idx);
+			uint16_t *w = (uint16_t *)PKT(pkt, body, targ->g->af), t;
+
+			memcpy(&sum, targ->g->af == AF_INET ? &pkt->ipv4.udp.uh_sum : &pkt->ipv6.udp.uh_sum, sizeof(sum));
 
 			slot->flags = 0;
+			t = *w;
 			PKT(pkt, body, targ->g->af)[0] = sequence >> 24;
 			PKT(pkt, body, targ->g->af)[1] = (sequence >> 16) & 0xff;
+			sum = ~cksum_add(~sum, cksum_add(~t, *w));
+			t = *++w;
 			PKT(pkt, body, targ->g->af)[2] = (sequence >> 8) & 0xff;
 			PKT(pkt, body, targ->g->af)[3] = sequence & 0xff;
+			sum = ~cksum_add(~sum, cksum_add(~t, *w));
+			memcpy(targ->g->af == AF_INET ? &pkt->ipv4.udp.uh_sum : &pkt->ipv6.udp.uh_sum, &sum, sizeof(sum));
 			nm_pkt_copy(frame, p, size);
 			if (fcnt == frags) {
-				update_addresses(pkt, targ->g);
+				update_addresses(pkt, targ);
 			}
 
 			if (options & OPT_DUMP) {
@@ -1945,19 +2258,25 @@ rxseq_body(void *data)
 	int dump = targ->g->options & OPT_DUMP;
 	struct netmap_ring *ring;
 	unsigned int frags_exp = 1;
-	uint32_t seq_exp = 0;
 	struct my_ctrs cur;
 	unsigned int frags = 0;
 	int first_packet = 1;
 	int first_slot = 1;
-	int i, af;
+	int i, j, af, nrings;
+	uint32_t seq, *seq_exp = NULL;
 
-	cur.pkts = cur.bytes = cur.events = cur.drop = cur.min_space = 0;
-	cur.t.tv_usec = cur.t.tv_sec = 0; //  unused, just silence the compiler
+	memset(&cur, 0, sizeof(cur));
 
 	if (setaffinity(targ->thread, targ->affinity))
 		goto quit;
 
+	nrings = targ->nmd->last_rx_ring - targ->nmd->first_rx_ring + 1;
+	seq_exp = calloc(nrings, sizeof(uint32_t));
+	if (seq_exp == NULL) {
+		D("failed to allocate seq array");
+		goto quit;
+	}
+
 	D("reading from %s fd %d main_fd %d",
 		targ->g->ifname, targ->fd, targ->g->main_fd);
 	/* unbounded wait for the first packet. */
@@ -1971,15 +2290,18 @@ rxseq_body(void *data)
 
 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
 
-	ring = NETMAP_RXRING(targ->nmd->nifp, targ->nmd->first_rx_ring);
 
 	while (!targ->cancel) {
 		unsigned int head;
-		uint32_t seq;
 		int limit;
 
-		/* Once we started to receive packets, wait at most 1 seconds
-		   before quitting. */
+#ifdef BUSYWAIT
+		if (ioctl(pfd.fd, NIOCRXSYNC, NULL) < 0) {
+			D("ioctl error on queue %d: %s", targ->me,
+					strerror(errno));
+			goto quit;
+		}
+#else /* !BUSYWAIT */
 		if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) {
 			clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
 			targ->toc.tv_sec -= 1; /* Subtract timeout time. */
@@ -1990,115 +2312,123 @@ rxseq_body(void *data)
 			D("poll err");
 			goto quit;
 		}
+#endif /* !BUSYWAIT */
 
-		if (nm_ring_empty(ring))
-			continue;
+		for (j = targ->nmd->first_rx_ring; j <= targ->nmd->last_rx_ring; j++) {
+			ring = NETMAP_RXRING(targ->nmd->nifp, j);
+			if (nm_ring_empty(ring))
+				continue;
 
-		limit = nm_ring_space(ring);
-		if (limit > targ->g->burst)
-			limit = targ->g->burst;
+			limit = nm_ring_space(ring);
+			if (limit > targ->g->burst)
+				limit = targ->g->burst;
 
 #if 0
-		/* Enable this if
-		 *     1) we remove the early-return optimization from
-		 *        the netmap poll implementation, or
-		 *     2) pipes get NS_MOREFRAG support.
-		 * With the current netmap implementation, an experiment like
-		 *    pkt-gen -i vale:1{1 -f txseq -F 9
-		 *    pkt-gen -i vale:1}1 -f rxseq
-		 * would get stuck as soon as we find nm_ring_space(ring) < 9,
-		 * since here limit is rounded to 0 and
-		 * pipe rxsync is not called anymore by the poll() of this loop.
-		 */
-		if (frags_exp > 1) {
-			int o = limit;
-			/* Cut off to the closest smaller multiple. */
-			limit = (limit / frags_exp) * frags_exp;
-			RD(2, "LIMIT %d --> %d", o, limit);
-		}
+			/* Enable this if
+			 *     1) we remove the early-return optimization from
+			 *        the netmap poll implementation, or
+			 *     2) pipes get NS_MOREFRAG support.
+			 * With the current netmap implementation, an experiment like
+			 *    pkt-gen -i vale:1{1 -f txseq -F 9
+			 *    pkt-gen -i vale:1}1 -f rxseq
+			 * would get stuck as soon as we find nm_ring_space(ring) < 9,
+			 * since here limit is rounded to 0 and
+			 * pipe rxsync is not called anymore by the poll() of this loop.
+			 */
+			if (frags_exp > 1) {
+				int o = limit;
+				/* Cut off to the closest smaller multiple. */
+				limit = (limit / frags_exp) * frags_exp;
+				RD(2, "LIMIT %d --> %d", o, limit);
+			}
 #endif
 
-		for (head = ring->head, i = 0; i < limit; i++) {
-			struct netmap_slot *slot = &ring->slot[head];
-			char *p = NETMAP_BUF(ring, slot->buf_idx);
-			int len = slot->len;
-			struct pkt *pkt;
+			for (head = ring->head, i = 0; i < limit; i++) {
+				struct netmap_slot *slot = &ring->slot[head];
+				char *p = NETMAP_BUF(ring, slot->buf_idx);
+				int len = slot->len;
+				struct pkt *pkt;
 
-			if (dump) {
-				dump_payload(p, slot->len, ring, head);
-			}
+				if (dump) {
+					dump_payload(p, slot->len, ring, head);
+				}
 
-			frags++;
-			if (!(slot->flags & NS_MOREFRAG)) {
-				if (first_packet) {
+				frags++;
+				if (!(slot->flags & NS_MOREFRAG)) {
+					if (first_packet) {
+						first_packet = 0;
+					} else if (frags != frags_exp) {
+						char prbuf[512];
+						RD(1, "Received packets with %u frags, "
+								"expected %u, '%s'", frags, frags_exp,
+								multi_slot_to_string(ring, head-frags+1,
+							       	frags,
+									prbuf, sizeof(prbuf)));
+					}
 					first_packet = 0;
-				} else if (frags != frags_exp) {
-					char prbuf[512];
-					RD(1, "Received packets with %u frags, "
-					      "expected %u, '%s'", frags, frags_exp,
-					      multi_slot_to_string(ring, head-frags+1, frags,
-								   prbuf, sizeof(prbuf)));
+					frags_exp = frags;
+					frags = 0;
 				}
-				first_packet = 0;
-				frags_exp = frags;
-				frags = 0;
-			}
 
-			p -= sizeof(pkt->vh) - targ->g->virt_header;
-			len += sizeof(pkt->vh) - targ->g->virt_header;
-			pkt = (struct pkt *)p;
-			if (ntohs(pkt->eh.ether_type) == ETHERTYPE_IP)
-				af = AF_INET;
-			else
-				af = AF_INET6;
-
-			if ((char *)pkt + len < ((char *)PKT(pkt, body, af)) +
-			    sizeof(seq)) {
-				RD(1, "%s: packet too small (len=%u)", __func__,
-				      slot->len);
-			} else {
-				seq = (PKT(pkt, body, af)[0] << 24) |
-				    (PKT(pkt, body, af)[1] << 16) |
-				    (PKT(pkt, body, af)[2] << 8) |
-				    PKT(pkt, body, af)[3];
-				if (first_slot) {
-					/* Grab the first one, whatever it
-					   is. */
-					seq_exp = seq;
-					first_slot = 0;
-				} else if (seq != seq_exp) {
-					uint32_t delta = seq - seq_exp;
-
-					if (delta < (0xFFFFFFFF >> 1)) {
-						RD(2, "Sequence GAP: exp %u found %u",
-						      seq_exp, seq);
-					} else {
-						RD(2, "Sequence OUT OF ORDER: "
-						      "exp %u found %u", seq_exp, seq);
+				p -= sizeof(pkt->vh) - targ->g->virt_header;
+				len += sizeof(pkt->vh) - targ->g->virt_header;
+				pkt = (struct pkt *)p;
+				if (ntohs(pkt->eh.ether_type) == ETHERTYPE_IP)
+					af = AF_INET;
+				else
+					af = AF_INET6;
+
+				if ((char *)pkt + len < ((char *)PKT(pkt, body, af)) +
+						sizeof(seq)) {
+					RD(1, "%s: packet too small (len=%u)", __func__,
+							slot->len);
+				} else {
+					seq = (PKT(pkt, body, af)[0] << 24) |
+						(PKT(pkt, body, af)[1] << 16) |
+						(PKT(pkt, body, af)[2] << 8) |
+						PKT(pkt, body, af)[3];
+					if (first_slot) {
+						/* Grab the first one, whatever it
+						   is. */
+						seq_exp[j] = seq;
+						first_slot = 0;
+					} else if (seq != seq_exp[j]) {
+						uint32_t delta = seq - seq_exp[j];
+
+						if (delta < (0xFFFFFFFF >> 1)) {
+							RD(2, "Sequence GAP: exp %u found %u",
+									seq_exp[j], seq);
+						} else {
+							RD(2, "Sequence OUT OF ORDER: "
+									"exp %u found %u", seq_exp[j], seq);
+						}
+						seq_exp[j] = seq;
 					}
-					seq_exp = seq;
+					seq_exp[j]++;
 				}
-				seq_exp++;
-			}
 
-			cur.bytes += slot->len;
-			head = nm_ring_next(ring, head);
-			cur.pkts++;
-		}
+				cur.bytes += slot->len;
+				head = nm_ring_next(ring, head);
+				cur.pkts++;
+			}
 
-		ring->cur = ring->head = head;
+			ring->cur = ring->head = head;
 
-		cur.events++;
-		targ->ctr = cur;
+			cur.events++;
+			targ->ctr = cur;
+		}
 	}
-
 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
 
+#ifndef BUSYWAIT
 out:
+#endif /* !BUSYWAIT */
 	targ->completed = 1;
 	targ->ctr = cur;
 
 quit:
+	if (seq_exp != NULL)
+		free(seq_exp);
 	/* reset the ``used`` flag. */
 	targ->used = 0;
 
@@ -2107,7 +2437,7 @@ rxseq_body(void *data)
 
 
 static void
-tx_output(struct my_ctrs *cur, double delta, const char *msg)
+tx_output(struct glob_arg *g, struct my_ctrs *cur, double delta, const char *msg)
 {
 	double bw, raw_bw, pps, abs;
 	char b1[40], b2[80], b3[80];
@@ -2131,60 +2461,160 @@ tx_output(struct my_ctrs *cur, double delta, const char *msg)
 		size = 60;
 	pps = cur->pkts / delta;
 	bw = (8.0 * cur->bytes) / delta;
-	/* raw packets have4 bytes crc + 20 bytes framing */
-	raw_bw = (8.0 * (cur->pkts * 24 + cur->bytes)) / delta;
+	raw_bw = (8.0 * cur->bytes + cur->pkts * g->framing) / delta;
 	abs = cur->pkts / (double)(cur->events);
 
 	printf("Speed: %spps Bandwidth: %sbps (raw %sbps). Average batch: %.2f pkts\n",
-		norm(b1, pps), norm(b2, bw), norm(b3, raw_bw), abs);
+		norm(b1, pps, normalize), norm(b2, bw, normalize), norm(b3, raw_bw, normalize), abs);
 }
 
 static void
-usage(void)
+usage(int errcode)
 {
+/* This usage is generated from the pkt-gen man page:
+ *   $ man pkt-gen > x
+ * and pasted here adding the string terminators and endlines with simple
+ * regular expressions. */
 	const char *cmd = "pkt-gen";
 	fprintf(stderr,
 		"Usage:\n"
 		"%s arguments\n"
-		"\t-i interface		interface name\n"
-		"\t-f function		tx rx ping pong txseq rxseq\n"
-		"\t-n count		number of iterations (can be 0)\n"
-		"\t-t pkts_to_send	also forces tx mode\n"
-		"\t-r pkts_to_receive	also forces rx mode\n"
-		"\t-l pkt_size		in bytes excluding CRC\n"
-		"\t-d dst_ip[:port[-dst_ip:port]]   single or range\n"
-		"\t-s src_ip[:port[-src_ip:port]]   single or range\n"
-		"\t-D dst-mac\n"
-		"\t-S src-mac\n"
-		"\t-a cpu_id		use setaffinity\n"
-		"\t-b burst size		testing, mostly\n"
-		"\t-c cores		cores to use\n"
-		"\t-p threads		processes/threads to use\n"
-		"\t-T report_ms		milliseconds between reports\n"
-		"\t-w wait_for_link_time	in seconds\n"
-		"\t-R rate		in packets per second\n"
-		"\t-X			dump payload\n"
-		"\t-H len		add empty virtio-net-header with size 'len'\n"
-		"\t-E pipes		allocate extra space for a number of pipes\n"
-		"\t-r			do not touch the buffers (send rubbish)\n"
-	        "\t-P file		load packet from pcap file\n"
-		"\t-z			use random IPv4 src address/port\n"
-		"\t-Z			use random IPv4 dst address/port\n"
-		"\t-F num_frags		send multi-slot packets\n"
-		"\t-A			activate pps stats on receiver\n"
-		"",
+"     -h      Show program usage and exit.\n"
+"\n"
+"     -i interface\n"
+"             Name of the network interface that pkt-gen operates on.  It can be a system network interface\n"
+"             (e.g., em0), the name of a vale(4) port (e.g., valeSSS:PPP), the name of a netmap pipe or\n"
+"             monitor, or any valid netmap port name accepted by the nm_open library function, as docu-\n"
+"             mented in netmap(4) (NIOCREGIF section).\n"
+"\n"
+"     -f function\n"
+"             The function to be executed by pkt-gen.  Specify tx for transmission, rx for reception, ping\n"
+"             for client-side ping-pong operation, and pong for server-side ping-pong operation.\n"
+"\n"
+"     -n count\n"
+"             Number of iterations of the pkt-gen function (with 0 meaning infinite).  In case of tx or rx,\n"
+"             count is the number of packets to receive or transmit.  In case of ping or pong, count is the\n"
+"             number of ping-pong transactions.\n"
+"\n"
+"     -l pkt_size\n"
+"             Packet size in bytes excluding CRC.  If passed a second time, use random sizes larger or\n"
+"             equal than the second one and lower than the first one.\n"
+"\n"
+"     -b burst_size\n"
+"             Transmit or receive up to burst_size packets at a time.\n"
+"\n"
+"     -4      Use IPv4 addresses.\n"
+"\n"
+"     -6      Use IPv6 addresses.\n"
+"\n"
+"     -d dst_ip[:port[-dst_ip:port]]\n"
+"             Destination IPv4/IPv6 address and port, single or range.\n"
+"\n"
+"     -s src_ip[:port[-src_ip:port]]\n"
+"             Source IPv4/IPv6 address and port, single or range.\n"
+"\n"
+"     -D dst_mac\n"
+"             Destination MAC address in colon notation (e.g., aa:bb:cc:dd:ee:00).\n"
+"\n"
+"     -S src_mac\n"
+"             Source MAC address in colon notation.\n"
+"\n"
+"     -a cpu_id\n"
+"             Pin the first thread of pkt-gen to a particular CPU using pthread_setaffinity_np(3).  If more\n"
+"             threads are used, they are pinned to the subsequent CPUs, one per thread.\n"
+"\n"
+"     -c cpus\n"
+"             Maximum number of CPUs to use (0 means to use all the available ones).\n"
+"\n"
+"     -p threads\n"
+"             Number of threads to use.  By default, only a single thread is used to handle all the netmap\n"
+"             rings.  If threads is larger than one, each thread handles a single TX ring (in tx mode), a\n"
+"             single RX ring (in rx mode), or a TX/RX ring pair.  The number of threads must be less than or\n"
+"             equal to the number of TX (or RX) rings available in the device specified by interface.\n"
+"\n"
+"     -T report_ms\n"
+"             Number of milliseconds between reports.\n"
+"\n"
+"     -w wait_for_link_time\n"
+"             Number of seconds to wait before starting the pkt-gen function, useful to make sure that the\n"
+"             network link is up.  A network device driver may take some time to enter netmap mode, or to\n"
+"             create a new transmit/receive ring pair when netmap(4) requests one.\n"
+"\n"
+"     -R rate\n"
+"             Packet transmission rate.  Not setting the packet transmission rate tells pkt-gen to transmit\n"
+"             packets as quickly as possible.  On servers from 2010 onward netmap(4) is able to com-\n"
+"             pletely use all of the bandwidth of a 10 or 40Gbps link, so this option should be used unless\n"
+"             your intention is to saturate the link.\n"
+"\n"
+"     -X      Dump payload of each packet transmitted or received.\n"
+"\n"
+"     -H len  Add empty virtio-net-header with size 'len'.  Valid sizes are 0, 10 and 12.  This option is\n"
+"             only used with Virtual Machine technologies that use virtio as a network interface.\n"
+"\n"
+"     -P file\n"
+"             Load the packet to be transmitted from a pcap file rather than constructing it within\n"
+"             pkt-gen.\n"
+"\n"
+"     -z      Use random IPv4/IPv6 src address/port.\n"
+"\n"
+"     -Z      Use random IPv4/IPv6 dst address/port.\n"
+"\n"
+"     -N      Do not normalize units (i.e., use bps, pps instead of Mbps, Kpps, etc.).\n"
+"\n"
+"     -F num_frags\n"
+"             Send multi-slot packets, each one with num_frags fragments.  A multi-slot packet is repre-\n"
+"             sented by two or more consecutive netmap slots with the NS_MOREFRAG flag set (except for the\n"
+"             last slot).  This is useful to transmit or receive packets larger than the netmap buffer\n"
+"             size.\n"
+"\n"
+"     -M frag_size\n"
+"             In multi-slot mode, frag_size specifies the size of each fragment, if smaller than the packet\n"
+"             length divided by num_frags.\n"
+"\n"
+"     -I      Use indirect buffers.  It is only valid for transmitting on VALE ports, and it is implemented\n"
+"             by setting the NS_INDIRECT flag in the netmap slots.\n"
+"\n"
+"     -W      Exit immediately if all the RX rings are empty the first time they are examined.\n"
+"\n"
+"     -v      Increase the verbosity level.\n"
+"\n"
+"     -r      In tx mode, do not initialize packets, but send whatever the content of the uninitialized\n"
+"             netmap buffers is (rubbish mode).\n"
+"\n"
+"     -A      Compute mean and standard deviation (over a sliding window) for the transmit or receive rate.\n"
+"\n"
+"     -B      Take Ethernet framing and CRC into account when computing the average bps.  This adds 4 bytes\n"
+"             of CRC and 20 bytes of framing to each packet.\n"
+"\n"
+"     -C tx_slots[,rx_slots[,tx_rings[,rx_rings]]]\n"
+"             Configuration in terms of number of rings and slots to be used when opening the netmap port.\n"
+"             Such configuration has an effect on software ports created on the fly, such as VALE ports and\n"
+"             netmap pipes.  The configuration may consist of 1 to 4 numbers separated by commas: tx_slots,\n"
+"             rx_slots, tx_rings, rx_rings.  Missing numbers or zeroes stand for default values.  As an\n"
+"             additional convenience, if exactly one number is specified, then this is assigned to both\n"
+"             tx_slots and rx_slots.  If there is no fourth number, then the third one is assigned to both\n"
+"             tx_rings and rx_rings.\n"
+"\n"
+"     -o options		data generation options (parsed using atoi)\n"
+"				OPT_PREFETCH	1\n"
+"				OPT_ACCESS	2\n"
+"				OPT_COPY	4\n"
+"				OPT_MEMCPY	8\n"
+"				OPT_TS		16 (add a timestamp)\n"
+"				OPT_INDIRECT	32 (use indirect buffers)\n"
+"				OPT_DUMP	64 (dump rx/tx traffic)\n"
+"				OPT_RUBBISH	256\n"
+"					(send whatever the buffers contain)\n"
+"				OPT_RANDOM_SRC  512\n"
+"				OPT_RANDOM_DST  1024\n"
+"				OPT_PPS_STATS   2048\n"
+"				OPT_UPDATE_CSUM 4096\n"
+		     "",
 		cmd);
-
-	exit(0);
+	exit(errcode);
 }
 
-enum {
-	TD_TYPE_SENDER = 1,
-	TD_TYPE_RECEIVER,
-	TD_TYPE_OTHER,
-};
-
-static void
+static int
 start_threads(struct glob_arg *g) {
 	int i;
 
@@ -2195,51 +2625,65 @@ start_threads(struct glob_arg *g) {
 	 * using a single descriptor.
 	 */
 	for (i = 0; i < g->nthreads; i++) {
+		uint64_t seed = (uint64_t)time(0) | ((uint64_t)time(0) << 32);
 		t = &targs[i];
 
 		bzero(t, sizeof(*t));
 		t->fd = -1; /* default, with pcap */
 		t->g = g;
+		memcpy(t->seed, &seed, sizeof(t->seed));
 
 		if (g->dev_type == DEV_NETMAP) {
-			struct nm_desc nmd = *g->nmd; /* copy, we overwrite ringid */
-			uint64_t nmd_flags = 0;
-			nmd.self = &nmd;
+			int m = -1;
+
+			/*
+			 * if the user wants both HW and SW rings, we need to
+			 * know when to switch from NR_REG_ONE_NIC to NR_REG_ONE_SW
+			 */
+			if (g->orig_mode == NR_REG_NIC_SW) {
+				m = (g->td_type == TD_TYPE_RECEIVER ?
+						g->nmd->reg.nr_rx_rings :
+						g->nmd->reg.nr_tx_rings);
+			}
 
 			if (i > 0) {
+				int j;
 				/* the first thread uses the fd opened by the main
 				 * thread, the other threads re-open /dev/netmap
 				 */
-				if (g->nthreads > 1) {
-					nmd.req.nr_flags =
-						g->nmd->req.nr_flags & ~NR_REG_MASK;
-					nmd.req.nr_flags |= NR_REG_ONE_NIC;
-					nmd.req.nr_ringid = i;
+				t->nmd = nmport_clone(g->nmd);
+				if (t->nmd == NULL)
+					return -1;
+
+				j = i;
+				if (m > 0 && j >= m) {
+					/* switch to the software rings */
+					t->nmd->reg.nr_mode = NR_REG_ONE_SW;
+					j -= m;
 				}
+				t->nmd->reg.nr_ringid = j & NETMAP_RING_MASK;
 				/* Only touch one of the rings (rx is already ok) */
 				if (g->td_type == TD_TYPE_RECEIVER)
-					nmd_flags |= NETMAP_NO_TX_POLL;
+					t->nmd->reg.nr_flags |= NETMAP_NO_TX_POLL;
 
 				/* register interface. Override ifname and ringid etc. */
-				t->nmd = nm_open(t->g->ifname, NULL, nmd_flags |
-						NM_OPEN_IFNAME | NM_OPEN_NO_MMAP, &nmd);
-				if (t->nmd == NULL) {
-					D("Unable to open %s: %s",
-							t->g->ifname, strerror(errno));
-					continue;
+				if (nmport_open_desc(t->nmd) < 0) {
+					nmport_undo_prepare(t->nmd);
+					t->nmd = NULL;
+					return -1;
 				}
 			} else {
 				t->nmd = g->nmd;
 			}
 			t->fd = t->nmd->fd;
-
+			t->frags = g->frags;
 		} else {
 			targs[i].fd = g->main_fd;
 		}
 		t->used = 1;
 		t->me = i;
 		if (g->affinity >= 0) {
-			t->affinity = (g->affinity + i) % g->system_cpus;
+			t->affinity = (g->affinity + i) % g->cpus;
 		} else {
 			t->affinity = -1;
 		}
@@ -2258,6 +2702,7 @@ start_threads(struct glob_arg *g) {
 			t->used = 0;
 		}
 	}
+	return 0;
 }
 
 static void
@@ -2272,7 +2717,7 @@ main_thread(struct glob_arg *g)
 	prev.pkts = prev.bytes = prev.events = 0;
 	gettimeofday(&prev.t, NULL);
 	for (;;) {
-		char b1[40], b2[40], b3[40], b4[70];
+		char b1[40], b2[40], b3[40], b4[100];
 		uint64_t pps, usec;
 		struct my_ctrs x;
 		double abs;
@@ -2329,13 +2774,13 @@ main_thread(struct glob_arg *g)
 			ppsdev = sqrt(ppsdev);
 
 			snprintf(b4, sizeof(b4), "[avg/std %s/%s pps]",
-				 norm(b1, ppsavg), norm(b2, ppsdev));
+				 norm(b1, ppsavg, normalize), norm(b2, ppsdev, normalize));
 		}
 
 		D("%spps %s(%spkts %sbps in %llu usec) %.2f avg_batch %d min_space",
-			norm(b1, pps), b4,
-			norm(b2, (double)x.pkts),
-			norm(b3, (double)x.bytes*8),
+			norm(b1, pps, normalize), b4,
+			norm(b2, (double)x.pkts, normalize),
+			norm(b3, 1000000*((double)x.bytes*8+(double)x.pkts*g->framing)/usec, normalize),
 			(unsigned long long)usec,
 			abs, (int)cur.min_space);
 		prev = cur;
@@ -2357,7 +2802,7 @@ main_thread(struct glob_arg *g)
 		if (targs[i].used)
 			pthread_join(targs[i].thread, NULL); /* blocking */
 		if (g->dev_type == DEV_NETMAP) {
-			nm_close(targs[i].nmd);
+			nmport_close(targs[i].nmd);
 			targs[i].nmd = NULL;
 		} else {
 			close(targs[i].fd);
@@ -2389,25 +2834,26 @@ main_thread(struct glob_arg *g)
 	timersub(&toc, &tic, &toc);
 	delta_t = toc.tv_sec + 1e-6* toc.tv_usec;
 	if (g->td_type == TD_TYPE_SENDER)
-		tx_output(&cur, delta_t, "Sent");
-	else
-		tx_output(&cur, delta_t, "Received");
+		tx_output(g, &cur, delta_t, "Sent");
+	else if (g->td_type == TD_TYPE_RECEIVER)
+		tx_output(g, &cur, delta_t, "Received");
 }
 
 struct td_desc {
 	int ty;
-	char *key;
+	const char *key;
 	void *f;
+	int default_burst;
 };
 
 static struct td_desc func[] = {
-	{ TD_TYPE_SENDER,	"tx",		sender_body },
-	{ TD_TYPE_RECEIVER,	"rx",		receiver_body },
-	{ TD_TYPE_OTHER,	"ping",		pinger_body },
-	{ TD_TYPE_OTHER,	"pong",		ponger_body },
-	{ TD_TYPE_SENDER,	"txseq",	txseq_body },
-	{ TD_TYPE_RECEIVER,	"rxseq",	rxseq_body },
-	{ 0,			NULL,	NULL }
+	{ TD_TYPE_RECEIVER,	"rx",		receiver_body,	512},	/* default */
+	{ TD_TYPE_SENDER,	"tx",		sender_body,	512 },
+	{ TD_TYPE_OTHER,	"ping",		ping_body,	1 },
+	{ TD_TYPE_OTHER,	"pong",		pong_body,	1 },
+	{ TD_TYPE_SENDER,	"txseq",	txseq_body,	512 },
+	{ TD_TYPE_RECEIVER,	"rxseq",	rxseq_body,	512 },
+	{ 0,			NULL,		NULL, 		0 }
 };
 
 static int
@@ -2415,7 +2861,7 @@ tap_alloc(char *dev)
 {
 	struct ifreq ifr;
 	int fd, err;
-	char *clonedev = TAP_CLONEDEV;
+	const char *clonedev = TAP_CLONEDEV;
 
 	(void)err;
 	(void)dev;
@@ -2449,12 +2895,17 @@ tap_alloc(char *dev)
 		/* if a device name was specified, put it in the structure; otherwise,
 		* the kernel will try to allocate the "next" device of the
 		* specified type */
-		strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+		size_t len = strlen(dev);
+		if (len > IFNAMSIZ) {
+			D("%s too long", dev);
+			return -1;
+		}
+		memcpy(ifr.ifr_name, dev, len);
 	}
 
 	/* try to create the device */
 	if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
-		D("failed to to a TUNSETIFF: %s", strerror(errno));
+		D("failed to do a TUNSETIFF: %s", strerror(errno));
 		close(fd);
 		return err;
 	}
@@ -2467,9 +2918,9 @@ tap_alloc(char *dev)
 	D("new name is %s", dev);
 #endif /* linux */
 
-        /* this is the special file descriptor that the caller will use to talk
-         * with the virtual interface */
-        return fd;
+	/* this is the special file descriptor that the caller will use to talk
+	 * with the virtual interface */
+	return fd;
 }
 
 int
@@ -2483,12 +2934,17 @@ main(int arc, char **argv)
 
 	int ch;
 	int devqueues = 1;	/* how many device queues */
+	int wait_link_arg = 0;
+
+	int pkt_size_done = 0;
+
+	struct td_desc *fn = func;
 
 	bzero(&g, sizeof(g));
 
 	g.main_fd = -1;
-	g.td_body = receiver_body;
-	g.td_type = TD_TYPE_RECEIVER;
+	g.td_body = fn->f;
+	g.td_type = fn->ty;
 	g.report_interval = 1000;	/* report interval */
 	g.affinity = -1;
 	/* ip addresses can also be a range x.x.x.x-x.x.x.y */
@@ -2498,24 +2954,28 @@ main(int arc, char **argv)
 	g.dst_mac.name = "ff:ff:ff:ff:ff:ff";
 	g.src_mac.name = NULL;
 	g.pkt_size = 60;
-	g.burst = 512;		// default
+	g.pkt_min_size = 0;
 	g.nthreads = 1;
-	g.cpus = 1;		// default
+	g.cpus = 1;		/* default */
 	g.forever = 1;
 	g.tx_rate = 0;
 	g.frags = 1;
+	g.frag_size = (u_int)-1;	/* use the netmap buffer size by default */
 	g.nmr_config = "";
 	g.virt_header = 0;
-	g.wait_link = 2;
+	g.wait_link = 2;	/* wait 2 seconds for physical ports */
 
-	while ((ch = getopt(arc, argv, "46a:f:F:n:i:Il:d:s:D:S:b:c:o:p:"
-	    "T:w:WvR:XC:H:e:E:m:rP:zZA")) != -1) {
-		struct td_desc *fn;
+	while ((ch = getopt(arc, argv, "46a:f:F:Nn:i:Il:d:s:D:S:b:c:o:p:"
+	    "T:w:WvR:XC:H:rP:zZAhBM:")) != -1) {
 
 		switch(ch) {
 		default:
 			D("bad option %c %s", ch, optarg);
-			usage();
+			usage(-1);
+			break;
+
+		case 'h':
+			usage(0);
 			break;
 
 		case '4':
@@ -2526,6 +2986,10 @@ main(int arc, char **argv)
 			g.af = AF_INET6;
 			break;
 
+		case 'N':
+			normalize = 0;
+			break;
+
 		case 'n':
 			g.npackets = strtoull(optarg, NULL, 10);
 			break;
@@ -2539,6 +3003,10 @@ main(int arc, char **argv)
 			g.frags = i;
 			break;
 
+		case 'M':
+			g.frag_size = atoi(optarg);
+			break;
+
 		case 'f':
 			for (fn = func; fn->key; fn++) {
 				if (!strcmp(fn->key, optarg))
@@ -2553,7 +3021,7 @@ main(int arc, char **argv)
 			break;
 
 		case 'o':	/* data generation options */
-			g.options = atoi(optarg);
+			g.options |= atoi(optarg);
 			break;
 
 		case 'a':       /* force affinity */
@@ -2591,11 +3059,16 @@ main(int arc, char **argv)
 			break;
 
 		case 'I':
-			g.options |= OPT_INDIRECT;	/* XXX use indirect buffer */
+			g.options |= OPT_INDIRECT;	/* use indirect buffers */
 			break;
 
 		case 'l':	/* pkt_size */
-			g.pkt_size = atoi(optarg);
+			if (pkt_size_done) {
+				g.pkt_min_size = atoi(optarg);
+			} else {
+				g.pkt_size = atoi(optarg);
+				pkt_size_done = 1;
+			}
 			break;
 
 		case 'd':
@@ -2612,10 +3085,11 @@ main(int arc, char **argv)
 
 		case 'w':
 			g.wait_link = atoi(optarg);
+			wait_link_arg = 1;
 			break;
 
-		case 'W': /* XXX changed default */
-			g.forever = 0; /* do not exit rx even with no traffic */
+		case 'W':
+			g.forever = 0; /* exit RX with no traffic */
 			break;
 
 		case 'b':	/* burst */
@@ -2645,23 +3119,15 @@ main(int arc, char **argv)
 			g.options |= OPT_DUMP;
 			break;
 		case 'C':
+			D("WARNING: the 'C' option is deprecated, use the '+conf:' libnetmap option instead");
 			g.nmr_config = strdup(optarg);
 			break;
 		case 'H':
 			g.virt_header = atoi(optarg);
 			break;
-		case 'e': /* extra bufs */
-			g.extra_bufs = atoi(optarg);
-			break;
-		case 'E':
-			g.extra_pipes = atoi(optarg);
-			break;
 		case 'P':
 			g.packet_file = strdup(optarg);
 			break;
-		case 'm':
-			/* ignored */
-			break;
 		case 'r':
 			g.options |= OPT_RUBBISH;
 			break;
@@ -2674,26 +3140,45 @@ main(int arc, char **argv)
 		case 'A':
 			g.options |= OPT_PPS_STATS;
 			break;
+		case 'B':
+			/* raw packets have4 bytes crc + 20 bytes framing */
+			// XXX maybe add an option to pass the IFG
+			g.framing = 24 * 8;
+			break;
 		}
 	}
 
 	if (strlen(g.ifname) <=0 ) {
 		D("missing ifname");
-		usage();
+		usage(-1);
+	}
+
+	if (g.burst == 0) {
+		g.burst = fn->default_burst;
+		D("using default burst size: %d", g.burst);
 	}
 
 	g.system_cpus = i = system_ncpus();
 	if (g.cpus < 0 || g.cpus > i) {
 		D("%d cpus is too high, have only %d cpus", g.cpus, i);
-		usage();
+		usage(-1);
 	}
-D("running on %d cpus (have %d)", g.cpus, i);
+	D("running on %d cpus (have %d)", g.cpus, i);
 	if (g.cpus == 0)
 		g.cpus = i;
 
+	if (!wait_link_arg && !strncmp(g.ifname, "vale", 4)) {
+		g.wait_link = 0;
+	}
+
 	if (g.pkt_size < 16 || g.pkt_size > MAX_PKTSIZE) {
 		D("bad pktsize %d [16..%d]\n", g.pkt_size, MAX_PKTSIZE);
-		usage();
+		usage(-1);
+	}
+
+	if (g.pkt_min_size > 0 && (g.pkt_min_size < 16 || g.pkt_min_size > g.pkt_size)) {
+		D("bad pktminsize %d [16..%d]\n", g.pkt_min_size, g.pkt_size);
+		usage(-1);
 	}
 
 	if (g.src_mac.name == NULL) {
@@ -2707,14 +3192,14 @@ D("running on %d cpus (have %d)", g.cpus, i);
 	}
 	/* extract address ranges */
 	if (extract_mac_range(&g.src_mac) || extract_mac_range(&g.dst_mac))
-		usage();
+		usage(-1);
 	g.options |= extract_ip_range(&g.src_ip, g.af);
 	g.options |= extract_ip_range(&g.dst_ip, g.af);
 
 	if (g.virt_header != 0 && g.virt_header != VIRT_HDR_1
 			&& g.virt_header != VIRT_HDR_2) {
 		D("bad virtio-net-header length");
-		usage();
+		usage(-1);
 	}
 
     if (g.dev_type == DEV_TAP) {
@@ -2722,7 +3207,7 @@ D("running on %d cpus (have %d)", g.cpus, i);
 	g.main_fd = tap_alloc(g.ifname);
 	if (g.main_fd < 0) {
 		D("cannot open tap %s", g.ifname);
-		usage();
+		usage(-1);
 	}
 #ifndef NO_PCAP
     } else if (g.dev_type == DEV_PCAP) {
@@ -2732,7 +3217,7 @@ D("running on %d cpus (have %d)", g.cpus, i);
 	g.p = pcap_open_live(g.ifname, 256 /* XXX */, 1, 100, pcap_errbuf);
 	if (g.p == NULL) {
 		D("cannot open pcap on %s", g.ifname);
-		usage();
+		usage(-1);
 	}
 	g.main_fd = pcap_fileno(g.p);
 	D("using pcap on %s fileno %d", g.ifname, g.main_fd);
@@ -2740,19 +3225,13 @@ D("running on %d cpus (have %d)", g.cpus, i);
     } else if (g.dummy_send) { /* but DEV_NETMAP */
 	D("using a dummy send routine");
     } else {
-	struct nmreq base_nmd;
+	g.nmd = nmport_prepare(g.ifname);
+	if (g.nmd == NULL)
+		goto out;
 
-	bzero(&base_nmd, sizeof(base_nmd));
+	parse_nmr_config(g.nmr_config, &g.nmd->reg);
 
-	parse_nmr_config(g.nmr_config, &base_nmd);
-	if (g.extra_bufs) {
-		base_nmd.nr_arg3 = g.extra_bufs;
-	}
-	if (g.extra_pipes) {
-	    base_nmd.nr_arg1 = g.extra_pipes;
-	}
-
-	base_nmd.nr_flags |= NR_ACCEPT_VNET_HDR;
+	g.nmd->reg.nr_flags |= NR_ACCEPT_VNET_HDR;
 
 	/*
 	 * Open the netmap device using nm_open().
@@ -2761,32 +3240,30 @@ D("running on %d cpus (have %d)", g.cpus, i);
 	 * which in turn may take some time for the PHY to
 	 * reconfigure. We do the open here to have time to reset.
 	 */
-	g.nmd = nm_open(g.ifname, &base_nmd, 0, NULL);
-	if (g.nmd == NULL) {
-		D("Unable to open %s: %s", g.ifname, strerror(errno));
-		goto out;
-	}
-
+	g.orig_mode = g.nmd->reg.nr_mode;
 	if (g.nthreads > 1) {
-		struct nm_desc saved_desc = *g.nmd;
-		saved_desc.self = &saved_desc;
-		saved_desc.mem = NULL;
-		nm_close(g.nmd);
-		saved_desc.req.nr_flags &= ~NR_REG_MASK;
-		saved_desc.req.nr_flags |= NR_REG_ONE_NIC;
-		saved_desc.req.nr_ringid = 0;
-		g.nmd = nm_open(g.ifname, &base_nmd, NM_OPEN_IFNAME, &saved_desc);
-		if (g.nmd == NULL) {
-			D("Unable to open %s: %s", g.ifname, strerror(errno));
-			goto out;
+		switch (g.orig_mode) {
+		case NR_REG_ALL_NIC:
+		case NR_REG_NIC_SW:
+			g.nmd->reg.nr_mode = NR_REG_ONE_NIC;
+			break;
+		case NR_REG_SW:
+			g.nmd->reg.nr_mode = NR_REG_ONE_SW;
+			break;
+		default:
+			break;
 		}
+		g.nmd->reg.nr_ringid = 0;
 	}
+	if (nmport_open_desc(g.nmd) < 0)
+		goto out;
 	g.main_fd = g.nmd->fd;
-	D("mapped %dKB at %p", g.nmd->req.nr_memsize>>10, g.nmd->mem);
+	ND("mapped %luKB at %p", (unsigned long)(g.nmd->req.nr_memsize>>10),
+				g.nmd->mem);
 
 	if (g.virt_header) {
 		/* Set the virtio-net header length, since the user asked
-		 * for it explicitely. */
+		 * for it explicitly. */
 		set_vnet_hdr_len(&g);
 	} else {
 		/* Check whether the netmap port we opened requires us to send
@@ -2796,9 +3273,9 @@ D("running on %d cpus (have %d)", g.cpus, i);
 
 	/* get num of queues in tx or rx */
 	if (g.td_type == TD_TYPE_SENDER)
-		devqueues = g.nmd->req.nr_tx_rings;
+		devqueues = g.nmd->reg.nr_tx_rings + g.nmd->reg.nr_host_tx_rings;
 	else
-		devqueues = g.nmd->req.nr_rx_rings;
+		devqueues = g.nmd->reg.nr_rx_rings + g.nmd->reg.nr_host_rx_rings;
 
 	/* validate provided nthreads. */
 	if (g.nthreads < 1 || g.nthreads > devqueues) {
@@ -2806,21 +3283,31 @@ D("running on %d cpus (have %d)", g.cpus, i);
 		// continue, fail later
 	}
 
+	if (g.td_type == TD_TYPE_SENDER) {
+		int mtu = get_if_mtu(&g);
+
+		if (mtu > 0 && g.pkt_size > mtu) {
+			D("pkt_size (%d) must be <= mtu (%d)",
+				g.pkt_size, mtu);
+			return -1;
+		}
+	}
+
 	if (verbose) {
 		struct netmap_if *nifp = g.nmd->nifp;
-		struct nmreq *req = &g.nmd->req;
+		struct nmreq_register *req = &g.nmd->reg;
 
-		D("nifp at offset %d, %d tx %d rx region %d",
+		D("nifp at offset %"PRIu64" ntxqs %d nrxqs %d memid %d",
 		    req->nr_offset, req->nr_tx_rings, req->nr_rx_rings,
-		    req->nr_arg2);
-		for (i = 0; i <= req->nr_tx_rings; i++) {
+		    req->nr_mem_id);
+		for (i = 0; i < req->nr_tx_rings + req->nr_host_tx_rings; i++) {
 			struct netmap_ring *ring = NETMAP_TXRING(nifp, i);
-			D("   TX%d at 0x%p slots %d", i,
+			D("   TX%d at offset %p slots %d", i,
 			    (void *)((char *)ring - (char *)nifp), ring->num_slots);
 		}
-		for (i = 0; i <= req->nr_rx_rings; i++) {
+		for (i = 0; i < req->nr_rx_rings + req->nr_host_rx_rings; i++) {
 			struct netmap_ring *ring = NETMAP_RXRING(nifp, i);
-			D("   RX%d at 0x%p slots %d", i,
+			D("   RX%d at offset %p slots %d", i,
 			    (void *)((char *)ring - (char *)nifp), ring->num_slots);
 		}
 	}
@@ -2845,7 +3332,7 @@ D("running on %d cpus (have %d)", g.cpus, i);
 	/* Exit if something went wrong. */
 	if (g.main_fd < 0) {
 		D("aborting");
-		usage();
+		usage(-1);
 	}
     }
 
@@ -2870,16 +3357,16 @@ D("running on %d cpus (have %d)", g.cpus, i);
 		int lim = (g.tx_rate)/300;
 		if (g.burst > lim)
 			g.burst = lim;
-		if (g.burst < g.frags)
-			g.burst = g.frags;
+		if (g.burst == 0)
+			g.burst = 1;
 		x = ((uint64_t)1000000000 * (uint64_t)g.burst) / (uint64_t) g.tx_rate;
 		g.tx_period.tv_nsec = x;
 		g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000;
 		g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000;
 	}
 	if (g.td_type == TD_TYPE_SENDER)
-	    D("Sending %d packets every  %ld.%09ld s",
-			g.burst, g.tx_period.tv_sec, g.tx_period.tv_nsec);
+	    D("Sending %d packets every  %jd.%09ld s",
+			g.burst, (intmax_t)g.tx_period.tv_sec, g.tx_period.tv_nsec);
 	/* Install ^C handler. */
 	global_nthreads = g.nthreads;
 	sigemptyset(&ss);
@@ -2888,7 +3375,8 @@ D("running on %d cpus (have %d)", g.cpus, i);
 	if (pthread_sigmask(SIG_BLOCK, &ss, NULL) < 0) {
 		D("failed to block SIGINT: %s", strerror(errno));
 	}
-	start_threads(&g);
+	if (start_threads(&g) < 0)
+		return 1;
 	/* Install the handler and re-enable SIGINT for the main thread */
 	memset(&sa, 0, sizeof(sa));
 	sa.sa_handler = sigint_h;
diff --git a/apps/tlem/GNUmakefile b/apps/tlem/GNUmakefile
index cbd180b00..611c1e42d 100644
--- a/apps/tlem/GNUmakefile
+++ b/apps/tlem/GNUmakefile
@@ -9,12 +9,13 @@ SRCDIR ?= ../..
 VPATH = $(SRCDIR)/apps/tlem
 
 NO_MAN=
-CFLAGS = -O2 -pipe
-CFLAGS += -Werror -Wall -Wunused-function
-CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include
+CFLAGS = -O2 -pipe -g
+CFLAGS += -Werror -Wall -Wunused-function -Wno-address-of-packed-member
+CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include -I$(SRCDIR)/libnetmap
 CFLAGS += -Wextra
 
-LDLIBS += -lpthread
+LDFLAGS += -L $(BUILDDIR)/build-libnetmap
+LDLIBS += -lnetmap -lpthread
 ifeq ($(shell uname),Linux)
 	LDLIBS += -lrt	# on linux
 endif
diff --git a/apps/tlem/tlem.8 b/apps/tlem/tlem.8
index 1f8e4e83c..fa8c9841c 100644
--- a/apps/tlem/tlem.8
+++ b/apps/tlem/tlem.8
@@ -22,9 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD$
-.\"
-.Dd February 16, 2016
+.Dd February 13, 2020
 .Dt TLEM 1
 .Os
 .Sh NAME
@@ -38,11 +36,19 @@
 .Op Fl B Ar bandwidth
 .Op Fl D Ar delay
 .Op Fl L Ar loss
-.Op Fl Q Ar queue size
+.Op Fl R Ar reordering
+.Op Fl Q Ar queue-size
 .Op Fl C Ar cpu-placement
-.Op Fl b Ar batch size
+.Op Fl G Ar gateway
+.Op Fl b Ar batch-size
 .Op Fl w Ar wait-link
+.Op Fl s Ar session-name
+.Op Fl a
 .Op Fl v
+.Op Fl q
+.Op Fl r
+.Op Fl M Ar max-bw Ns Cm , Ns Ar max-delay Ns Cm , Ns Ar max-hold
+.El
 .Sh DESCRIPTION
 .Nm
 implements a high speed bidirectional link emulator between netmap ports,
@@ -88,7 +94,7 @@ specified once (in which case they affect both directions),
 or twice (once per direction).
 .Bl -tag -width Ds
 .It Fl i Ar port
-Name of the netmap port. It must be supplied exactly twice to indentify
+Name of the netmap port. It must be supplied exactly twice to identify
 the two ports that must be interconnected.
 Any netmap port type (physical interface, VALE switch, pipe, monitor port...)
 can be used.
@@ -104,7 +110,7 @@ with reference to the actual packet size (excluding CRC and framing).
 .Cm ether
 indicates that the ethernet framing (160 bits) and CRC (32 bits)
 will be included in the computation of the packet size.
-.It Fl D Ar dt | Cm constant, Ns Ar dt | Cm uniform, Ns Ar dmin,dmax | Cm exp, Ar dmin,davg
+.It Fl D Ar dt | Cm constant, Ns Ar dt | Cm uniform, Ns Ar dmin,dmax | Cm exp, Ns Ar dmin,davg
 Additional delay in transmission, with
 constant, uniform or exponential distribution, defaults to 0.
 .Ar dt, dmin, dmax, avg
@@ -117,28 +123,70 @@ Optional packet or bit error rate, defaults to 0.
 Simulates packet or bit errors, causing offending packets to be dropped.
 .Ar x
 is a floating point number indicating the packet or bit error rate.
+.It Fl R Cm const, Ns Ar p, Ns Ar t
+Optional packet reordering, defaults to none.
+With probability
+.Ar p
+incoming packets are hold for the given
+.Ar t
+amount of time. The probability and time are expressed as in
+the loss and delay arguments.
 .It Fl Q Ar size
 Queue size,
 .Ar size
-is a number optionally folllowed by k, K, m, M, g, G to specify
+is a number optionally followed by k, K, m, M, g, G to specify
 the queue size in bytes, Kilobytes, Megabytes, Gigabytes.
 The queue is used to buffer incoming packets before bandwidth
 limitations are applied.
 .It Fl C Ar a Ns Op , Ns Ar b Ns Op , Ns Ar c,d
 Indicates the cores on which the four threads should be placed.
 One, two or four values can be specified.
+.It Fl G Ar ipv4-address
+Indicates the optional default gateways to be used in route-mode.
 .It Fl w Ar wait-link
 indicates the number of seconds to wait before transmitting.
 It defaults to 2, and may be useful when talking to physical
 ports to let link negotiation complete before starting transmission.
+.It Fl s Ar session-file
+Enables client/server mode. The first instance of
+.Nm
+that successfully creates and/or locks the
+.Ar session-file
+becomes the server. Other
+.Nm
+instances that use the same
+.Ar session-file
+do not start new emulations, but rather send their parameters
+to the server. This feature can be used to dynamically change the
+emulation parameters of a running emulation. Please note that the
+internal buffers are not re-allocated, and therefore the dynamic emulated
+delay and bandwidth can never exceed the values used initially by the
+server. Alternatively, the
+.Fl M
+option can be used when starting the server to set the maximum bandwidth, delay
+and hold-time that can be accepted in client requests.
+.It Fl a
+Only useful in client/server mode. Ask the server to shutdown
+and wait until it terminates.
 .It Fl v
-Enable verbose mode
+Increase verbosity.
+.It Fl q
+Decrease verbosity.
 .It Fl b Ar batch-size
 Maximum batch size to use during transmissions.
 .Nm
 normally transmits packets one at a time, but it may use
 larger batches, up to the value specified with this option,
 when running at high rates.
+.It Fl r
+Enable route-mode.
+.It Fl M Ar max-bw Ns Cm , Ns Ar max-delay Ns Cm , Ns Ar max-hold
+
+Set the maximum bandwidth, delay and packet-reordering
+hold-time. The parameters are only meaningful for the
+server process when operating in client/server mode (see the
+.Fl s
+option).
 .El
 .Sh OPERATION
 .Nm
@@ -156,6 +204,21 @@ computes the transmit time applying the additional delay.
 Packets annotated with their transmit time are copied in
 a large in-memory buffer. The output thread spins on the buffer,
 doing short sleeps, until packets reach their transmit time.
+.Sh ROUTE-MODE
+In route-mode
+.Nm
+operates as an IPv4 router between the two subnets at its ends,
+replying to and sending the necessary ARP messages and updating
+the destination MAC addresses. The IP addresses and subnets are
+obtained from the ports, so this mode cannot be used with
+software-only netmap ports like ephemeral VALE ports and pipes.
+.Pp
+There are some limitations: unresolved destinations are sent as broadcasts
+until resolution; packets destined to the same subnet as their incoming
+port are dropped; TTL is not decremented.  Each subnet may also optionally
+have a default gateway: for each direction, incoming packets not destined
+to either of the two known subnets are sent to the default gateway of
+the output port, if specified, and dropped otherwise.
 .Sh PERFORMANCE
 We have measured speeds in excess of 20 Mpps and 40 Gbit/s per
 direction on a modern i7 CPU with 4 cores.  The accuracy in delays
@@ -163,7 +226,6 @@ is in the order of 30-50us provided that C states higher than C1
 are disabled, and the CPU clock is set to the maximum speed.
 Performance depends heavily on memory speed and suitable
 NICs with native netmap drivers. See the paper below for more details.
-of good network interf
 .Sh SEE ALSO
 .Pa http://info.iet.unipi.it/~luigi/netmap/
 .Pp
@@ -178,7 +240,10 @@ http://info.iet.unipi.it/~luigi/research.html
 has been written by
 .An Luigi Rizzo
 at the Universita` di Pisa, Italy.
+Route mode and client/server operation has been added by Giuseppe Lettieri
+at the Univerista` di Pisa, Italy.
 .Pp
 This work has received funding from the European
 Union's Horizon 2020 research and innovation programme
-2014-2018 under grant agreement No. 644866.
+2014-2018 under grant agreement No. 644866, and from
+East Cost Datacom Inc., Rockledge, FL, USA.
diff --git a/apps/tlem/tlem.c b/apps/tlem/tlem.c
index ec9789b41..df9643b52 100644
--- a/apps/tlem/tlem.c
+++ b/apps/tlem/tlem.c
@@ -1,3 +1,4 @@
+/* vim: set shiftwidth=4 softtabstop=4 :*/
 /*
  * Copyright (C) 2016 Universita` di Pisa. All rights reserved.
  *
@@ -23,6 +24,8 @@
  * SUCH DAMAGE.
  */
 
+#define WITH_MAX_LAG
+
 #if 0 /* COMMENT */
 
 This program implements TLEM, a bandwidth and delay emulator between two
@@ -49,7 +52,7 @@ The producer can either wait for traffic using a blocking poll(),
 or periodically check the input around short usleep().
 The latter mechanism is the preferred one as it allows a controlled
 latency with a low interrupt load and a modest system load.
-
+/
 The queue is sized so that overflows can occur only if the consumer
 is severely backlogged, hence the only appropriate option is drop
 traffic rather than wait for space.  The case of an empty queue is
@@ -61,7 +64,7 @@ In order to get good and predictable performance, it is important
 that threads are pinned to a single core, and it is preferable that
 prod() and cons() for each direction share the cache as much as possible.
 Putting them on two hyperthreads of the same core seems to give
-good results but that shoud be investigated further.
+good results but that should be investigated further.
 
 It also seems useful to use a scheduler (SCHED_FIFO or SCHED_RR)
 that gives more predictable cycles to the CPU, as well as try
@@ -87,7 +90,7 @@ prod()
 			in nanoseconds. A batch of packets may
 			have the same value q->prod_now
 
-    Four functions are then called in sequence:
+    Five functions are then called in sequence:
 
     q->c_loss (set with the -L command line option) decides
     	whether the packet should be dropped before even queuing.
@@ -95,6 +98,12 @@ prod()
 	The function is supposed to set q->c_drop = 1 if the
 	packet should be dropped, or leave it to 0 otherwise.
 
+    q-c_reorder (set with the -R command line option) decides
+        whether the packet should be temporary hold to emulate
+	packet reordering. To hold a packet, it should set
+	q->cur_hold_delay to a non-zero value. The packet will
+	reenter the stream once the cur_hold_delay has expired.
+
     no_room (not configurable) checks whether there is space
     	in the queue, enforcing both the queue size set with -Q
 	and the space allocated for the delay line.
@@ -125,56 +134,110 @@ prod()
 #define NED(_fmt, ...)	do {} while (0)
 #define ED(_fmt, ...)						\
 	do {							\
+	   if (verbose > 0) {					\
 		struct timeval _t0;				\
 		gettimeofday(&_t0, NULL);			\
 		fprintf(stderr, "%03d.%03d [%5d] \t" _fmt "\n", \
 		(int)(_t0.tv_sec % 1000), (int)_t0.tv_usec/1000, \
 		__LINE__, ##__VA_ARGS__);     \
+	   }							\
 	} while (0)
 
 #define _GNU_SOURCE	// for CPU_SET() etc
 #include 
-#define NETMAP_WITH_LIBS
-#include 
+#include 
+#include 
 #include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
 
 
-int verbose = 0;
+int verbose = 1;
 
 static int do_abort = 0;
 
+#ifdef linux
+static int latency_fd = -1;
+static void latency_reduction_start(void)
+{
+    uint32_t target = 0;
+
+    if (latency_fd >= 0)
+        return;
+    latency_fd = open("/dev/cpu_dma_latency", O_RDWR);
+    if (latency_fd < 0) {
+        ED("WARNING: failed to setup low latency: %s", strerror(errno));
+        return;
+    }
+    if (write(latency_fd, &target, sizeof(target)) < 0) {
+        ED("WARNING: failed to setup low latency: %s", strerror(errno));
+    }
+    ED("latency reduction started");
+}
+static void latency_reduction_stop(void)
+{
+    if (latency_fd >= 0)
+        close(latency_fd);
+}
+#else
+#define latency_reduction_start()
+#define latency_reduction_stop()
+#endif /* linux */
+
 #include 
 #include 
+#include 
 #include 
 #include 
 
+// for route-mode
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
 #include  // setpriority
 
 #ifdef __FreeBSD__
+#include 	/* sokcaddr_dl */
 #include  /* pthread w/ affinity */
 #include  /* cpu_set */
+#define MAP_HUGETLB 0	/* not supported */
 #endif /* __FreeBSD__ */
 
 #ifdef linux
 #define cpuset_t        cpu_set_t
+#include 
+#ifndef MAP_HUGETLB
+#define MAP_HUGETLB 0x40000
+#endif
 #endif
 
+#include "ctrs.h"	/* norm() */
+
 #ifdef __APPLE__
 #define cpuset_t        uint64_t        // XXX
 static inline void CPU_ZERO(cpuset_t *p)
 {
-        *p = 0;
+    *p = 0;
 }
 
 static inline void CPU_SET(uint32_t i, cpuset_t *p)
 {
-        *p |= 1<< (i & 0x3f);
+    *p |= 1<< (i & 0x3f);
 }
 
 #define pthread_setaffinity_np(a, b, c) ((void)a, 0)
 #define sched_setscheduler(a, b, c)	(1) /* error */
 #define clock_gettime(a,b)      \
-        do {struct timespec t0 = {0,0}; *(b) = t0; } while (0)
+	do {struct timespec t0 = {0,0}; *(b) = t0; } while (0)
 
 #define	_P64	unsigned long
 #endif
@@ -187,6 +250,25 @@ static inline void CPU_SET(uint32_t i, cpuset_t *p)
 #define	_P64	uint64_t
 #endif /* print stuff */
 
+#define	MY_CACHELINE	(128ULL)
+#define ALIGN_CACHE	__attribute__ ((aligned (MY_CACHELINE)))
+
+struct stats {
+	uint64_t	packets;
+	uint64_t	bytes;
+	uint64_t	drop_packets;
+	uint64_t	drop_bytes;
+	uint64_t	reorder_packets;
+	uint64_t	reorder_bytes;
+} ALIGN_CACHE;
+
+/* external configuration for each impairment (bw, delay, loss, reorder, ...) */
+struct _ec {
+        uint8_t         ec_valid;       /* 1 iff the other fields are valid */
+        uint8_t         ec_index;       /* impairment sub-type */
+        uint16_t        ec_datasz;      /* size of the parameters */
+        uint32_t        ec_dataoff;     /* offset of the parameters */
+};
 
 struct _qs;	/* forward */
 /*
@@ -203,11 +285,61 @@ struct _cfg {
 
     const char *optarg;	/* command line argument. Initial value is the error message */
     /* placeholders for common values */
-    void *arg;		/* allocated memory if any */
-    int arg_len;	/* size of *arg in case a realloc is needed */
-    uint64_t d[16];	/* static storage for simple cases */
+    void *arg;		/* allocated memory */
+    struct _ec *ec;     /* external configuration */
+    uint64_t def_qsize; /* default qsize (for bw configs) */
+};
+
+/* impairments */
+enum {
+	I_DELAY = 0,
+	I_BW,
+	I_LOSS,
+	I_REORDER,
+	I_NUM
+};
+
+/* configuration instance. There may be one or more of these
+ * for direction. One of them is the active one, currently
+ * used by the server. Clients prepare an instance not in use,
+ * then make it active when ready.
+ */
+struct _eci {
+        struct _ec      ec_imp[I_NUM];
+	uint64_t	ec_delay_offset;
+	int		ec_allow_drop;
+	uint32_t	ec_qsize;
+#define EC_DATASZ       (1U << 17)
+        char            ec_data[EC_DATASZ];
 };
 
+/* set of configuration instances. One set per direction */
+struct _ecs {
+        /* fields only written by the server */
+	uint64_t	max_bps;	/* bits per second */
+	uint64_t	max_delay;	/* nanoseconds */
+	uint64_t	max_hold_delay; /* nanoseconds */
+        /* fields written by the server on startup, and then by
+         * the clients in mutual exclusion among themselves.
+         * The communication among the clients and the server
+         * is lockless, and based on ordered updates to the
+         * active field.
+         */
+        volatile uint64_t active; /* active configuration instance */
+#define EC_NINST      2
+        struct _eci    instances[EC_NINST];
+};
+
+/* contents of the external configuration */
+struct _ecf {
+#define EC_NOPTS      2
+	struct stats	stats[2 * EC_NOPTS];
+        uint32_t        version;
+#define EC_VERSION    1
+        struct _ecs     sets[EC_NOPTS];
+};
+#define EC_HDRSZ (offsetof(struct _ecf, sets))
+
 /*
  *
 A packet in the queue is q_pkt plus the payload.
@@ -268,16 +400,28 @@ To simulate bandwidth limitations efficiently, the producer has a second
 pointer, prod_tail_1, used to check for expired packets. This is done lazily.
 
  */
+
+/* for packets hold for reorderind we only record their size and
+ * hold time.
+ */
+struct h_pkt {
+	uint64_t	pktlen;
+	uint64_t	releasetime;
+};
+
 /*
  * When sizing the buffer, we must assume some value for the bandwidth.
  * INFINITE_BW is supposed to be faster than what we support
  */
 #define INFINITE_BW	(200ULL*1000000*1000)
-#define	MY_CACHELINE	(128ULL)
-#define PKT_PAD		(32)	/* padding on packets */
+#define PKT_PAD		(8)	/* padding on packets */
 #define MAX_PKT		(9200)	/* max packet size */
+#define MAX_FRAGS       (1000)  /* max number of fragments */
 
-#define ALIGN_CACHE	__attribute__ ((aligned (MY_CACHELINE)))
+struct _frag {
+       char           *buf;
+       unsigned int    len;
+};
 
 struct _qs { /* shared queue */
 	uint64_t	t0;	/* start of times */
@@ -285,31 +429,29 @@ struct _qs { /* shared queue */
 	uint64_t 	buflen;	/* queue length */
 	char *buf;
 
+        struct _ecs    *ec;             /* external configuration set */
+        uint32_t        ec_active;      /* active instance in the set */
+        size_t          ec_nta[EC_NINST]; /* allocated byes in each instance */
 	/* the queue has at least 1 empty position */
-	uint64_t	max_bps;	/* bits per second */
-	uint64_t	max_delay;	/* nanoseconds */
 	uint64_t	qsize;	/* queue size in bytes */
 
 	/* handlers for various options */
-	struct _cfg	c_delay;
-	struct _cfg	c_bw;
-	struct _cfg	c_loss;
+	struct _cfg	c_imp[I_NUM];
 
 	/* producer's fields */
-	uint64_t	tx ALIGN_CACHE;	/* tx counter */
-	uint64_t	prod_tail_1;	/* head of queue */
+	uint64_t	prod_tail_1 ALIGN_CACHE; /* head of queue */
 	uint64_t	prod_queued;	/* queued bytes */
 	uint64_t	prod_head;	/* cached copy */
 	uint64_t	prod_tail;	/* cached copy */
 	uint64_t	prod_now;	/* most recent producer timestamp */
-	uint64_t	prod_drop;	/* drop packet count */
 	uint64_t	prod_max_gap;	/* rx round duration */
+	unsigned short	prod_seed[3];
+	struct stats	*txstats;
 
 	/* parameters for reading from the netmap port */
-	struct nm_desc *src_port;		/* netmap descriptor */
+	struct nmport_d *src_port;		/* netmap descriptor */
 	const char *	prod_ifname;	/* interface name */
-	struct netmap_ring *rxring;	/* current ring being handled */
-	uint32_t	si;		/* ring index */
+	struct netmap_ring *rxring;	/* source netmap ring */
 	int		burst;
 	uint32_t	rx_qmax;	/* stats on max queued */
 
@@ -331,6 +473,8 @@ struct _qs { /* shared queue */
 	/* producer's fields controlling the queueing */
 	char *		cur_pkt;	/* current packet being analysed */
 	uint32_t	cur_len;	/* length of current packet */
+        struct _frag    cur_frags[MAX_FRAGS];
+        int             cur_nfrags;
 
 	int		cur_drop;	/* 1 if current  packet should be dropped. */
 		/*
@@ -354,25 +498,647 @@ struct _qs { /* shared queue */
 		 * The code makes sure that there is no reordering and possibly
 		 * bumps the output time as needed.
 		 */
+	int		allow_drop;	/* improve delay accuracy by dropping packets */
+		/*
+		 * by default, TLEM adjusts the cur_delay of each packet to
+		 * avoid reordering, thus sacrificing the delay emulation
+		 * accuracy.  In 'allow_drop' mode we try to improve the
+		 * accuracy by dropping the packets that should be reordered,
+		 * instead of queueing them.
+		 */
+	int		reuse_delay;	/* reuse the last computed delay */
+		/*
+		 * In 'allow_drop' mode we reuse the last computed delay until
+		 * we find a packet that can be sent in order. If we kept
+		 * recomputing the delay, instead, we would skew the
+		 * distribution towards larger values.
+		 */
+	uint64_t	delay_offset;	/* to be subtracted from cur_delay */
+
+	/* producers's fields for reordering */
+	uint64_t	cur_hold_delay; /* reordering delay (ns) from c_reorder.run() */
+	uint64_t	hold_tail, hold_head; /* pointers in the reorder queue */
+	char	       *hold_buf;	/* the reorder queue */
+	uint64_t	hold_buflen;	/* and its size */
+	uint64_t	hold_next_rt;	/* release time of the first hold packet */
+	uint64_t	hold_tail_rt;	/* release time of the last hold packet */
+	int		hold_release;   /* there are packets ready to be released */
 
 
 	/* consumer's fields */
-	const char *		cons_ifname;
-	uint64_t rx ALIGN_CACHE;	/* rx counter */
 //	uint64_t	cons_head;	/* cached copy */
 //	uint64_t	cons_tail;	/* cached copy */
-	uint64_t	cons_now;	/* most recent producer timestamp */
+	uint64_t	cons_now ALIGN_CACHE;	/* most recent producer timestamp */
 	uint64_t	cons_lag;	/* tail - head */
 	uint64_t	rx_wait;	/* stats */
+	const char *	cons_ifname;
+	struct stats	*rxstats;
 
 	/* shared fields */
 	volatile uint64_t tail ALIGN_CACHE ;	/* producer writes here */
 	volatile uint64_t head ALIGN_CACHE ;	/* consumer reads from here */
 };
 
+static int
+ec_next(int i)
+{
+    return (i + 1) % EC_NINST;
+}
+
+/* if fname is NULL tlem will run standalone, i.e., in server mode
+ * with no possibility for clients to change the configuration.
+ * Otherwise, the first tlem instance that successfully locks the
+ * first four bytes of the configuration file becomes the server.
+ * Clients write-lock the rest of the file, to guarantee mutual
+ * exclusive configuration updates among them.
+ */
+static int ecf_fd = -1;
+static struct _ecf *
+ec_map(const char *fname, int *server)
+{
+    size_t sz;
+    struct _ecf *ecf;
+    int mmap_flags;
+
+    sz = sizeof(struct _ecf);
+    if (fname) {
+        ecf_fd = open(fname, O_RDWR | O_CREAT, 0664);
+        if (ecf_fd < 0) {
+            ED("cannot open %s: %s", fname, strerror(errno));
+            return NULL;
+        }
+        if (ftruncate(ecf_fd, sz) < 0) {
+            ED("cannot truncate(%s, %zu): %s",
+                    fname, sz, strerror(errno));
+            return NULL;
+        }
+        mmap_flags = MAP_SHARED;
+
+        /* try to lock the entire file.
+         * If we succeed, we are the server.
+         */
+        if (lockf(ecf_fd, F_TLOCK, 0) == 0) {
+            *server = 1;
+            /* we will release the non-header part when
+             * we are done with the initial configuration
+             */
+        } else {
+            if (errno != EACCES && errno != EAGAIN) {
+                ED("failed to lock %s: %s", fname, strerror(errno));
+                return NULL;
+            }
+            /* we are a client. Skip the header and wait
+             * for exclusive access to the rest.
+             */
+            *server = 0;
+            if (lseek(ecf_fd, EC_HDRSZ, SEEK_SET) < 0) {
+                ED("failed to skip the header of %s: %s",
+                        fname, strerror(errno));
+                return NULL;
+            }
+            if (lockf(ecf_fd, F_LOCK, 0) < 0) {
+                ED("failed to lock the client area of %s: %s",
+                        fname, strerror(errno));
+                return NULL;
+            }
+        }
+    } else {
+        mmap_flags = MAP_ANONYMOUS | MAP_PRIVATE;
+    }
+    ecf = mmap(NULL, sz,
+            PROT_READ | PROT_WRITE,
+            mmap_flags, ecf_fd, 0);
+    /* errors are all fatal. Locks will be released on exit */
+    if (ecf == MAP_FAILED) {
+        D("cannot mmap %s: %s", fname, strerror(errno));
+        return NULL;
+    }
+    if (*server) {
+        memset(ecf, 0, sz);
+        ecf->version = EC_VERSION;
+    } else {
+        if (ecf->version != EC_VERSION) {
+            ED("Expected version %d, got %d",
+                    EC_VERSION, ecf->version);
+            return NULL;
+        }
+    }
+    return ecf;
+}
+
+static int
+ec_waitterminate()
+{
+    if (ecf_fd < 0)
+        return 0;
+    if (lseek(ecf_fd, 0, SEEK_SET) < 0) {
+        ED("failed to rewind the session file: %s",
+                strerror(errno));
+        return 1;
+    }
+    if (lockf(ecf_fd, F_LOCK, 0) < 0) {
+        ED("failed to lock the session file %s",
+                strerror(errno));
+        return 1;
+    }
+    return 0;
+}
+
+static int
+ec_allowclients()
+{
+    if (ecf_fd < 0) {
+        /* OK, standalone mode */
+        return 0;
+    }
+    if (lseek(ecf_fd, EC_HDRSZ, SEEK_SET) < 0) {
+        ED("failed to skip the header: %s",
+                strerror(errno));
+        return 1;
+    }
+    if (lockf(ecf_fd, F_ULOCK, 0) < 0) {
+        ED("failed to unlock the client area: %s",
+                strerror(errno));
+        return 1;
+    }
+    return 0;
+}
+
+static void ec_activate(struct _qs *q); // forward
+static int
+ec_init(struct _qs *q, struct _ecs *ec, int server)
+{
+    int i;
+    struct _eci *ci;
+
+    q->ec = ec;
+    for (i = 0; i < EC_NINST; i++)
+	q->ec_nta[i] = 0;
+    q->ec_active = server ? 0 : ec_next(q->ec->active);
+    ci = &q->ec->instances[q->ec_active];
+    for (i = 0; i < I_NUM; i++) {
+	ci->ec_imp[i].ec_valid = 0;
+	q->c_imp[i].ec = &ci->ec_imp[i];
+    }
+    return 0;
+}
+
+static void
+ec_terminate(struct _ecs *ec)
+{
+    ec->active = EC_NINST;
+}
+
+/* allocate sz bytes in the non-active config instance */
+static void *
+ec_alloc(struct _qs *q, struct _ec *ec, size_t sz)
+{
+    int i = q->ec_active;
+    struct _eci *a = &q->ec->instances[i];
+    size_t nta = q->ec_nta[i];
+
+    if (sz + nta >= EC_DATASZ) {
+        ED("no room for %zu bytes in external config instance %d", sz, i);
+        return NULL;
+    }
+    q->ec_nta[i] += sz;
+    ec->ec_dataoff = nta;
+    ec->ec_datasz = sz;
+    return &a->ec_data[nta];
+}
+
+static inline void
+ec_checkactive(struct _qs *q)
+{
+    uint64_t i = q->ec->active;
+    if (unlikely(i >= EC_NINST)) {
+        /* setting ec_active to an out-of-bounds value is
+         * interpreted as an exit request
+         */
+        do_abort = 1;
+        return;
+    }
+    if (i != q->ec_active) {
+        asm volatile("" ::: "memory");
+        __sync_synchronize();
+        ND("switching to configuration %i", i);
+        q->ec_active = i;
+        ec_activate(q);
+    }
+}
+
+static void
+ec_switchactive(struct _qs *q)
+{
+    if (q->ec_active != q->ec->active) {
+        asm volatile("" ::: "memory");
+        __sync_synchronize();
+        ND("switching to configuration %i", q->ec_active);
+        q->ec->active = q->ec_active;
+    }
+}
+
+/* route-mode data structures and helper functions
+ *
+ * In route-mode TLEM acts as a router between the two subnets at its ends.
+ * This is implemented as follows:
+ * - there are two arp tables, one for each subnet
+ * - arp tables are private to the cons() process
+ * - the prod() processes extract the relevant info from any received ARP
+ *   message and pass them down to the cons() process insisting on the same
+ *   port, as illustrated in the following diagram:
+ *
+ *         |---> prod1 --------------------------> cons1 --->|
+ *         |       |                                 ^       |
+ *         | ARP req/repl info                       |       |
+ *         |       |                                 |       |
+ * port1<->+       |                                 |       +<->port2
+ *         |       |                                 |       |
+ *         |       |                       ARP req/repl info |
+ *         |       V                                 |       |
+ *         |<--- cons2 <-------------------------- prod2 <---|
+ *
+ * - the cons() processes react to these infos by sending ARP replies/
+ *   updating their private ARP table as needed
+ * - the cons() processes change the outgoing packets destination addresses
+ *   before injecting them, sending ARP requests when needed.
+ * - TTL decrement is not implemented, for performance reasons.
+ *
+ * Delegating all the heavy work to the cons() has the advantage that the only
+ * new inter-thread interactions are prod1->cons1 and prod2->cons2; these can
+ * be implemented by lockless and barrier-less mailboxes. Since writes into the
+ * mailbox are rare, the consumer can bring it into its local cache and poll it
+ * as often as needed, without incurring too much of a performance hit.
+ *
+ */
+
+/* the arp table is implemented as a sparse array indexed by
+ * the host part of the ip address.
+ *
+ * The array is in virtual memory (mmap) and is left uninitialized, so that the
+ * kernel will allocate and zero-fill pages on demand.  The ether_addr is
+ * stored in negated form and therefore it is always valid: uninitialized
+ * entries will give the broadcast address.  A new arp request will be sent
+ * when 'now' is after 'next_req'. The initial zero value of 'next_req' will
+ * trigger an arp request the first time the entry is read.
+ */
+struct arp_table_entry {
+	uint64_t	next_req;	/* when to send next arp request */
+	union {
+		uint8_t		ether_addr[6 + 2]; /* size + padding */
+		struct {
+			uint32_t eth1;
+			uint16_t eth2;
+			uint16_t pad;
+		};
+	};
+} __attribute__((packed));
+
+void
+arp_table_entry_dump(int idx, struct arp_table_entry *e)
+{
+    ED("%d: next %" PRIu64 " addr %02x:%02x:%02x:%02x:%02x:%02x",
+            idx, e->next_req,
+            (uint8_t)~e->ether_addr[0],
+            (uint8_t)~e->ether_addr[1],
+            (uint8_t)~e->ether_addr[2],
+            (uint8_t)~e->ether_addr[3],
+            (uint8_t)~e->ether_addr[4],
+            (uint8_t)~e->ether_addr[5]);
+}
+
+struct arp_table_entry *
+arp_table_new(in_addr_t mask)
+{
+    // XXX this only works if mask is in CIDR form */
+    size_t s = (~ntohl(mask) + 1) * sizeof(struct arp_table_entry);
+    struct arp_table_entry *e;
+    ED("allocating %zu bytes for arp table", s);
+    e = mmap(NULL, s, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+    if (e == MAP_FAILED)
+        return NULL;
+    return e;
+}
+
+static inline int
+arp_idx(in_addr_t addr, in_addr_t mask)
+{
+    return ntohl(addr & ~mask);
+}
+
+
+/* arp commands are sent by the producer to the consumer that insists on
+ * the same port. The commands are sent when the producer receives an ARP
+ * message, as follows:
+ * - when an ARP request is received, ask the consumer to send an ARP
+ *   reply
+ * - when an ARP reply is received, ask the consumer to update its
+ *   ARP table
+ * The commands also contain the ethernet and IP address of the sender
+ * of the received ARP message.
+ */
+struct arp_cmd {
+	union {
+		uint8_t		ether_addr[6];
+		struct {
+			uint32_t eth1;
+			uint16_t eth2;
+		};
+	};
+	uint8_t		valid; /* 0: empty, 1: new, 2: seen */
+	uint8_t		cmd;   /* ARPOP_REQUEST or ARPOP_REPLY */
+	in_addr_t	ip_addr;
+	uint8_t		pad[4];
+} __attribute__((packed));
+
+/* the commands are sent in a small mailbox shared between the producer and the
+ * consumer. The head and tail pointers are not shared, and the synchronization
+ * is enforced by the 'valid' fields inside the commands themselves.  This
+ * saves some cache misses and eliminates the need for memory barriers.
+ */
+#define ARP_CMD_QSIZE 16
+struct arp_cmd_q {
+	struct arp_cmd	q[ARP_CMD_QSIZE] ALIGN_CACHE;
+	uint64_t	head ALIGN_CACHE; /* private to the consumer */
+	uint64_t	toclean;	  /* private to the consumer */
+	uint64_t	tail ALIGN_CACHE; /* private to the producer */
+};
+
+/* consumer: extract a new command.  The command slot is not immediately
+ * released, so that at most ARP_CMD_QSIZE messages are read for each
+ * cons() loop.
+ */
+static inline struct arp_cmd *
+arpq_get_cmd(struct arp_cmd_q *a)
+{
+    int h = a->head & (ARP_CMD_QSIZE - 1);
+    if (unlikely(a->q[h].valid == 1)) {
+        a->q[h].valid = 2; /* mark as seen */
+        a->head++;
+        return &a->q[h];
+    }
+    return NULL;
+}
+
+/* consumer: release all seen slots */
+static inline void
+arpq_release(struct arp_cmd_q *a)
+{
+    int c = a->toclean & (ARP_CMD_QSIZE - 1);
+    if (likely(a->q[c].valid != 2))
+        return;
+    while (a->q[c].valid == 2) {
+        a->q[c].valid = 0;
+        a->toclean++;
+	c = a->toclean & (ARP_CMD_QSIZE - 1);
+    }
+}
+
+struct arp_cmd *
+arpq_new_cmd(struct arp_cmd_q *a)
+{
+    int t = a->tail & (ARP_CMD_QSIZE - 1);
+    struct arp_cmd *c = &a->q[t];
+
+    return (c->valid ? NULL : c);
+}
+
+void
+arpq_push(struct arp_cmd_q *a, struct arp_cmd *c)
+{
+    c->valid = 1;
+    a->tail++;
+}
+
+static inline int
+is_arp(const void *pkt)
+{
+    const struct ether_header *h = pkt;
+    return h->ether_type == htons(ETHERTYPE_ARP);
+}
+
+struct arp_cmd_q arpq[2];
+
+/* IPv4 info for a port. Shared between the producer and the consumer that
+ * insist on the same port
+ */
+struct ipv4_info {
+	char		name[IFNAMSIZ + 1];
+	in_addr_t	ip_addr;
+	in_addr_t	ip_mask;
+	in_addr_t	ip_subnet;
+	in_addr_t	ip_bcast;
+	in_addr_t	ip_gw;
+	union {
+		struct {
+		    uint8_t  pad1[2];
+		    uint8_t  ether_addr[6];
+		};
+		struct {
+		    uint16_t pad2;
+		    uint16_t eth1;
+		    uint32_t eth2;
+		};
+	};
+	/* pre-formatted arp messages */
+	union {
+		uint8_t pkt[60];
+		struct {
+			struct ether_header eh;
+			struct ether_arp    ah;
+		} arp __attribute__((packed));
+	} arp_reply, arp_request;
+
+	struct arp_table_entry *arp_table;
+};
+
+void
+ipv4_dump(const struct ipv4_info *i)
+{
+    const uint8_t *ipa = (uint8_t *)&i->ip_addr,
+          *ipm = (uint8_t *)&i->ip_mask,
+          *ipb = (uint8_t *)&i->ip_bcast,
+          *ipc = (uint8_t *)&i->ip_gw,
+          *ea = i->ether_addr;
+
+    ED("%s: ip %u.%u.%u.%u/%u.%u.%u.%u bcast %u.%u.%u.%u gw %u.%u.%u.%u mac %02x:%02x:%02x:%02x:%02x:%02x",
+            i->name,
+            ipa[0], ipa[1], ipa[2], ipa[3],
+            ipm[0], ipm[1], ipm[2], ipm[3],
+            ipb[0], ipb[1], ipb[2], ipb[3],
+            ipc[0], ipc[1], ipc[2], ipc[3],
+            ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
+}
+
+struct ipv4_info ipv4[2];
+
+static void usage();
+void
+route_mode_init(const char *ifname[], const char *gateways[])
+{
+    int fd, i;
+    struct ifreq ifr;
+#ifdef __FreeBSD__
+    struct ifaddrs *ifap, *p;
+
+    if (getifaddrs(&ifap) < 0) {
+	ED("failed to get interface list: %s", strerror(errno));
+	usage();
+    }
+#endif /* __FreeBSD__ */
+
+    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+    if (fd < 0) {
+	ED("failed to open SOCK_DGRAM socket: %s", strerror(errno));
+	usage();
+    }
+
+    for (i = 0; i < 2; i++) {
+	struct ipv4_info *ip = &ipv4[i];
+	char *dst = ip->name;
+	const char *scan;
+	struct ether_header *eh;
+	struct ether_arp *ah;
+	void *hwaddr = NULL;
+
+	/* try to extract the port name */
+	if (!strncmp("vale", ifname[i], 4)) {
+	    ED("route mode not supported for VALE port %s", ifname[i]);
+	    usage();
+	}
+	if (strncmp("netmap:", ifname[i], 7)) {
+	    ED("missing netmap: prefix in %s", ifname[i]);
+	    usage();
+	}
+	scan = ifname[i] + 7;
+	if (strlen(scan) >= IFNAMSIZ) {
+	    ED("name too long: %s", scan);
+	    usage();
+	}
+	while (*scan && isalnum(*scan))
+	    *dst++ = *scan++;
+	*dst = '\0';
+	ED("trying to get configuration for %s", ip->name);
+
+	/* MAC address */
+#ifdef linux
+	memset(&ifr, 0, sizeof(ifr));
+	strcpy(ifr.ifr_name, ip->name);
+	if (ioctl(fd, SIOCGIFHWADDR, &ifr) >= 0) {
+	    hwaddr = ifr.ifr_addr.sa_data;
+	}
+#elif defined (__FreeBSD__)
+	errno = ENOENT;
+	for (p = ifap; p; p = p->ifa_next) {
+
+	    if (!strcmp(p->ifa_name, ip->name) &&
+		    p->ifa_addr != NULL &&
+		    p->ifa_addr->sa_family == AF_LINK)
+	    {
+		struct sockaddr_dl *sdp =
+		    (struct sockaddr_dl *)p->ifa_addr;
+		hwaddr = sdp->sdl_data + sdp->sdl_nlen;
+		break;
+	    }
+	}
+#endif /* __FreeBSD__ */
+	if (hwaddr == NULL) {
+	    ED("failed to get MAC address for %s: %s",
+		    ip->name, strerror(errno));
+	    usage();
+	}
+	memcpy(ip->ether_addr, hwaddr, 6);
+
+#define get_ip_info(_c, _f, _m) 						\
+	memset(&ifr, 0, sizeof(ifr));						\
+	strcpy(ifr.ifr_name, ip->name);						\
+	ifr.ifr_addr.sa_family = AF_INET;					\
+	if (ioctl(fd, _c, &ifr) < 0) {						\
+	    ED("failed to get IPv4 " _m " for %s: %s",				\
+		    ip->name, strerror(errno));					\
+	    usage();								\
+	}									\
+	memcpy(&ip->_f, &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, 4);	\
+
+
+	/* IP address */
+	get_ip_info(SIOCGIFADDR, ip_addr, "address");
+	/* netmask */
+	get_ip_info(SIOCGIFNETMASK, ip_mask, "netmask");
+	/* broadcast */
+	get_ip_info(SIOCGIFBRDADDR, ip_bcast, "broadcast");
+#undef get_ip_info
+
+	/* do we have an IP address? */
+	if (ip->ip_addr == 0) {
+	    ED("no IPv4 address found for %s", ip->name);
+	    usage();
+	}
+
+	/* cache the subnet */
+	ip->ip_subnet = ip->ip_addr & ip->ip_mask;
+
+	/* default gateway, if any */
+	if (gateways[i]) {
+	    const char *gw = gateways[i];
+	    struct ipv4_info *ip = &ipv4[i];
+	    struct in_addr a;
+	    if (!inet_aton(gw, &a)) {
+		ED("not a valid IP address: %s", gw);
+		usage();
+	    }
+	    if ((a.s_addr & ip->ip_mask) != ip->ip_subnet) {
+		ED("gateway %s unreachable", gw);
+		usage();
+	    }
+	    ip->ip_gw = a.s_addr;
+	}
+
+	ipv4_dump(ip);
+
+	/* precompute the arp reply for this interface */
+	eh = &ip->arp_reply.arp.eh;
+	ah = &ip->arp_reply.arp.ah;
+	memset(&ip->arp_reply, 0, sizeof(ip->arp_reply));
+	memcpy(eh->ether_shost, ip->ether_addr, 6);
+	eh->ether_type = htons(ETHERTYPE_ARP);
+	ah->ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
+	ah->ea_hdr.ar_pro = htons(ETHERTYPE_IP);
+	ah->ea_hdr.ar_hln = 6;
+	ah->ea_hdr.ar_pln = 4;
+	ah->ea_hdr.ar_op = htons(ARPOP_REPLY);
+	memcpy(ah->arp_sha, ip->ether_addr, 6);
+	memcpy(ah->arp_spa, &ip->ip_addr, 4);
+
+	/* precompute the arp request for this interface */
+	eh = &ip->arp_request.arp.eh;
+	ah = &ip->arp_request.arp.ah;
+	memcpy(&ip->arp_request, &ip->arp_reply,
+		sizeof(ip->arp_reply));
+	memset(eh->ether_dhost, 0xff, 6);
+	ah->ea_hdr.ar_op = htons(ARPOP_REQUEST);
+
+	/* allocate the arp table */
+	ip->arp_table = arp_table_new(ip->ip_mask);
+	if (ip->arp_table == NULL) {
+	    ED("failed to allocate the arp table for %s: %s", ip->name,
+		    strerror(errno));
+	    usage();
+	}
+    }
+
+    close(fd);
+#ifdef __FreeBSD__
+    freeifaddrs(ifap);
+#endif /* __FreeBSD__ */
+}
+
 struct pipe_args {
 	int		zerocopy;
 	int		wait_link;
+	int		route_mode;
+	int		hugepages;
 
 	pthread_t	cons_tid;	/* main thread */
 	pthread_t	prod_tid;	/* producer thread */
@@ -381,8 +1147,22 @@ struct pipe_args {
 	int		cons_core;	/* core for cons() */
 	int		prod_core;	/* core for prod() */
 
-	struct nm_desc *pa;		/* netmap descriptor */
-	struct nm_desc *pb;
+	struct nmport_d *pa;		/* netmap descriptor */
+	struct nmport_d *pb;
+
+	/* route-mode */
+	struct arp_cmd_q *cons_arpq;	/* out mailbox for cons */
+	struct arp_cmd_q *prod_arpq;	/* in mailbox for prod */
+	struct ipv4_info *cons_ipv4;	/* mac addr etc. */
+	struct ipv4_info *prod_ipv4;	/* mac addr etc. */
+
+	/* raw stats */
+	struct stats	*stats;
+
+#ifdef WITH_MAX_LAG
+	/* max delay before the consumer starts dropping packets */
+	int64_t		max_lag;
+#endif /* WITH_MAX_LAG */
 
 	struct _qs	q;
 };
@@ -394,30 +1174,36 @@ struct pipe_args {
 static int
 setaffinity(int i)
 {
-        cpuset_t cpumask;
-	struct sched_param p;
-	int error;
+    cpuset_t cpumask;
+    struct sched_param p;
+    int error;
+    int maxprio;
 
-        if (i == -1)
-                return 0;
+    if (i == -1)
+        return 0;
 
-        /* Set thread affinity affinity.*/
-        CPU_ZERO(&cpumask);
-        CPU_SET(i, &cpumask);
+    /* Set thread affinity affinity.*/
+    CPU_ZERO(&cpumask);
+    CPU_SET(i, &cpumask);
 
-        if ( (error = pthread_setaffinity_np(pthread_self(), sizeof(cpuset_t), &cpumask)) != 0) {
-                ED("Unable to set affinity to cpu %d: %s", i, strerror(error));
-        }
-	if (setpriority(PRIO_PROCESS, 0, -10)) {; // XXX not meaningful
-                ED("Unable to set priority: %s", strerror(errno));
-	}
-	bzero(&p, sizeof(p));
-	p.sched_priority = 10; // 99 on linux ?
-	// use SCHED_RR or SCHED_FIFO
-	if (sched_setscheduler(0, SCHED_RR, &p)) {
-                ED("Unable to set scheduler: %s", strerror(errno));
-	}
-        return 0;
+    if ( (error = pthread_setaffinity_np(pthread_self(), sizeof(cpuset_t), &cpumask)) != 0) {
+        ED("Unable to set affinity to cpu %d: %s", i, strerror(error));
+    }
+    if (setpriority(PRIO_PROCESS, 0, -10)) {; // XXX not meaningful
+        ED("Unable to set priority: %s", strerror(errno));
+    }
+    maxprio = sched_get_priority_max(SCHED_RR);
+    if (maxprio < 0) {
+        ED("Unable to retrieve max RR priority, using 10");
+        maxprio = 10;
+    }
+    bzero(&p, sizeof(p));
+    p.sched_priority = maxprio;
+    // use SCHED_RR or SCHED_FIFO
+    if (sched_setscheduler(0, SCHED_RR, &p)) {
+        ED("Unable to set scheduler: %s", strerror(errno));
+    }
+    return 0;
 }
 
 
@@ -434,14 +1220,14 @@ set_tns_now(uint64_t *now, uint64_t t0)
 
 static inline int pad(int x)
 {
-	return ((x) + PKT_PAD - 1) & ~(PKT_PAD - 1) ;
+    return ((x) + PKT_PAD - 1) & ~(PKT_PAD - 1) ;
 }
 
 /* compare two timestamps */
 static inline int64_t
 ts_cmp(uint64_t a, uint64_t b)
 {
-	return (int64_t)(a - b);
+    return (int64_t)(a - b);
 }
 
 /* create a packet descriptor */
@@ -459,17 +1245,17 @@ pkt_at(struct _qs *q, uint64_t ofs)
 static int
 q_reclaim(struct _qs *q)
 {
-	struct q_pkt *p0, *p;
-
-	p = p0 = pkt_at(q, q->prod_tail_1);
-	/* always reclaim queued packets */
-	while (ts_cmp(p->pt_qout, q->prod_now) <= 0 && q->prod_queued > 0) {
-	    ND(1, "reclaim pkt at %ld len %d left %ld", q->prod_tail_1, p->pktlen, q->prod_queued);
-	    q->prod_queued -= p->pktlen;
-	    q->prod_tail_1 = p->next;
-	    p = pkt_at(q, q->prod_tail_1);
-	}
-	return p != p0;
+    struct q_pkt *p0, *p;
+
+    p = p0 = pkt_at(q, q->prod_tail_1);
+    /* always reclaim queued packets */
+    while (ts_cmp(p->pt_qout, q->prod_now) <= 0 && q->prod_queued > 0) {
+        ND(1, "reclaim pkt at %ld len %d left %ld", q->prod_tail_1, p->pktlen, q->prod_queued);
+        q->prod_queued -= p->pktlen;
+        q->prod_tail_1 = p->next;
+        p = pkt_at(q, q->prod_tail_1);
+    }
+    return p != p0;
 }
 
 /*
@@ -504,29 +1290,28 @@ no_room(struct _qs *q)
     uint64_t new_t = t + need;
 
     if (q->buflen - new_t < MAX_PKT + sizeof(*p))
-	new_t = 0; /* further padding */
+        new_t = 0; /* further padding */
 
     /* XXX let the queue overflow once, otherwise it is complex
      * to deal with 0-sized queues
      */
     if (q->prod_queued > q->qsize) {
-	q_reclaim(q);
-	if (q->prod_queued > q->qsize) {
-	    q->prod_drop++;
-	    RD(1, "too many bytes queued %lu, drop %lu",
-		(_P64)q->prod_queued, (_P64)q->prod_drop);
-	    return 1;
-	}
+        q_reclaim(q);
+        if (q->prod_queued > q->qsize) {
+            RD(1, "too many bytes queued %llu, drop %llu",
+                    (unsigned long long)q->prod_queued, (unsigned long long)q->txstats->drop_packets);
+            return 1;
+        }
     }
 
     if ((h <= t && new_t == 0 && h == 0) || (h > t && (new_t == 0 || new_t >= h)) ) {
-	h = q->prod_head = q->head; /* re-read head, just in case */
-	/* repeat the test */
-	if ((h <= t && new_t == 0 && h == 0) || (h > t && (new_t == 0 || new_t >= h)) ) {
-	    ND(1, "no room for insert h %ld t %ld new_t %ld",
-		(_P64)h, (_P64)t, (_P64)new_t);
-	    return 1; /* no room for insert */
-	}
+        h = q->prod_head = q->head; /* re-read head, just in case */
+        /* repeat the test */
+        if ((h <= t && new_t == 0 && h == 0) || (h > t && (new_t == 0 || new_t >= h)) ) {
+            ND(1, "no room for insert h %lld t %lld new_t %lld",
+                    (long long)h, (long long)t, (long long)new_t);
+            return 1; /* no room for insert */
+        }
     }
     p->next = new_t; /* prepare for queueing */
     p->pktlen = 0;
@@ -541,36 +1326,43 @@ static inline int
 enq(struct _qs *q)
 {
     struct q_pkt *p = pkt_at(q, q->prod_tail);
+    char *dst = (char *)(p + 1);
+    unsigned int len = q->cur_frags[0].len;
+    int i;
 
     /* hopefully prefetch has been done ahead */
-    nm_pkt_copy(q->cur_pkt, (char *)(p+1), q->cur_len);
+    nm_pkt_copy(q->cur_pkt, dst, len);
+    /* copy the fragments, if any */
+    for (i = 1; i < q->cur_nfrags; i++) {
+        dst += len;
+        len = q->cur_frags[i].len;
+        /* we cannot use nm_pkt_copy, since dst may be unaligned */
+        memcpy(dst, q->cur_frags[i].buf, len);
+    }
     p->pktlen = q->cur_len;
     p->pt_qout = q->qt_qout;
-    p->pt_tx = q->qt_tx;
+    p->pt_tx = q->qt_tx - q->cur_tt;
     ND(1, "enqueue len %d at %d new tail %ld qout %ld tx %ld",
-	q->cur_len, (int)q->prod_tail, p->next,
-	p->pt_qout, p->pt_tx);
+            q->cur_len, (int)q->prod_tail, p->next,
+            p->pt_qout, p->pt_tx);
     q->prod_tail = p->next;
-    q->tx++;
-    if (q->max_bps)
-	q->prod_queued += p->pktlen;
+    if (q->qsize)
+        q->prod_queued += p->pktlen;
     /* XXX update timestamps ? */
     return 0;
 }
 
 
-int
-rx_queued(struct nm_desc *d)
+static inline int
+hold_update_release(struct _qs *q)
 {
-    u_int tot = 0, i;
-    for (i = d->first_rx_ring; i <= d->last_rx_ring; i++) {
-	struct netmap_ring *rxr = NETMAP_RXRING(d->nifp, i);
-
-	ND(5, "ring %d h %d cur %d tail %d", i,
-		rxr->head, rxr->cur, rxr->tail);
-	tot += nm_ring_space(rxr);
+    if (q->hold_release)
+        return 1;
+    if (q->hold_next_rt && ts_cmp(q->hold_next_rt, q->prod_now) <= 0) {
+        q->hold_release = 1;
+        return 1;
     }
-    return tot;
+    return 0;
 }
 
 /*
@@ -583,54 +1375,31 @@ wait_for_packets(struct _qs *q)
     uint64_t prev = q->prod_now;
 
     ioctl(q->src_port->fd, NIOCRXSYNC, 0); /* forced */
+    ec_checkactive(q);
     while (!do_abort) {
-
-	n0 = rx_queued(q->src_port);
-	if (n0 > (int)q->rx_qmax) {
-	    q->rx_qmax = n0;
-	}
-	if (n0)
-	    break;
-	prev = 0; /* we slept */
-	if (1) {
-	    usleep(5);
-	    ioctl(q->src_port->fd, NIOCRXSYNC, 0);
-	} else {
-	    struct pollfd pfd;
-	    struct netmap_ring *rx;
-	    int ret;
-
-	    pfd.fd = q->src_port->fd;
-	    pfd.revents = 0;
-	    pfd.events = POLLIN;
-	    ND(1, "prepare for poll on %s", q->prod_ifname);
-	    ret = poll(&pfd, 1, 10000);
-	    if (ret <= 0 || verbose) {
-		D("poll %s ev %x %x rx %d@%d",
-		    ret <= 0 ? "timeout" : "ok",
-		    pfd.events,
-		    pfd.revents,
-		    rx_queued(q->src_port),
-		    NETMAP_RXRING(q->src_port->nifp, q->src_port->first_rx_ring)->cur
-		);
-	    }
-	    if (pfd.revents & POLLERR) {
-		rx = NETMAP_RXRING(q->src_port->nifp, q->src_port->first_rx_ring);
-		D("error on fd0, rx [%d,%d,%d)",
-		    rx->head, rx->cur, rx->tail);
-		sleep(1);
-	    }
-	}
+        if (hold_update_release(q))
+            break;
+        n0 = nm_ring_space(q->rxring);
+        if (n0 > (int)q->rx_qmax) {
+            q->rx_qmax = n0;
+        }
+        if (n0)
+            break;
+        prev = 0; /* we slept */
+        usleep(5);
+        ioctl(q->src_port->fd, NIOCRXSYNC, 0);
+        ec_checkactive(q);
+        set_tns_now(&q->prod_now, q->t0);
     }
     set_tns_now(&q->prod_now, q->t0);
     if (ts_cmp(q->qt_qout, q->prod_now) < 0) {
-	q->qt_qout = q->prod_now;
+        q->qt_qout = q->prod_now;
     }
     if (prev > 0 && (prev = q->prod_now - prev) > q->prod_max_gap) {
-	q->prod_max_gap = prev;
+        q->prod_max_gap = prev;
     }
     ND(10, "%s %d queued packets at %ld ms",
-	q->prod_ifname, n0, (q->prod_now/1000000) % 10000);
+            q->prod_ifname, n0, (q->prod_now/1000000) % 10000);
 }
 
 /*
@@ -646,12 +1415,12 @@ prefetch_packet(struct netmap_ring *rxr, int pos)
     const char *buf;
 
     if (ofs >= rxr->num_slots)
-	return;
+        return;
     rs = &rxr->slot[ofs];
     buf = NETMAP_BUF(rxr, rs->buf_idx);
     l = rs->len;
     for (i = 0; i < l; i += 64)
-	__builtin_prefetch(buf + i);
+        __builtin_prefetch(buf + i);
 }
 
 /*
@@ -661,50 +1430,101 @@ static void
 scan_ring(struct _qs *q, int next /* bool */)
 {
     struct netmap_slot *rs;
-    struct netmap_ring *rxr = q->rxring; /* invalid if next == 0 */
-    struct nm_desc *pa = q->src_port;
-
-    /* fast path for the first two */
-    if (likely(next != 0)) { /* current ring */
-	ND(10, "scan next");
-	/* advance */
-	rxr->head = rxr->cur = nm_ring_next(rxr, rxr->cur);
-	if (!nm_ring_empty(rxr)) /* good one */
-	    goto got_one;
-	q->si++;	/* otherwise update and fallthrough */
-    } else { /* scan from beginning */
-	q->si = pa->first_rx_ring;
-	ND(10, "scanning first ring %d", q->si);
-    }
-    while (q->si <= pa->last_rx_ring) {
-	q->rxring = rxr = NETMAP_RXRING(pa->nifp, q->si);
-	if (!nm_ring_empty(rxr))
-	    break;
-	q->si++;
-	continue;
-    }
-    if (q->si > pa->last_rx_ring) { /* no data, cur == tail */
-        ND(5, "no more pkts on %s", q->prod_ifname);
-	return;
-    }
-got_one:
-    rs = &rxr->slot[rxr->cur];
-    if (unlikely(rs->buf_idx < 2)) {
-	D("wrong index rx[%d] = %d", rxr->cur, rs->buf_idx);
-	sleep(2);
-    }
-    if (unlikely(rs->len > MAX_PKT)) { // XXX
-	D("wrong len rx[%d] len %d", rxr->cur, rs->len);
-	rs->len = 0;
+    struct netmap_ring *rxr = q->rxring;
+    int nfrags;
+
+    if (likely(next != 0)) {
+        /* advance */
+        rxr->head = rxr->cur = nm_ring_next(rxr, rxr->cur);
+        if (nm_ring_empty(rxr)) /* no more packets */
+            return;
     }
-    q->cur_pkt = NETMAP_BUF(rxr, rs->buf_idx);
-    q->cur_len = rs->len;
+    rs = &rxr->slot[rxr->cur];
+    /* netmap makes sure that we do not receive incomplete packets */
+    nfrags = 0;
+    q->cur_len = 0;
+    do {
+        struct _frag *f = &q->cur_frags[nfrags];
+        f->buf = NETMAP_BUF(rxr, rs->buf_idx);
+        f->len = rs->len;
+        q->cur_len += f->len;
+        nfrags++;
+        if (!(rs->flags & NS_MOREFRAG))
+            break;
+        rxr->cur = nm_ring_next(rxr, rxr->cur);
+        rs = &rxr->slot[rxr->cur];
+    } while (nfrags < MAX_FRAGS);
+    q->cur_pkt = q->cur_frags[0].buf;
+    q->cur_nfrags = nfrags;
+    rxr->head = rxr->cur;
     //prefetch_packet(rxr, 1); not much better than prefetching q->cur_pkt, one line
     __builtin_prefetch(q->cur_pkt);
     __builtin_prefetch(rs+1); /* one row ahead ? */
     ND(10, "-------- slot %d tail %d len %d buf %p", rxr->cur, rxr->tail, q->cur_len, q->cur_pkt);
 }
 
+/*
+ * Packet reordering.
+ *
+ * Packets subject to reordering are hold in a separate FIFO queue,
+ * local to the producer thread. Note that cur_hold_delay may then be
+ * increased to make sure that no further reording is necessary
+ * in the FIFO.
+ *
+ * Once out of the queue, the hold packets reenter the stream
+ * and go through the normal processing.
+ */
+
+static inline void
+reorder_hold(struct _qs *q)
+{
+    struct h_pkt *nh;
+
+    nh = (struct h_pkt *)(q->hold_buf + q->hold_tail);
+    nm_pkt_copy(q->cur_pkt, (char *)(nh + 1), q->cur_len);
+    nh->pktlen = q->cur_len;
+    nh->releasetime = q->cur_hold_delay;
+    if (!q->hold_next_rt) {
+        q->hold_next_rt = nh->releasetime;
+    } else {
+        /* not empty, prevent further reordering */
+        if (nh->releasetime < q->hold_tail_rt)
+            nh->releasetime = q->hold_tail_rt;
+    }
+    q->hold_tail_rt = nh->releasetime;
+    q->hold_tail += sizeof(*nh) + nh->pktlen;
+    if (unlikely(q->hold_tail >= q->hold_buflen))
+        q->hold_tail = 0;
+}
+
+static int
+reorder_release(struct _qs *q)
+{
+    struct h_pkt *h;
+    if (!q->hold_release)
+        return 0;
+    h = (struct h_pkt *)(q->hold_buf + q->hold_head);
+    q->cur_pkt = q->hold_buf + q->hold_head + sizeof(*h);
+    q->cur_len = h->pktlen;
+    q->cur_frags[0].buf = q->cur_pkt;
+    q->cur_frags[0].len = q->cur_len;
+    q->cur_nfrags = 1;
+    q->hold_head += sizeof(*h) + q->cur_len;
+    if (unlikely(q->hold_head >= q->hold_buflen))
+        q->hold_head = 0;
+    q->hold_release = 0;
+    if (q->hold_head != q->hold_tail) {
+        h = (struct h_pkt *)(q->hold_buf + q->hold_head);
+        q->hold_next_rt = h->releasetime;
+        if (ts_cmp(q->hold_next_rt, q->prod_now) < 0)
+            q->hold_release = 1;
+    } else {
+        q->hold_next_rt = 0;
+    }
+    return 1;
+}
+
+
 /*
  * simple handler for parameters not supplied
  */
@@ -720,15 +1540,108 @@ null_run_fn(struct _qs *q, struct _cfg *cfg)
 static int
 drop_after(struct _qs *q)
 {
-	(void)q; // XXX
-	return 0;
+    int drop = q->cur_drop;
+    q->cur_drop = 0;
+    return drop;
 }
 
-
-static void *
-prod(void *_pa)
+/* poducer: send the proper command depending on the contents of the received
+ * ARP message in pkt
+ */
+void
+prod_push_arp(const struct pipe_args *pa, const void *pkt)
 {
-    struct pipe_args *pa = _pa;
+    const struct ether_header *eh = pkt;
+    const struct ether_arp *arp = (const struct ether_arp *)(eh + 1);
+    const struct ipv4_info *ip = pa->prod_ipv4;
+    struct arp_cmd_q *a = pa->prod_arpq;
+    struct arp_cmd *c;
+    in_addr_t ip_saddr, ip_taddr;
+    uint16_t arpop = ntohs(arp->ea_hdr.ar_op);
+
+    memcpy(&ip_saddr, arp->arp_spa, 4);
+    memcpy(&ip_taddr, arp->arp_tpa, 4);
+    if (ip_taddr != ip->ip_addr ||
+            ((ip_saddr & ip->ip_mask) != ip->ip_subnet) ||
+            (arpop != ARPOP_REQUEST && arpop != ARPOP_REPLY)) {
+        /* not for us, drop */
+        return;
+    }
+    c = arpq_new_cmd(a);
+    if (c == NULL) {
+        /* no space left in the mailbox */
+        return;
+    }
+    c->cmd = arpop; /* just the low byte */
+    memcpy(c->ether_addr, arp->arp_sha, 6);
+    c->ip_addr = ip_saddr;
+    arpq_push(a, c);
+}
+
+static void
+prod_procpkt(struct _qs *q)
+{
+    uint64_t t_tx, tt;	/* output and transmission time */
+
+    q->c_imp[I_LOSS].run(q, &q->c_imp[I_LOSS]);
+    if (q->cur_drop) {
+        q->txstats->drop_packets++;
+        q->txstats->drop_bytes += q->cur_len;
+        return;
+    }
+    if (no_room(q)) {
+        q->tail = q->prod_tail; /* notify */
+        usleep(1); // XXX give cons a chance to run ?
+        set_tns_now(&q->prod_now, q->t0);
+        if (no_room(q)) {/* try to run drop-free once */
+            q->txstats->drop_packets++;
+            q->txstats->drop_bytes += q->cur_len;
+            return;
+        }
+    }
+    // XXX possibly implement c_tt for transmission time emulation
+    q->c_imp[I_BW].run(q, &q->c_imp[I_BW]);
+    tt = q->cur_tt;
+    q->qt_qout += tt;
+    if (drop_after(q)) {
+	q->qt_qout -= tt;
+        q->txstats->drop_packets++;
+        q->txstats->drop_bytes += q->cur_len;
+        return;
+    }
+    if (!q->reuse_delay) {
+        q->c_imp[I_DELAY].run(q, &q->c_imp[I_DELAY]); /* compute delay */
+        if (q->delay_offset > q->cur_delay) {
+            q->cur_delay = 0;
+        } else {
+            q->cur_delay -= q->delay_offset;
+        }
+    }
+    t_tx = q->qt_qout + q->cur_delay;
+    ND(5, "tt %ld qout %ld tx %ld qt_tx %ld", tt, q->qt_qout, t_tx, q->qt_tx);
+    /* insure no reordering and spacing by transmission time */
+    if (t_tx < q->qt_tx + tt) {
+        q->reuse_delay = 1;
+        if (q->allow_drop) {
+            q->qt_qout -= tt;
+            q->txstats->drop_packets++;
+            q->txstats->drop_bytes += q->cur_len;
+            return;
+        }
+        t_tx = q->qt_tx + tt;
+    } else {
+        q->reuse_delay = 0;
+    }
+    q->qt_tx = t_tx;
+    enq(q);
+    q->txstats->packets++;
+    q->txstats->bytes += q->cur_len;
+}
+
+static void *
+prod(void *_pa)
+{
+    struct pipe_args *pa = _pa;
     struct _qs *q = &pa->q;
 
     setaffinity(pa->prod_core);
@@ -736,47 +1649,140 @@ prod(void *_pa)
     q->qt_qout = q->qt_tx = q->prod_now;
     ND("start times %ld", q->prod_now);
     while (!do_abort) { /* producer, infinite */
-	int count;
-
-	wait_for_packets(q);	/* also updates prod_now */
-	// XXX optimize to flush frequently
-	for (count = 0, scan_ring(q, 0); count < q->burst && !nm_ring_empty(q->rxring);
-		count++, scan_ring(q, 1)) {
-	    // transmission time
-	    uint64_t t_tx, tt;	/* output and transmission time */
-
-	    if (q->cur_len < 60) {
-		RD(5, "short packet len %d", q->cur_len);
-		continue; // short frame
-	    }
-	    q->c_loss.run(q, &q->c_loss);
-	    if (q->cur_drop)
-		continue;
-	    if (no_room(q)) {
-		q->tail = q->prod_tail; /* notify */
-		usleep(1); // XXX give cons a chance to run ?
-		if (no_room(q)) /* try to run drop-free once */
-		    continue;
-	    }
-	    // XXX possibly implement c_tt for transmission time emulation
-	    q->c_bw.run(q, &q->c_bw);
-	    tt = q->cur_tt;
-	    q->qt_qout += tt;
-	    if (drop_after(q))
-		continue;
-	    q->c_delay.run(q, &q->c_delay); /* compute delay */
-	    t_tx = q->qt_qout + q->cur_delay;
-	    ND(5, "tt %ld qout %ld tx %ld qt_tx %ld", tt, q->qt_qout, t_tx, q->qt_tx);
-	    /* insure no reordering and spacing by transmission time */
-	    q->qt_tx = (t_tx >= q->qt_tx + tt) ? t_tx : q->qt_tx + tt;
-	    enq(q);
-	}
-	q->tail = q->prod_tail; /* notify */
+        int count;
+
+        wait_for_packets(q);	/* also updates prod_now */
+
+        for (count = 0; count < q->burst && reorder_release(q); count++) {
+            prod_procpkt(q);
+        }
+        // XXX optimize to flush frequently
+        for (scan_ring(q, 0); count < q->burst && !nm_ring_empty(q->rxring);
+                count++, scan_ring(q, 1)) {
+            if (q->cur_len < 60) {
+                RD(5, "short packet len %d", q->cur_len);
+                continue; // short frame
+            }
+            if (pa->route_mode && unlikely(is_arp(q->cur_pkt))) {
+                /* pass it to the consumer in the other direction */
+                prod_push_arp(pa, q->cur_pkt);
+                continue;
+            }
+            q->c_imp[I_REORDER].run(q, &q->c_imp[I_REORDER]);
+            if (q->cur_hold_delay) {
+                q->cur_hold_delay += q->prod_now;
+                q->txstats->reorder_packets++;
+                q->txstats->reorder_bytes += q->cur_len;
+                reorder_hold(q);
+                continue;
+            }
+            prod_procpkt(q);
+        }
+        q->tail = q->prod_tail; /* notify */
     }
     D("exiting on abort");
     return NULL;
 }
 
+/* react to a command sent by the producer in the other direction.
+ * returns the number of packets injected.
+ */
+int
+cons_handle_arp(struct pipe_args *pa, struct arp_cmd *c)
+{
+    struct ipv4_info *ip = pa->cons_ipv4;
+    struct ether_header *eh = &ip->arp_reply.arp.eh;
+    struct ether_arp *ah = &ip->arp_reply.arp.ah;
+    struct arp_table_entry *e;
+    int rv = 0;
+
+    switch (c->cmd) {
+        case ARPOP_REQUEST:
+            /* send reply */
+            memcpy(eh->ether_dhost, c->ether_addr, 6);
+            memcpy(ah->arp_tha, c->ether_addr, 6);
+            memcpy(ah->arp_tpa, &c->ip_addr, 4);
+            if (nmport_inject(pa->pb, eh, sizeof(ip->arp_reply)) == 0) {
+                RD(1, "failed to inject arp reply");
+                break;
+            }
+            /* force the reply out */
+            rv = pa->q.burst;
+            break;
+        case ARPOP_REPLY:
+            e = ip->arp_table + arp_idx(c->ip_addr, ip->ip_mask);
+            set_tns_now(&e->next_req, pa->q.cons_now);
+            e->next_req += 5000000000;
+            e->eth1 = ~c->eth1;
+            e->eth2 = ~c->eth2;
+            break;
+        default:
+            /* we don't handle these ones */
+            RD(1, "unknown/unsupported ARP operation: %x", c->cmd);
+            break;
+    }
+    return rv;
+}
+
+/* change the ethernet target address according to the local ARP table
+ * and set the source address to the local MAC.
+ * may send an ARP request.
+ * returns the number of packets injected, or < 0 if the packet
+ * needs to be dropped
+ */
+static inline int
+cons_update_macs(struct pipe_args *pa, void *pkt)
+{
+    struct ether_header *eh = pkt;
+    struct ip *iph = (struct ip *)(eh + 1);
+    in_addr_t dst = iph->ip_dst.s_addr;
+    struct arp_table_entry *e;
+    struct ipv4_info *ipv4 = pa->cons_ipv4;
+    int idx;
+    int injected = 0;
+    //uint8_t *d = (uint8_t *)&dst;
+
+    ND("dst %u.%u.%u.%u", d[0], d[1], d[2], d[3]);
+    if (unlikely((ntohs(eh->ether_type) < 0x600)))
+	return -2; /* ignore 802.3 packets */
+    if (unlikely(!(ntohs(eh->ether_type) == ETHERTYPE_IP)))
+        return -1; /* drop */
+    if (unlikely(dst == ipv4->ip_bcast || dst == 0xffffffff))
+        return -1; /* drop */
+    if ((dst & ipv4->ip_mask) != ipv4->ip_subnet) {
+        if (ipv4->ip_gw) {
+            /* send to the default gateway */
+            dst = ipv4->ip_gw;
+        } else {
+            return -1; /* drop */
+        }
+    }
+    idx = arp_idx(dst, ipv4->ip_mask);
+    e = ipv4->arp_table + idx;
+    ND("idx %d e %p", idx, e);
+    //arp_table_entry_dump(idx, e);
+    if (unlikely(ts_cmp(pa->q.cons_now, e->next_req) > 0)) {
+        /* send arp request for this client */
+        struct ether_arp *ah = &ipv4->arp_request.arp.ah;
+        ND("sending arp request");
+        memcpy(ah->arp_tpa, &dst, 4);
+        set_tns_now(&e->next_req, pa->q.cons_now);
+        e->next_req += 5000000000; /* 5s */
+        if (nmport_inject(pa->pb, &ipv4->arp_request,
+                    sizeof(ipv4->arp_request)) == 0) {
+            RD(1, "failed to inject arp request");
+        } else {
+            injected = 1;
+        }
+    }
+    /* copy negated dst into eh (either broadcast or unicast) */
+    *(uint32_t *)eh = ~e->eth1;
+    *(uint16_t *)((char *)eh + 4) = ~e->eth2;
+    /* copy local MAC address into source */
+    *(uint16_t *)((char *)eh + 6) = ipv4->eth1;
+    *(uint32_t *)((char *)eh + 8) = ipv4->eth2;
+    return injected;
+}
 
 /*
  * the consumer reads from the queue using head,
@@ -787,8 +1793,9 @@ cons(void *_pa)
 {
     struct pipe_args *pa = _pa;
     struct _qs *q = &pa->q;
+    int pending = 0, retrying = 0;
+#if 0
     int cycles = 0;
-    int pending = 0;
     const char *pre_start, *pre_end; /* prefetch limits */
 
     /*
@@ -796,62 +1803,138 @@ cons(void *_pa)
      */
     pre_start = q->buf + q->head;
     pre_end = pre_start + 2048;
-
     (void)cycles; // XXX disable warning
+#endif
+
     set_tns_now(&q->cons_now, q->t0);
     while (!do_abort) { /* consumer, infinite */
-	struct q_pkt *p = (struct q_pkt *)(q->buf + q->head);
-	if (p->next < q->head) { /* wrap around prefetch */
-	    pre_start = q->buf + p->next;
-	}
-	pre_end = q->buf + p->next + 2048;
-#if 1
-	/* prefetch the first line saves 4ns */
+        uint64_t h = q->head; /* read only once */
+        uint64_t t = q->tail; /* read only once */
+        struct q_pkt *p = (struct q_pkt *)(q->buf + h);
+        struct arp_cmd *arpc;
+        int64_t delta;
+#if 0
+        struct q_pkt *p = (struct q_pkt *)(q->buf + q->head);
+        if (p->next < q->head) { /* wrap around prefetch */
+            pre_start = q->buf + p->next;
+        }
+        pre_end = q->buf + p->next + 2048;
+        //#if 1
+        /* prefetch the first line saves 4ns */
         (void)pre_end;//   __builtin_prefetch(pre_end - 2048);
-#else
-	/* prefetch, ideally up to a full packet not just one line.
-	 * this does not seem to have a huge effect.
-	 * 4ns out of 198 on 1500 byte packets
-	 */
-	for (; pre_start < pre_end; pre_start += 64)
-	    __builtin_prefetch(pre_start);
+        //#else
+        /* prefetch, ideally up to a full packet not just one line.
+         * this does not seem to have a huge effect.
+         * 4ns out of 198 on 1500 byte packets
+         */
+        for (; pre_start < pre_end; pre_start += 64)
+            __builtin_prefetch(pre_start);
 #endif
+        if (pa->route_mode) {
+            while (unlikely(arpc = arpq_get_cmd(pa->cons_arpq))) {
+                // uint8_t *ip_addr = (uint8_t *)&arpc->ip_addr;
+                ND("arp %x ether %02x:%02x:%02x:%02x:%02x:%02x ip %u.%u.%u.%u",
+                        arpc->cmd,
+                        arpc->ether_addr[0],
+                        arpc->ether_addr[1],
+                        arpc->ether_addr[2],
+                        arpc->ether_addr[3],
+                        arpc->ether_addr[4],
+                        arpc->ether_addr[5],
+                        ip_addr[0],
+                        ip_addr[1],
+                        ip_addr[2],
+                        ip_addr[3]);
+                pending += cons_handle_arp(pa, arpc);
+            }
+            arpq_release(pa->cons_arpq);
+        }
+        if ( h == t || (delta = ts_cmp(p->pt_tx, q->cons_now) ) > 0) {
+            ND(4, "                 >>>> TXSYNC, pkt not ready yet h %ld t %ld now %ld tx %ld",
+                    h, t, q->cons_now, p->pt_tx);
+            q->rx_wait++;
+            if (pending > 0) {
+                /* this also sends any pending arp messages from this or
+                 * previous loop iterations
+                 */
+                ioctl(pa->pb->fd, NIOCTXSYNC, 0);
+                pending = 0;
+            } else {
+                usleep(5);
+            }
+            set_tns_now(&q->cons_now, q->t0);
+            continue;
+        }
+#ifdef WITH_MAX_LAG
+        if (delta < -pa->max_lag) {
+            q->rxstats->drop_packets++;
+            q->rxstats->drop_bytes += p->pktlen;
+            goto next;
+        }
+#endif /* WITH_MAX_LAG */
+        ND(5, "drain len %ld now %ld tx %ld h %ld t %ld next %ld",
+                p->pktlen, q->cons_now, p->pt_tx, h, t, p->next);
+        if (pa->route_mode && !retrying) {
+            int injected = cons_update_macs(pa, p + 1);
+            if (unlikely(injected < 0)) {
+                /* drop this packet. Any pending arp message
+                 * will be sent in the next iteration
+                 */
+		if (injected == -1) {
+		    q->rxstats->drop_packets++;
+		    q->rxstats->drop_bytes += p->pktlen;
+		}
+                goto next;
+            }
+            pending += injected;
+        }
+        /* XXX inefficient but simple */
+        if (nmport_inject(pa->pb, (char *)(p + 1), p->pktlen) == 0) {
+            ND(5, "inject failed len %d now %ld tx %ld h %ld t %ld next %ld",
+                    (int)p->pktlen, q->cons_now, p->pt_tx, h, t, p->next);
+            ioctl(pa->pb->fd, NIOCTXSYNC, 0);
+            set_tns_now(&q->cons_now, q->t0);
+            pending = 0;
+            retrying = 1;
+            continue;
+        }
+        retrying = 0;
+        pending++;
+        if (pending > q->burst) {
+            ioctl(pa->pb->fd, NIOCTXSYNC, 0);
+            pending = 0;
+        }
 
-	if (q->head == q->tail || ts_cmp(p->pt_tx, q->cons_now) > 0) {
-	    ND(4, "                 >>>> TXSYNC, pkt not ready yet h %ld t %ld now %ld tx %ld",
-		q->head, q->tail, q->cons_now, p->pt_tx);
-	    q->rx_wait++;
-	    ioctl(pa->pb->fd, NIOCTXSYNC, 0); // XXX just in case
-	    pending = 0;
-	    usleep(5);
-	    set_tns_now(&q->cons_now, q->t0);
-	    continue;
-	}
-	ND(5, "drain len %ld now %ld tx %ld h %ld t %ld next %ld",
-		p->pktlen, q->cons_now, p->pt_tx, q->head, q->tail, p->next);
-	/* XXX inefficient but simple */
-	if (nm_inject(pa->pb, (char *)(p + 1), p->pktlen) == 0) {
-	    ND(5, "inject failed len %d now %ld tx %ld h %ld t %ld next %ld",
-		(int)p->pktlen, q->cons_now, p->pt_tx, q->head, q->tail, p->next);
-	    ioctl(pa->pb->fd, NIOCTXSYNC, 0);
-	    pending = 0;
-	    continue;
-	}
-	pending++;
-	if (pending > q->burst) {
-	    ioctl(pa->pb->fd, NIOCTXSYNC, 0);
-	    pending = 0;
-	}
-
-	q->head = p->next;
-	/* drain packets from the queue */
-	q->rx++;
-	// XXX barrier
+        q->rxstats->packets++;
+        q->rxstats->bytes += p->pktlen;
+next:
+        q->head = p->next;
+        /* drain packets from the queue */
+        // XXX barrier
     }
     D("exiting on abort");
     return NULL;
 }
 
+static uint64_t get_bufsize(uint64_t max_bps, uint64_t max_delay, uint64_t qsize, size_t hdrsz)
+{
+    double need;
+
+    /* allocate space for the queue:
+     * compute required bw*delay (adding 1ms for good measure),
+     * then add the queue size in bytes, then account for the headers
+     * and the packet expansion for padding
+     */
+
+    need = max_bps ? max_bps : INFINITE_BW;
+    need *= max_delay + 1000000;	/* delay is in nanoseconds */
+    need /= TIME_UNITS; /* total bits */
+    need /= 8; /* in bytes */
+    need += qsize; /* in bytes */
+    need += 3 * MAX_PKT; // safety
+    need *= (1 + 1.0 * (hdrsz + PKT_PAD) / 64);
+    return need;
+}
 
 /*
  * main thread for each direction.
@@ -864,59 +1947,75 @@ tlem_main(void *_a)
     struct pipe_args *a = _a;
     struct _qs *q = &a->q;
     uint64_t need;
+    int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
+    char b1[40], b2[40] = "0";
 
     setaffinity(a->cons_core);
     set_tns_now(&q->t0, 0); /* starting reference */
 
-    a->pa = nm_open(q->prod_ifname, NULL, NETMAP_NO_TX_POLL, NULL);
-    if (a->pa == NULL) {
-	ED("cannot open %s", q->prod_ifname);
-	return NULL;
-    }
-    // XXX use a single mmap ?
-    a->pb = nm_open(q->cons_ifname, NULL, NM_OPEN_NO_MMAP, a->pa);
-    if (a->pb == NULL) {
-	ED("cannot open %s", q->cons_ifname);
-	nm_close(a->pa);
-	return NULL;
+    if (a->hugepages) {
+        mmap_flags |= MAP_HUGETLB;
     }
+
     a->zerocopy = a->zerocopy && (a->pa->mem == a->pb->mem);
     ND("------- zerocopy %ssupported", a->zerocopy ? "" : "NOT ");
-    /* allocate space for the queue:
-     * compute required bw*delay (adding 1ms for good measure),
-     * then add the queue size i bytes, then multiply by three due
-     * to the packet expansion for padding
-     */
 
-    need = q->max_bps ? q->max_bps : INFINITE_BW;
-    need *= q->max_delay + 1000000;	/* delay is in nanoseconds */
-    need /= TIME_UNITS; /* total bits */
-    need /= 8; /* in bytes */
-    need += q->qsize; /* in bytes */
-    need += 3 * MAX_PKT; // safety
-
-    /*
-     * This is the memory strictly for packets.
-     * The size can increase a lot if we account for descriptors and
-     * rounding.
-     * In fact, the expansion factor can be up to a factor of 3
-     * for particularly bad situations (65-byte packets)
-     */
-    need *= 3; /* room for descriptors and padding */
-
-    q->buf = calloc(1, need);
-    if (q->buf == NULL) {
-	ED("alloc %ld bytes for queue failed, exiting", (_P64)need);
-	nm_close(a->pa);
-	nm_close(a->pb);
-	return(NULL);
+    need = get_bufsize(q->ec->max_bps, q->ec->max_delay,
+            q->qsize, sizeof(struct q_pkt));
+    norm(b1, need, 1);
+
+retry:
+    q->buf = mmap(0, need, PROT_WRITE | PROT_READ, mmap_flags, -1, 0);
+    if (q->buf == MAP_FAILED) {
+        ED("alloc %s bytes for queue failed, exiting", b1);
+        if (mmap_flags & MAP_HUGETLB && a->hugepages < 2) {
+            ED("trying again without hugepages");
+            mmap_flags &= ~MAP_HUGETLB;
+            goto retry;
+        }
+        nmport_close(a->pa);
+        nmport_close(a->pb);
+        do_abort = 1;
+        return(NULL);
+    }
+    if (mlock(q->buf, need) < 0) {
+        ED("(not fatal) failed to pin buffer memory: %s", strerror(errno));
     }
     q->buflen = need;
-    ED("----\n\t%s -> %s :  bps %ld delay %s loss %s queue %ld bytes"
-	"\n\tbuffer %lu bytes",
-	q->prod_ifname, q->cons_ifname,
-	(_P64)q->max_bps, q->c_delay.optarg, q->c_loss.optarg, (_P64)q->qsize,
-	(_P64)q->buflen);
+
+    if (q->ec->max_hold_delay) {
+        need = get_bufsize(q->ec->max_bps, q->ec->max_hold_delay,
+                0, sizeof(struct h_pkt));
+        norm(b2, need, 1);
+
+retry2:
+        q->hold_buf = mmap(0, need, PROT_WRITE | PROT_READ, mmap_flags, -1, 0);
+        if (q->hold_buf == MAP_FAILED) {
+            ED("alloc %s bytes for hold-buf failed, exiting", b2);
+            if (mmap_flags & MAP_HUGETLB) {
+                ED("trying again without hugepages");
+                mmap_flags &= ~MAP_HUGETLB;
+                goto retry2;
+            }
+            nmport_close(a->pa);
+            nmport_close(a->pb);
+            do_abort = 1;
+            return(NULL);
+        }
+        if (mlock(q->hold_buf, need) < 0) {
+            ED("(not fatal) failed to pin hold buffer memory: %s", strerror(errno));
+        }
+        q->hold_buflen = need - (sizeof(struct h_pkt) + MAX_PKT);
+    }
+
+    ED("----\n\t%s -> %s :  bps %lld delay %s loss %s reorder %s queue %lld bytes"
+            "\n\tbuffer   %s bytes\n\thold-buf %s bytes",
+            q->prod_ifname, q->cons_ifname,
+            (long long)q->ec->max_bps, q->c_imp[I_DELAY].optarg, q->c_imp[I_LOSS].optarg,
+	    q->c_imp[I_REORDER].optarg,
+            (long long)q->qsize, b1,
+            b2);
+
 
     q->src_port = a->pa;
 
@@ -932,9 +2031,9 @@ tlem_main(void *_a)
 static void
 sigint_h(int sig)
 {
-	(void)sig;	/* UNUSED */
-	do_abort = 1;
-	signal(SIGINT, SIG_DFL);
+    (void)sig;	/* UNUSED */
+    do_abort = 1;
+    signal(SIGINT, SIG_DFL);
 }
 
 
@@ -942,10 +2041,10 @@ sigint_h(int sig)
 static void
 usage(void)
 {
-	fprintf(stderr,
-	    "usage: tlem [-v] [-D delay] [-B bps] [-L loss] [-Q qsize] \n"
-	    "\t[-b burst] [-w wait_time] -i ifa -i ifb\n");
-	exit(1);
+    fprintf(stderr,
+            "usage: tlem [-v] [-D delay] [-B bps] [-L loss] [-Q qsize] \n"
+            "\t[-b burst] [-w wait_time] [-G gateway] -i ifa -i ifb\n");
+    exit(1);
 }
 
 
@@ -962,47 +2061,47 @@ split_arg(const char *src, int *_ac)
     int l, i, ac; /* number of entries */
 
     if (!src)
-	return NULL;
+        return NULL;
     l = strlen(src);
     /* in the first pass we count fields, in the second pass
      * we allocate the av[] array and a copy of the string
      * and fill av[]. av[ac] = NULL, av[ac+1]
      */
     for (;;) {
-	i = ac = 0;
-	ND("start pass %d: <%s>", av ? 1 : 0, my);
-	while (i < l) {
-	    /* trim leading separator */
-	    while (i = l)
-		break;
-	    ND("   pass %d arg %d: <%s>", av ? 1 : 0, ac, src+i);
-	    if (av) /* in the second pass, set the result */
-		av[ac] = my+i;
-	    ac++;
-	    /* skip string */
-	    while (i ", av ? 1 : 0, my);
+        while (i < l) {
+            /* trim leading separator */
+            while (i = l)
+                break;
+            ND("   pass %d arg %d: <%s>", av ? 1 : 0, ac, src+i);
+            if (av) /* in the second pass, set the result */
+                av[ac] = my+i;
+            ac++;
+            /* skip string */
+            while (i \n", i, av[i]);
-    av[i++] = NULL;
-    av[i++] = my;
+    if (verbose > 2)
+        for (i = 0; i < ac; i++) fprintf(stderr, "%d: <%s>\n", i, av[i]);
+    av[ac] = NULL;
+    av[ac+1] = my;
     *_ac = ac;
     return av;
 }
 
-
 /*
  * apply a command against a set of functions,
  * install a handler in *dst
@@ -1010,47 +2109,55 @@ split_arg(const char *src, int *_ac)
 static int
 cmd_apply(const struct _cfg *a, const char *arg, struct _qs *q, struct _cfg *dst)
 {
-	int ac = 0;
-	char **av;
-	int i;
-
-	if (arg == NULL || *arg == '\0')
-		return 1; /* no argument may be ok */
-	if (a == NULL || dst == NULL) {
-		ED("program error - invalid arguments");
-		exit(1);
-	}
-	av = split_arg(arg, &ac);
-	if (av == NULL)
-		return 1; /* error */
-	for (i = 0; a[i].parse; i++) {
-		struct _cfg x = a[i];
-		const char *errmsg = x.optarg;
-		int ret;
-
-		x.arg = NULL;
-		x.arg_len = 0;
-		bzero(&x.d, sizeof(x.d));
-		ret = x.parse(q, &x, ac, av);
-		if (ret == 2) /* not recognised */
-			continue;
-		if (ret == 1) {
-			ED("invalid arguments: need '%s' have '%s'",
-				errmsg, arg);
-			break;
-		}
-		x.optarg = arg;
-		*dst = x;
-		return 0;
-	}
-	ED("arguments %s not recognised", arg);
-	free(av);
-	return 1;
+    int ac = 0;
+    char **av;
+    int i;
+
+    if (arg == NULL || *arg == '\0')
+        return 0; /* no argument may be ok */
+    if (a == NULL || dst == NULL) {
+        ED("program error - invalid arguments");
+        exit(1);
+    }
+    if (!strcmp(arg, "none")) {
+        dst->ec->ec_valid = 0; /* use default */
+        return 0;
+    }
+    av = split_arg(arg, &ac);
+    if (av == NULL)
+        goto out; /* error */
+    for (i = 0; a[i].parse; i++) {
+        struct _cfg x = a[i];
+        const char *errmsg = x.optarg;
+        int ret;
+
+        x.arg = NULL;
+        x.ec = dst->ec;
+        ret = x.parse(q, &x, ac, av);
+        if (ret == 2) /* not recognised */
+            continue;
+        if (ret == 1) {
+            ED("invalid arguments: need '%s' have '%s'",
+                    errmsg, arg);
+            break;
+        }
+        x.optarg = arg;
+        *dst = x;
+        dst->ec->ec_index = i;
+        dst->ec->ec_valid = 1;
+        return 0;
+    }
+    ED("arguments %s not recognised", arg);
+    free(av);
+out:
+    dst->ec->ec_valid = 0;
+    return 1;
 }
 
 static struct _cfg delay_cfg[];
 static struct _cfg bw_cfg[];
 static struct _cfg loss_cfg[];
+static struct _cfg reorder_cfg[];
 
 static uint64_t parse_bw(const char *arg);
 static uint64_t parse_qsize(const char *arg);
@@ -1062,221 +2169,515 @@ static uint64_t parse_qsize(const char *arg);
  */
 
 static void
-add_to(const char ** v, int l, const char *arg, const char *msg)
+add_to(const char ** v, int l, const char *arg, char opt)
 {
-	for (; l > 0 && *v != NULL ; l--, v++);
-	if (l == 0) {
-		ED("%s %s", msg, arg);
-		exit(1);
+    for (; l > 0 && *v != NULL ; l--, v++);
+    if (l == 0) {
+        ED("-%c too many times: %s", opt, arg);
+        exit(1);
+    }
+    *v = arg;
+}
+
+#define U_PARSE_ERR ~(0ULL)
+
+static uint64_t parse_time(const char *arg); // forward
+
+/* set the maximum values for delay, bw and hold-time */
+static int
+set_max(const char *arg, struct _qs *q)
+{
+    int ac = 0;
+    char **av;
+    uint64_t delay = 0, bps = 0, hold = 0;
+
+    if (arg == NULL)
+        return 0;
+
+    av = split_arg(arg, &ac);
+    if (av == NULL || ac < 1 || ac > 3) {
+        ND("arg %p av %p ac %d", arg, av, ac);
+        ED("invalid parameters for -M: need max-delay[,max-bps[,max-hold-time]]]");
+        return 1;
+    }
+    /* first argument: max delay */
+    delay = parse_time(av[0]);
+    if (delay == U_PARSE_ERR) {
+        ED("invalid max-delay: %s", av[0]);
+        return 1;
+    }
+    if (ac > 1) {
+        /* second argument: max bw */
+        bps = parse_bw(av[1]);
+        if (bps == U_PARSE_ERR) {
+            ED("invalid max-bps: %s", av[1]);
+            return 1;
+        }
+	/* if we did not get any bw limitation from -B, use this one */
+	if (q->c_imp[I_BW].run == null_run_fn) {
+	    if (cmd_apply(bw_cfg, av[1], q, &q->c_imp[I_BW])) {
+		ED("warning: failed to set default bandwidth limitation to %s", av[1]);
+	    } else {
+		ED("set maximum bandwidth to %s", av[1]);
+	    }
 	}
-	*v = arg;
+    }
+    if (ac > 2) {
+        /* third argument: max hold time */
+        hold = parse_time(av[2]);
+        if (hold == U_PARSE_ERR) {
+            ED("invalid max-hold-time: %s", av[2]);
+            return 1;
+        }
+    }
+    if (delay > q->ec->max_delay)
+        q->ec->max_delay = delay;
+    if (bps > q->ec->max_bps)
+        q->ec->max_bps = bps;
+    if (hold > q->ec->max_hold_delay)
+        q->ec->max_hold_delay = hold;
+    return 0;
 }
 
+/* options that can be specified for each direction */
+struct dir_opt {
+    char opt;
+    int  flags;
+#define DOPT_CLONE  1	/* clone if only one is given */
+#define DOPT_IGNOR  2	/* ignore the option */
+    const char *arg[EC_NOPTS];
+};
+#define MAXOPTS 1024
+#define DOPT(a, f)  { .opt = a, .flags = f, .arg = { NULL, NULL } }
+
+/* mapping between options and configurations */
+struct cfg_opt {
+    int opt;
+    struct _cfg *c;
+};
+
+struct cfg_opt all_cfgs[] = {
+    [I_DELAY]	= { 'D', delay_cfg },
+    [I_BW]    	= { 'B', bw_cfg },
+    [I_LOSS]  	= { 'L', loss_cfg },
+    [I_REORDER]	= { 'R', reorder_cfg },
+};
+
 int
 main(int argc, char **argv)
 {
-	int ch, i, err=0;
+    int ch, i, j, err=0;
+
+    struct pipe_args bp[EC_NOPTS];
+    struct dir_opt dopt[] = {
+        DOPT('B', DOPT_CLONE), /* bandwidth in bps */
+        DOPT('D', DOPT_CLONE), /* delay in seconds (float) */
+        DOPT('Q', DOPT_CLONE), /* qsize in bytes */
+        DOPT('L', DOPT_CLONE), /* loss probability */
+        DOPT('R', DOPT_CLONE), /* reordering */
+        DOPT('G', 0),	       /* default gateway */
+        DOPT('M', DOPT_CLONE), /* max bw, delay and hold-time */
+        DOPT('P', DOPT_CLONE), /* allow dropping to obtain precise delay */
+        DOPT('i', 0),	       /* interface */
+        DOPT('O', DOPT_CLONE), /* delay offset */
+#ifdef WITH_MAX_LAG
+        DOPT('d', DOPT_CLONE),
+#else
+        DOPT('d', DOPT_IGNOR),
+#endif /* WITH_MAX_LAG */
+        DOPT(0, 0)  /* end of options */
+    };
+    struct dir_opt *invdopt[256], *scandopt;
+    int ncpus;
+    int cores[4];
+    int hugepages = 0;
+    char *sfname = NULL; /* session file name */
+    int server = 1, terminate = 0;
+    struct _ecf *ecf;
+    char doptstr[MAXOPTS], *strp = doptstr;
+    const char **ifname;
+
+    nmctx_set_threadsafe();
+
+    bzero(invdopt, sizeof(invdopt));
+    for (scandopt = dopt; scandopt->opt; scandopt++) {
+        bzero(scandopt->arg, sizeof(scandopt->arg));
+        invdopt[(unsigned int)scandopt->opt] = scandopt;
+        *strp++ = scandopt->opt;
+        *strp++ = ':';
+    }
+    *strp = '\0';
+    ifname = invdopt['i']->arg;
 
-#define	N_OPTS	2
-	struct pipe_args bp[N_OPTS];
-	const char *d[N_OPTS], *b[N_OPTS], *l[N_OPTS], *q[N_OPTS], *ifname[N_OPTS];
-	int cores[4] = { 2, 8, 4, 10 }; /* default values */
+    bzero(&bp, sizeof(bp));	/* all data initially go here */
 
-	bzero(d, sizeof(d));
-	bzero(b, sizeof(b));
-	bzero(l, sizeof(l));
-	bzero(q, sizeof(q));
-	bzero(ifname, sizeof(ifname));
+    for (i = 0; i < EC_NOPTS; i++) {
+        struct _qs *q = &bp[i].q;
+        uint64_t seed = time(0);
+	int j;
 
-	fprintf(stderr, "%s built %s %s\n", argv[0], __DATE__, __TIME__);
+        memcpy(q->prod_seed, &seed, sizeof(q->prod_seed));
+	for (j = 0; j < I_NUM; j++) {
+	    q->c_imp[j].optarg = "0";
+	    q->c_imp[j].run = null_run_fn;
+	}
+    }
 
-	bzero(&bp, sizeof(bp));	/* all data initially go here */
+    ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+    if (ncpus <= 0) {
+        ED("failed to get the number of online CPUs: %s",
+                strerror(errno));
+        cores[0] = cores[1] = cores[2] = cores[3] = 0;
+    } else {
+        /* try to put prod/cons on two HT of the same core */
+        int h = ncpus / 2;
+        cores[0] = h / 3;
+        cores[1] = cores[0] + h;
+        cores[2] = (2 * h) / 3;
+        cores[3] = cores[2] + h;
+    }
 
-	for (i = 0; i < N_OPTS; i++) {
-	    struct _qs *q = &bp[i].q;
-	    q->c_delay.optarg = "0";
-	    q->c_delay.run = null_run_fn;
-	    q->c_loss.optarg = "0";
-	    q->c_loss.run = null_run_fn;
-	    q->c_bw.optarg = "0";
-	    q->c_bw.run = null_run_fn;
-	}
+    // Options:
+    // B	bandwidth in bps
+    // D	delay in seconds
+    // Q	qsize in bytes
+    // L	loss probability
+    // R	reordering probability and delay min/max
+    // i	interface name (two mandatory)
+    // v	verbose
+    // b	batch size
+    // r	route mode
+    // d	max consumer delay
+
+    strcat(doptstr, "C:b:cvw:rHs:qa");
+    while ( (ch = getopt(argc, argv, doptstr)) != -1) {
+        switch (ch) {
+            case '?':
+                ED("unknown option '-%c'", optopt);
+                break;
+            case 'C': /* CPU placement, up to 4 arguments */
+                {
+                    int ac = 0;
+                    char **av = split_arg(optarg, &ac);
+                    if (ac == 1) { /* sequential after the first */
+                        cores[0] = atoi(av[0]);
+                        cores[1] = cores[0] + 1;
+                        cores[2] = cores[1] + 1;
+                        cores[3] = cores[2] + 1;
+                    } else if (ac == 2) { /* two sequential pairs */
+                        cores[0] = atoi(av[0]);
+                        cores[1] = cores[0] + 1;
+                        cores[2] = atoi(av[1]);
+                        cores[3] = cores[2] + 1;
+                    } else if (ac == 4) { /* four values */
+                        cores[0] = atoi(av[0]);
+                        cores[1] = atoi(av[1]);
+                        cores[2] = atoi(av[2]);
+                        cores[3] = atoi(av[3]);
+                    } else {
+                        ED(" -C accepts 1, 2 or 4 comma separated arguments");
+                        usage();
+                    }
+                    if (av)
+                        free(av);
+                }
+                break;
+
+            case 'b':	/* burst */
+                bp[0].q.burst = atoi(optarg);
+                break;
+
+            case 'c':
+                bp[0].zerocopy = 0; /* do not zerocopy */
+                break;
+            case 'v':
+                verbose++;
+                break;
+            case 'q':
+                if (verbose > 0)
+                    verbose--;
+                break;
+            case 'w':
+                bp[0].wait_link = atoi(optarg);
+                break;
+            case 'r':
+                bp[0].route_mode = 1;
+                break;
+            case 'H':
+                hugepages++;
+                break;
+            case 's':
+                if (sfname != NULL) {
+                    D("option 's' duplicated");
+                    usage();
+                }
+                sfname = optarg;
+                break;
+            case 'a':
+                terminate = 1;
+                break;
+            default:
+                if (invdopt[ch]) {
+                    struct dir_opt *o = invdopt[ch];
+                    if (!(o->flags & DOPT_IGNOR)) {
+                        add_to(o->arg, EC_NOPTS, optarg, o->opt);
+                    } else {
+                        ED("option '-%c' ignored", o->opt);
+                    }
+                } else {
+                    ED("unknown option '-%c'", ch);
+                }
+        }
+    }
 
-	// Options:
-	// B	bandwidth in bps
-	// D	delay in seconds
-	// Q	qsize in bytes
-	// L	loss probability
-	// i	interface name (two mandatory)
-	// v	verbose
-	// b	batch size
-
-	while ( (ch = getopt(argc, argv, "B:C:D:L:Q:b:ci:vw:")) != -1) {
-		switch (ch) {
-		default:
-			D("bad option %c %s", ch, optarg);
-			usage();
-			break;
-
-		case 'C': /* CPU placement, up to 4 arguments */
-			{
-				int ac = 0;
-				char **av = split_arg(optarg, &ac);
-				if (ac == 1) { /* sequential after the first */
-					cores[0] = atoi(av[0]);
-					cores[1] = cores[0] + 1;
-					cores[2] = cores[1] + 1;
-					cores[3] = cores[2] + 1;
-				} else if (ac == 2) { /* two sequential pairs */
-					cores[0] = atoi(av[0]);
-					cores[1] = cores[0] + 1;
-					cores[2] = atoi(av[1]);
-					cores[3] = cores[2] + 1;
-				} else if (ac == 4) { /* four values */
-					cores[0] = atoi(av[0]);
-					cores[1] = atoi(av[1]);
-					cores[2] = atoi(av[2]);
-					cores[3] = atoi(av[3]);
-				} else {
-					ED(" -C accepts 1, 2 or 4 comma separated arguments");
-					usage();
-				}
-				if (av)
-					free(av);
-			}
-			break;
-
-		case 'B': /* bandwidth in bps */
-			add_to(b, N_OPTS, optarg, "-B too many times");
-			break;
-
-		case 'D': /* delay in seconds (float) */
-			add_to(d, N_OPTS, optarg, "-D too many times");
-			break;
-
-		case 'Q': /* qsize in bytes */
-			add_to(q, N_OPTS, optarg, "-Q too many times");
-			break;
-
-		case 'L': /* loss probability */
-			add_to(l, N_OPTS, optarg, "-L too many times");
-			break;
-
-		case 'b':	/* burst */
-			bp[0].q.burst = atoi(optarg);
-			break;
-
-		case 'i':	/* interface */
-			add_to(ifname, N_OPTS, optarg, "-i too many times");
-			break;
-		case 'c':
-			bp[0].zerocopy = 0; /* do not zerocopy */
-			break;
-		case 'v':
-			verbose++;
-			break;
-		case 'w':
-			bp[0].wait_link = atoi(optarg);
-			break;
-		}
+    argc -= optind;
+    argv += optind;
 
-	}
+    /* map the session area and auto-detect whether we are server or client */
+    ecf = ec_map(sfname, &server);
+    if (ecf == NULL)
+        exit(1);
 
-	argc -= optind;
-	argv += optind;
+    if (terminate)
+        goto skip_args;
 
-	/*
-	 * consistency checks for common arguments
-	 */
-	if (!ifname[0] || !ifname[0]) {
-		ED("missing interface(s)");
-		usage();
-	}
-	if (strcmp(ifname[0], ifname[1]) == 0) {
-		ED("must specify two different interfaces %s %s", ifname[0], ifname[1]);
-		usage();
-	}
-	if (bp[0].q.burst < 1 || bp[0].q.burst > 8192) {
-		ED("invalid burst %d, set to 1024", bp[0].q.burst);
-		bp[0].q.burst = 1024; // XXX 128 is probably better
+    /*
+     * consistency checks for common arguments
+     */
+    if (server) {
+        if (!ifname[0] || !ifname[1]) {
+            ED("missing interface(s)");
+            usage();
+        }
+        if (strcmp(ifname[0], ifname[1]) == 0) {
+            ED("must specify two different interfaces %s %s", ifname[0], ifname[1]);
+            usage();
+        }
+        if (bp[0].q.burst < 1 || bp[0].q.burst > 8192) {
+            ED("invalid burst %d, set to 1024", bp[0].q.burst);
+            bp[0].q.burst = 1024; // XXX 128 is probably better
+        }
+        if (bp[0].wait_link > 100) {
+            ED("invalid wait_link %d, set to 4", bp[0].wait_link);
+            bp[0].wait_link = 4;
+        }
+
+        if (bp[0].route_mode) {
+	    const char *gateways[] = { invdopt['G']->arg[0], invdopt['G']->arg[1] };
+	    route_mode_init(ifname, gateways);
+        }
+
+        bp[1] = bp[0]; /* copy parameters, but swap interfaces */
+        bp[0].q.prod_ifname = bp[1].q.cons_ifname = ifname[0];
+        bp[1].q.prod_ifname = bp[0].q.cons_ifname = ifname[1];
+        bp[0].prod_ipv4 = bp[1].cons_ipv4 = &ipv4[0];
+        bp[0].cons_ipv4 = bp[1].prod_ipv4 = &ipv4[1];
+
+
+        /* assign cores. prod and cons work better if on the same HT */
+        bp[0].cons_core = cores[0];
+        bp[0].prod_core = cores[1];
+        bp[1].cons_core = cores[2];
+        bp[1].prod_core = cores[3];
+        ED("running on cores %d->%d %d->%d", cores[1], cores[0], cores[3], cores[2]);
+
+    }
+
+    /* use same parameters for both directions if needed */
+    for (scandopt = dopt; scandopt->opt; scandopt++) {
+        if (!(scandopt->flags & DOPT_CLONE))
+            continue;
+        if (scandopt->arg[1] == NULL)
+            scandopt->arg[1] = scandopt->arg[0];
+    }
+
+skip_args:
+    /* apply commands */
+    j = 0;
+    for (i = 0; i < EC_NOPTS; i++) { /* once per queue */
+        struct _qs *q = &bp[i].q;
+        struct _eci *a;
+	int k;
+
+        if (ec_init(q, &ecf->sets[i], server))
+            exit(1);
+        if (terminate) {
+            ec_terminate(&ecf->sets[i]);
+            continue;
+        }
+        a = &q->ec->instances[q->ec_active];
+	for (k = 0; k < I_NUM; k++) {
+	    err += cmd_apply(all_cfgs[k].c, invdopt[all_cfgs[k].opt]->arg[i], q, &q->c_imp[k]);
 	}
-	if (bp[0].wait_link > 100) {
-		ED("invalid wait_link %d, set to 4", bp[0].wait_link);
-		bp[0].wait_link = 4;
+#ifdef WITH_MAX_LAG
+        if (invdopt['d']->arg[i] != NULL) {
+            uint64_t max_lag = parse_time(invdopt[(int)'d']->arg[i]);
+            if (max_lag == U_PARSE_ERR) {
+                err++;
+            } else {
+                bp[i].max_lag = max_lag;
+            }
+        }
+#endif /* WITH_MAX_LAG */
+        if (invdopt['P']->arg[i] != NULL) {
+            const char *p = invdopt['P']->arg[i];
+            if (!strcmp(p, "0") || !strcmp(p, "1")) {
+                a->ec_allow_drop = atoi(p);
+                q->allow_drop = a->ec_allow_drop;
+            } else {
+                ED("-P expects either 0 or 1");
+                err++;
+            }
+        }
+        if (invdopt['O']->arg[i] != NULL) {
+            a->ec_delay_offset = parse_time(invdopt['O']->arg[i]);
+            q->delay_offset = a->ec_delay_offset;
+        }
+	if (invdopt['Q']->arg[i] != NULL) {
+            a->ec_qsize = parse_qsize(invdopt['Q']->arg[0]);
+            q->qsize = a->ec_qsize;
+	} else if (invdopt['B']->arg[i] != NULL) {
+	    /* we need e small finite queue for bandwidth emulation,
+	     * otherwise delay is unbounded
+	     */
+	    ED("setting qsize to %lluB", (unsigned long long)q->c_imp[I_BW].def_qsize);
+	    a->ec_qsize = q->c_imp[I_BW].def_qsize;
+	    q->qsize = q->c_imp[I_BW].def_qsize;
+	} else {
+	    ED("using unlimited qsize");
+	    a->ec_qsize = 0;
+	    q->qsize = 0; /* infinite */
 	}
+        bp[i].q.txstats = &ecf->stats[j++];
+        bp[i].q.rxstats = &ecf->stats[j++];
+    }
 
-	bp[1] = bp[0]; /* copy parameters, but swap interfaces */
-	bp[0].q.prod_ifname = bp[1].q.cons_ifname = ifname[0];
-	bp[1].q.prod_ifname = bp[0].q.cons_ifname = ifname[1];
-
-	/* assign cores. prod and cons work better if on the same HT */
-	bp[0].cons_core = cores[0];
-	bp[0].prod_core = cores[1];
-	bp[1].cons_core = cores[2];
-	bp[1].prod_core = cores[3];
-	ED("running on cores %d %d %d %d", cores[0], cores[1], cores[2], cores[3]);
-
-	/* use same parameters for both directions if needed */
-	if (d[1] == NULL)
-		d[1] = d[0];
-	if (b[1] == NULL)
-		b[1] = b[0];
-	if (l[1] == NULL)
-		l[1] = l[0];
-
-	/* apply commands */
-	for (i = 0; i < N_OPTS; i++) { /* once per queue */
-		struct _qs *q = &bp[i].q;
-		err += cmd_apply(delay_cfg, d[i], q, &q->c_delay);
-		err += cmd_apply(bw_cfg, b[i], q, &q->c_bw);
-		err += cmd_apply(loss_cfg, l[i], q, &q->c_loss);
-	}
+    if (terminate) {
+        int rv = 0;
+        ED("exiting due to -a");
+        if (!server)
+            rv = ec_waitterminate();
+        exit(rv);
+    }
 
-	if (q[0] == NULL)
-		q[0] = "0";
-	if (q[1] == NULL)
-		q[1] = q[0];
-	bp[0].q.qsize = parse_qsize(q[0]);
-	bp[1].q.qsize = parse_qsize(q[1]);
+    if (err) {
+        ED("exiting due to %d error(s)", err);
+        exit(1);
+    }
 
-	if (bp[0].q.qsize == 0) {
-		ED("qsize= 0 is not valid, set to 50k");
-		bp[0].q.qsize = 50000;
-	}
-	if (bp[1].q.qsize == 0) {
-		ED("qsize= 0 is not valid, set to 50k");
-		bp[1].q.qsize = 50000;
+    if (server) {
+	/* lock everything in core */
+	if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0) {
+	    ED("failed to lock memory: %s", strerror(errno));
 	}
+        /* set the maximum values */
+        for (i = 0; i < EC_NOPTS; i++) {
+            if (set_max(invdopt['M']->arg[i], &bp[i].q))
+                exit(1);
+        }
+        /* now the clients may send new configurations */
+        if (ec_allowclients())
+            exit(1);
+    } else {
+        for (i = 0; i < EC_NOPTS; i++)
+            ec_switchactive(&bp[i].q);
+        exit(0);
+    }
+
+#ifdef WITH_MAX_LAG
+    for (i = 0; i < EC_NOPTS; i++) {
+        if (bp[i].max_lag == 0) {
+            bp[i].max_lag = 100000; /* 100 us */
+        }
+    }
+#endif /* WITH_MAX_LAG */
+
+    /* assign arp command queues for route mode */
+    bp[0].prod_arpq = &arpq[0];
+    bp[0].cons_arpq = &arpq[1];
+    bp[1].prod_arpq = &arpq[1];
+    bp[1].cons_arpq = &arpq[0];
+
+    /* hugepages */
+    if (hugepages) {
+#ifdef MAP_HUGETLB
+        ED("using hugepages");
+        bp[0].hugepages = bp[1].hugepages = hugepages;
+#else /* !MAP_HUGETLB */
+        ED("WARNING: hugepages not supported");
+        hugepages = 0;
+#endif /* MAP_HUGETLB */
+    }
 
-	pthread_create(&bp[0].cons_tid, NULL, tlem_main, (void*)&bp[0]);
-	pthread_create(&bp[1].cons_tid, NULL, tlem_main, (void*)&bp[1]);
-
-	signal(SIGINT, sigint_h);
-	sleep(1);
-	while (!do_abort) {
-	    struct _qs olda = bp[0].q, oldb = bp[1].q;
-	    struct _qs *q0 = &bp[0].q, *q1 = &bp[1].q;
-
-	    sleep(1);
-	    ED("%ld -> %ld maxq %d round %ld, %ld <- %ld maxq %d round %ld",
-		(_P64)(q0->rx - olda.rx), (_P64)(q0->tx - olda.tx),
-		q0->rx_qmax, (_P64)q0->prod_max_gap,
-		(_P64)(q1->rx - oldb.rx), (_P64)(q1->tx - oldb.tx),
-		q1->rx_qmax, (_P64)q1->prod_max_gap
-		);
-	    ED("plr nominal %le actual %le",
-		(double)(q0->c_loss.d[0])/(1<<24),
-		q0->c_loss.d[1] == 0 ? 0 :
-		(double)(q0->c_loss.d[2])/q0->c_loss.d[1]);
-	    bp[0].q.rx_qmax = (bp[0].q.rx_qmax * 7)/8; // ewma
-	    bp[0].q.prod_max_gap = (bp[0].q.prod_max_gap * 7)/8; // ewma
-	    bp[1].q.rx_qmax = (bp[1].q.rx_qmax * 7)/8; // ewma
-	    bp[1].q.prod_max_gap = (bp[1].q.prod_max_gap * 7)/8; // ewma
+    for (i = 0; i < 2; i++) {
+        struct pipe_args *a = &bp[i];
+
+        a->pa = nmport_prepare(a->q.prod_ifname);
+        if (a->pa == NULL) {
+            D("cannot open %s", a->q.prod_ifname);
+            exit(1);
+        }
+        a->pa->reg.nr_flags |= NETMAP_NO_TX_POLL;
+        if (nmport_open_desc(a->pa) < 0) {
+            D("cannot open %s", a->q.prod_ifname);
+	    exit(1);
+        }
+	if (a->pa->first_rx_ring != a->pa->last_rx_ring) {
+	    D("WARNING: %s has more than one rx ring; only ring %d will be used",
+			    a->q.prod_ifname, a->pa->first_rx_ring);
 	}
-	D("exiting on abort");
-	sleep(1);
+	a->q.rxring = NETMAP_RXRING(a->pa->nifp, a->pa->first_rx_ring);
+        a->pb = nmport_open(a->q.cons_ifname);
+        if (a->pb == NULL) {
+            D("cannot open %s", a->q.cons_ifname);
+            exit(1);
+        }
 
-	return (0);
+    }
+    sleep(bp[0].wait_link);
+
+    latency_reduction_start();
+
+    pthread_create(&bp[0].cons_tid, NULL, tlem_main, (void*)&bp[0]);
+    pthread_create(&bp[1].cons_tid, NULL, tlem_main, (void*)&bp[1]);
+
+    signal(SIGINT, sigint_h);
+    sleep(1);
+    while (!do_abort) {
+        struct stats old0tx = *bp[0].q.txstats,
+                     old0rx = *bp[0].q.rxstats,
+                     old1tx = *bp[1].q.txstats,
+                     old1rx = *bp[1].q.rxstats;
+        struct _qs *q0 = &bp[0].q, *q1 = &bp[1].q;
+
+        sleep(1);
+        ED("%lld -> %lld maxq %d round %lld drop %lld/%lld, %lld <- %lld maxq %d round %lld drop %lld/%lld",
+                (long long)(q0->rxstats->packets - old0rx.packets),
+                (long long)(q0->txstats->packets - old0tx.packets),
+                q0->rx_qmax, (long long)q0->prod_max_gap,
+                (long long)(q0->txstats->drop_packets - old0tx.drop_packets),
+                (long long)(q0->rxstats->drop_packets - old0rx.drop_packets),
+                (long long)(q1->rxstats->packets - old1rx.packets),
+                (long long)(q1->txstats->packets - old1tx.packets),
+                q1->rx_qmax, (long long)q1->prod_max_gap,
+                (long long)(q1->txstats->drop_packets - old1tx.drop_packets),
+                (long long)(q1->rxstats->drop_packets - old1rx.drop_packets)
+          );
+        ND("plr nominal %le actual %le",
+                (double)(q0->c_loss.d[0])/(1<<24),
+                q0->c_loss.d[1] == 0 ? 0 :
+                (double)(q0->c_loss.d[2])/q0->c_loss.d[1]);
+        bp[0].q.rx_qmax = 0;
+        bp[0].q.prod_max_gap = 0;
+        bp[1].q.rx_qmax = 0;
+        bp[1].q.prod_max_gap = 0;
+    }
+    ED("exiting on abort");
+    sleep(1);
+
+    latency_reduction_stop();
+
+    return (0);
 }
 
 /* conversion factor for numbers.
@@ -1295,52 +2696,53 @@ struct _sm {	/* string and multiplier */
 static double
 parse_gen(const char *arg, const struct _sm *conv, int *err)
 {
-	double d;
-	char *ep;
-	int dummy;
-
-	if (err == NULL)
-		err = &dummy;
-	*err = 0;
-	if (arg == NULL)
-		goto error;
-	d = strtod(arg, &ep);
-	if (ep == arg) { /* no value */
-		ED("bad argument %s", arg);
-		goto error;
-	}
-	/* special case, no conversion */
-	if (conv == NULL && *ep == '\0')
-		goto done;
-	ND("checking %s [%s]", arg, ep);
-	for (;conv->s; conv++) {
-		if (strchr(conv->s, *ep))
-			goto done;
-	}
+    double d;
+    char *ep;
+    int dummy;
+
+    if (err == NULL)
+        err = &dummy;
+    *err = 0;
+    if (arg == NULL)
+        goto error;
+    d = strtod(arg, &ep);
+    if (ep == arg) { /* no value */
+        ED("bad argument %s", arg);
+        goto error;
+    }
+    if (conv == NULL) {
+        if (*ep == '\0') /* special case, no conversion */
+            goto done;
+        ED("bad suffix %s", ep);
+        goto error;
+    }
+    ND("checking %s [%s]", arg, ep);
+    for (;conv->s; conv++) {
+        if (strchr(conv->s, *ep))
+            goto done;
+    }
 error:
-	*err = 1;	/* unrecognised */
-	return 0;
+    *err = 1;	/* unrecognised */
+    return 0;
 
 done:
-	if (conv) {
-		ND("scale is %s %lf", conv->s, conv->m);
-		d *= conv->m; /* apply default conversion */
-	}
-	ND("returning %lf", d);
-	return d;
+    if (conv) {
+        ND("scale is %s %lf", conv->s, conv->m);
+        d *= conv->m; /* apply default conversion */
+    }
+    ND("returning %lf", d);
+    return d;
 }
 
-#define U_PARSE_ERR ~(0ULL)
-
 /* returns a value in nanoseconds */
 static uint64_t
 parse_time(const char *arg)
 {
     struct _sm a[] = {
-	{"", 1000000000 /* seconds */},
-	{"n", 1 /* nanoseconds */}, {"u", 1000 /* microseconds */},
-	{"m", 1000000 /* milliseconds */}, {"s", 1000000000 /* seconds */},
-	{NULL, 0 /* seconds */}
+        {"", 1000000000 /* seconds */},
+        {"n", 1 /* nanoseconds */}, {"u", 1000 /* microseconds */},
+        {"m", 1000000 /* milliseconds */}, {"s", 1000000000 /* seconds */},
+        {NULL, 0 /* seconds */}
     };
     int err;
     uint64_t ret = (uint64_t)parse_gen(arg, a, &err);
@@ -1355,7 +2757,7 @@ static uint64_t
 parse_bw(const char *arg)
 {
     struct _sm a[] = {
-	{"", 1}, {"kK", 1000}, {"mM", 1000000}, {"gG", 1000000000}, {NULL, 0}
+        {"", 1}, {"kK", 1000}, {"mM", 1000000}, {"gG", 1000000000}, {NULL, 0}
     };
     int err;
     uint64_t ret = (uint64_t)parse_gen(arg, a, &err);
@@ -1369,7 +2771,7 @@ static uint64_t
 parse_qsize(const char *arg)
 {
     struct _sm a[] = {
-	{"", 1}, {"kK", 1024}, {"mM", 1024*1024}, {"gG", 1024*1024*1024}, {NULL, 0}
+        {"", 1}, {"kK", 1024}, {"mM", 1024*1024}, {"gG", 1024*1024*1024}, {NULL, 0}
     };
     int err;
     uint64_t ret = (uint64_t)parse_gen(arg, a, &err);
@@ -1384,9 +2786,9 @@ parse_qsize(const char *arg)
 
 #include  /* log, exp etc. */
 static inline uint64_t
-my_random24(void)	/* 24 useful bits */
+my_random24(struct _qs *q)	/* 24 useful bits */
 {
-	return random() & ((1<<24) - 1);
+    return nrand48(q->prod_seed) & ((1<<24) - 1);
 }
 
 
@@ -1504,6 +2906,20 @@ BANDWIDTH emulation	-B option_arguments
     ether,b		constant bw, including ethernet framing
 			(20 bytes framing + 4 bytes crc)
 
+REORDERING emulation 	-R option_arguments
+
+    NOTE: The config function should store, in q->max_hold_delay,
+    a reasonable estimate of the maximum hold delay applied to the packets
+    as this is needed to size the memory buffer used to hold reordered
+    packets.
+
+    If the option is not supplied, the system does not reorder packets.
+
+    Currently implemented options
+
+    const,p,t		hold packets for t ns, with probability t
+
+
 #endif /* end of comment block */
 
 /*
@@ -1513,62 +2929,87 @@ BANDWIDTH emulation	-B option_arguments
  * as this is used to size the queue.
  */
 
+static int
+update_max_delay(struct _qs *q, uint64_t delay)
+{
+    if (q->ec->max_delay) {
+        if (q->ec->max_delay < delay) {
+            ED("invalid new delay %lld (max %lld)",
+                    (long long)delay, (long long)q->ec->max_delay);
+            return 1;
+        }
+    } else {
+        q->ec->max_delay = delay;
+    }
+    return 0;
+}
+
 /* constant delay, also accepts just a number */
 static int
 const_delay_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
 {
-	uint64_t delay;
-
-	if (strncmp(av[0], "const", 5) != 0 && ac > 1)
-		return 2; /* unrecognised */
-	if (ac > 2)
-		return 1; /* error */
-	delay = parse_time(av[ac - 1]);
-	if (delay == U_PARSE_ERR)
-		return 1; /* error */
-	dst->d[0] = delay;
-	q->max_delay = delay;
-	return 0;	/* success */
+    uint64_t delay, *d;
+
+    if (strncmp(av[0], "const", 5) != 0 && ac > 1)
+        return 2; /* unrecognised */
+    if (ac > 2)
+        return 1; /* error */
+    delay = parse_time(av[ac - 1]);
+    if (delay == U_PARSE_ERR)
+        return 1; /* error */
+    if (update_max_delay(q, delay))
+        return 1;
+    dst->arg = ec_alloc(q, dst->ec, sizeof(uint64_t));
+    if (dst->arg == NULL)
+        return 1;
+    d = dst->arg;
+    d[0] = delay;
+    return 0;	/* success */
 }
 
 /* runtime function, store the delay into q->cur_delay */
 static int
 const_delay_run(struct _qs *q, struct _cfg *arg)
 {
-	q->cur_delay = arg->d[0]; /* the delay */
-	return 0;
+    uint64_t *d = arg->arg;
+    q->cur_delay = d[0]; /* the delay */
+    return 0;
 }
 
 static int
 uniform_delay_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
 {
-	uint64_t dmin, dmax;
-
-	(void)q;
-	if (strcmp(av[0], "uniform") != 0)
-		return 2; /* not recognised */
-	if (ac != 3)
-		return 1; /* error */
-	dmin = parse_time(av[1]);
-	dmax = parse_time(av[2]);
-	if (dmin == U_PARSE_ERR || dmax == U_PARSE_ERR || dmin > dmax)
-		return 1;
-	D("dmin %ld dmax %ld", (_P64)dmin, (_P64)dmax);
-	dst->d[0] = dmin;
-	dst->d[1] = dmax;
-	dst->d[2] = dmax - dmin;
-	q->max_delay = dmax;
-	return 0;
+    uint64_t dmin, dmax, *d;
+
+    if (strcmp(av[0], "uniform") != 0)
+        return 2; /* not recognised */
+    if (ac != 3)
+        return 1; /* error */
+    dmin = parse_time(av[1]);
+    dmax = parse_time(av[2]);
+    if (dmin == U_PARSE_ERR || dmax == U_PARSE_ERR || dmin > dmax)
+        return 1;
+    ED("dmin %lld dmax %lld", (long long)dmin, (long long)dmax);
+    if (update_max_delay(q, dmax))
+        return 1;
+    dst->arg = ec_alloc(q, dst->ec, 3 * sizeof(uint64_t));
+    if (dst->arg == NULL)
+        return 1;
+    d = dst->arg;
+    d[0] = dmin;
+    d[1] = dmax;
+    d[2] = dmax - dmin;
+    return 0;
 }
 
 static int
 uniform_delay_run(struct _qs *q, struct _cfg *arg)
 {
-	uint64_t x = my_random24();
-	q->cur_delay = arg->d[0] + ((arg->d[2] * x) >> 24);
+    uint64_t x = my_random24(q), *d = arg->arg;
+    q->cur_delay = d[0] + ((d[2] * x) >> 24);
 #if 0 /* COMPUTE_STATS */
 #endif /* COMPUTE_STATS */
-	return 0;
+    return 0;
 }
 
 /*
@@ -1586,44 +3027,86 @@ static int
 exp_delay_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
 {
 #define	PTS_D_EXP	512
-	uint64_t i, d_av, d_min, *t; /*table of values */
-
-        (void)q;
-        if (strcmp(av[0], "exp") != 0)
-		return 2; /* not recognised */
-        if (ac != 3)
-                return 1; /* error */
-        d_av = parse_time(av[1]);
-        d_min = parse_time(av[2]);
-        if (d_av == U_PARSE_ERR || d_min == U_PARSE_ERR || d_av < d_min)
-                return 1; /* error */
-	d_av -= d_min;
-	dst->arg_len = PTS_D_EXP * sizeof(uint64_t);
-	dst->arg = calloc(1, dst->arg_len);
-	if (dst->arg == NULL)
-		return 1; /* no memory */
-	t = (uint64_t *)dst->arg;
-        q->max_delay = d_av * 4 + d_min; /* exp(-4) */
-	/* tabulate -ln(1-n)*delay  for n in 0..1 */
-	for (i = 0; i < PTS_D_EXP; i++) {
-		double d = -log2 ((double)(PTS_D_EXP - i) / PTS_D_EXP) * d_av + d_min;
-		t[i] = (uint64_t)d;
-		ND(5, "%ld: %le", i, d);
-	}
-        return 0;
+    uint64_t i, d_av, d_min, d_max, *t; /*table of values */
+
+    if (strcmp(av[0], "exp") != 0)
+        return 2; /* not recognised */
+    if (ac != 3)
+        return 1; /* error */
+    d_min = parse_time(av[1]);
+    d_av = parse_time(av[2]);
+    if (d_av == U_PARSE_ERR || d_min == U_PARSE_ERR || d_av < d_min)
+        return 1; /* error */
+    d_max = d_av * 4 + d_min; /* exp(-4) */
+    if (update_max_delay(q, d_max))
+        return 1;
+    d_av -= d_min;
+    dst->arg = ec_alloc(q, dst->ec, PTS_D_EXP * sizeof(uint64_t));
+    if (dst->arg == NULL)
+        return 1; /* no memory */
+    t = (uint64_t *)dst->arg;
+    /* tabulate -ln(1-n)*delay  for n in 0..1 */
+    for (i = 0; i < PTS_D_EXP; i++) {
+        double d = -log ((double)(PTS_D_EXP - i) / PTS_D_EXP) * d_av + d_min;
+        t[i] = (uint64_t)d;
+        ND(5, "%ld: %le", i, d);
+    }
+    return 0;
 }
 
 static int
 exp_delay_run(struct _qs *q, struct _cfg *arg)
 {
-	uint64_t *t = (uint64_t *)arg->arg;
-        q->cur_delay = t[my_random24() & (PTS_D_EXP - 1)];
-	RD(5, "delay %lu", (_P64)q->cur_delay);
-        return 0;
+    uint64_t *t = (uint64_t *)arg->arg;
+    q->cur_delay = t[my_random24(q) & (PTS_D_EXP - 1)];
+    ND(5, "delay %llu", (unsigned long long)q->cur_delay);
+    return 0;
 }
 
+static int
+interpacket_delay_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
+{
+    uint64_t delay, gmin, gmax, *d;
+    if (strcmp(av[0], "inter-packet") != 0)
+        return 2; /* not recognized */
+    if (ac != 4)
+        return 1; /* error */
+    gmin = parse_time(av[1]);
+    gmax = parse_time(av[2]);
+    delay = parse_time(av[3]);
+    if (gmin == U_PARSE_ERR || gmax == U_PARSE_ERR || delay == U_PARSE_ERR
+            || gmin > gmax)
+        return 1;
+    ED("min-gap %lld max-gap %lld delay %lld",
+            (long long)gmin, (long long)gmax, (long long)delay);
+    if (update_max_delay(q, delay))
+        return 1;
+    dst->arg = ec_alloc(q, dst->ec, 4 * sizeof(uint64_t));
+    if (dst->arg == NULL)
+        return 1;
+    d = dst->arg;
+    d[0] = gmin;
+    d[1] = gmax;
+    d[2] = gmax - gmin;
+    d[3] = delay;
+    return 0;
+}
+
+static int
+interpacket_delay_run(struct _qs *q, struct _cfg *arg)
+{
+    uint64_t x = my_random24(q), *d = arg->arg;
+    uint64_t gap = d[0] + ((d[2] * x) >> 24);
+    uint64_t base = q->qt_tx;
+    if (base < q->prod_now) {
+        base = q->prod_now;
+        gap = d[3];
+    }
+    q->cur_delay = (base - q->prod_now) + gap;
+    return 0;
+}
 
-#define TLEM_CFG_END	NULL, 0, {0}
+#define TLEM_CFG_END	NULL, NULL, 0
 
 static struct _cfg delay_cfg[] = {
 	{ const_delay_parse, const_delay_run,
@@ -1632,27 +3115,53 @@ static struct _cfg delay_cfg[] = {
 		"uniform,dmin,dmax # dmin <= dmax", TLEM_CFG_END },
 	{ exp_delay_parse, exp_delay_run,
 		"exp,dmin,davg # dmin <= davg", TLEM_CFG_END },
+	{ interpacket_delay_parse, interpacket_delay_run,
+	        "inter-packet,min-gap,max-gap,delay # min-gap <= max-gap", TLEM_CFG_END },
 	{ NULL, NULL, NULL, TLEM_CFG_END }
 };
 
+static int
+update_max_bw(struct _qs *q, uint64_t bw)
+{
+    if (q->ec->max_bps) {
+        if (q->ec->max_bps < bw) {
+            ED("invalid new bandwidth %lld (max %lld)",
+                    (long long)bw, (long long)q->ec->max_bps);
+            return 1;
+        }
+    } else {
+        q->ec->max_bps = bw;	/* bw used to determine queue size */
+    }
+    return 0;
+}
+
 /* standard bandwidth, also accepts just a number */
 static int
 const_bw_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
 {
-	uint64_t bw;
-
-	(void)q;
-	if (strncmp(av[0], "const", 5) != 0)
-		return 2; /* unrecognised */
-	if (ac > 2)
-		return 1; /* error */
-	bw = parse_bw(av[ac - 1]);
-	if (bw == U_PARSE_ERR) {
-		return (ac == 2) ? 1 /* error */ : 2 /* unrecognised */;
-	}
-	dst->d[0] = bw;
-	q->max_bps = bw;	/* bw used to determine queue size */
-	return 0;	/* success */
+    uint64_t bw;
+    uint32_t *d;
+    int i;
+
+    if (strncmp(av[0], "const", 5) != 0 && ac > 1)
+        return 2; /* unrecognised */
+    if (ac > 2)
+        return 1; /* error */
+    bw = parse_bw(av[ac - 1]);
+    if (bw == U_PARSE_ERR) {
+        return (ac == 2) ? 1 /* error */ : 2 /* unrecognised */;
+    }
+    dst->arg = ec_alloc(q, dst->ec, MAX_PKT * sizeof(uint32_t));
+    if (dst->arg == NULL)
+        return 1;
+    if (update_max_bw(q, bw))
+        return 1;
+    d = dst->arg;
+    for (i = 0; i < MAX_PKT; i++) {
+        d[i] = bw ? 8ULL * TIME_UNITS * i / bw : 0;
+    }
+    dst->def_qsize = 50000;
+    return 0;	/* success */
 }
 
 
@@ -1660,28 +3169,38 @@ const_bw_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
 static int
 const_bw_run(struct _qs *q, struct _cfg *arg)
 {
-	uint64_t bps = arg->d[0];
-	q->cur_tt = bps ? 8ULL* TIME_UNITS * q->cur_len / bps : 0 ;
-	return 0;
+    uint32_t *d = arg->arg;
+    q->cur_tt = d[q->cur_len];
+    q->cur_drop = 0;
+    return 0;
 }
 
 /* ethernet bandwidth, add 672 bits per packet */
 static int
 ether_bw_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
 {
-	uint64_t bw;
-
-	(void)q;
-	if (strcmp(av[0], "ether") != 0)
-		return 2; /* unrecognised */
-	if (ac != 2)
-		return 1; /* error */
-	bw = parse_bw(av[ac - 1]);
-	if (bw == U_PARSE_ERR)
-		return 1; /* error */
-	dst->d[0] = bw;
-	q->max_bps = bw;	/* bw used to determine queue size */
-	return 0;	/* success */
+    uint64_t bw;
+    uint32_t *d;
+    int i;
+
+    if (strcmp(av[0], "ether") != 0)
+        return 2; /* unrecognised */
+    if (ac != 2)
+        return 1; /* error */
+    bw = parse_bw(av[ac - 1]);
+    if (bw == U_PARSE_ERR)
+        return 1; /* error */
+    if (update_max_bw(q, bw))
+        return 1;
+    dst->arg = ec_alloc(q, dst->ec, MAX_PKT * sizeof(uint32_t));
+    if (dst->arg == NULL)
+        return 1;
+    d = dst->arg;
+    for (i = 0; i < MAX_PKT; i++) {
+        d[i] = bw ? 8ULL * TIME_UNITS * (i + 24) / bw : 0;
+    }
+    dst->def_qsize = 50000;
+    return 0;	/* success */
 }
 
 
@@ -1689,9 +3208,82 @@ ether_bw_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
 static int
 ether_bw_run(struct _qs *q, struct _cfg *arg)
 {
-	uint64_t bps = arg->d[0];
-	q->cur_tt = bps ? 8ULL * TIME_UNITS * (q->cur_len + 24) / bps : 0 ;
-	return 0;
+    uint32_t *d = arg->arg;
+    q->cur_tt = d[q->cur_len];
+    q->cur_drop = 0;
+    return 0;
+}
+
+/* token bucket. We don't limit the transmission time of
+ * each packet, but non-conforming packets are dropped
+ */
+#define WSHIFT 20
+struct avgbw_arg {
+    uint64_t token;
+    uint64_t bucket;
+    uint64_t depth;
+    uint64_t last_token;
+};
+static int
+avg_bw_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
+{
+    double bw, token;
+    struct avgbw_arg *d;
+
+    if (strcmp(av[0], "avg") != 0)
+        return 2; /* unrecognised */
+    if (ac != 2)
+        return 1; /* error */
+    bw = parse_bw(av[ac - 1]);
+    if (bw == U_PARSE_ERR)
+        return 1; /* error */
+    if (update_max_bw(q, bw))
+        return 1;
+    token = (bw / 8) * (1UL << WSHIFT) / 1e9;
+    dst->arg = ec_alloc(q, dst->ec, sizeof(*d));
+    if (dst->arg == NULL)
+        return 1;
+    d = dst->arg;
+    d->token = token;
+    d->bucket = 0;
+    d->depth = 4 * token;
+    if (d->depth < 2*MAX_PKT)
+	d->depth = 2*MAX_PKT;
+    d->last_token = 0;
+    dst->def_qsize = 0; /* skip the queue emulation */
+    D("token %lluB/%.2fms depth %llu",
+	    (unsigned long long)d->token, (1UL << WSHIFT)/1e6,
+	    (unsigned long long)d->depth);
+    return 0;	/* success */
+
+}
+
+static int
+avg_bw_run(struct _qs *q, struct _cfg *arg)
+{
+    struct avgbw_arg *d = arg->arg;
+    uint64_t now = (q->prod_now >> WSHIFT);
+    uint64_t sz = q->cur_len + 24;
+    uint64_t tokens;
+
+    /* insert all the necessary tokens */
+    tokens = (now - d->last_token) * d->token;
+    d->last_token = now;
+    d->bucket += tokens;
+    if (d->bucket > d->depth)
+	d->bucket = d->depth;
+    ND(1, "%llu: now %llu last %llu tokens %llu bucket %llu",
+		(unsigned long long)q->prod_now,
+		(unsigned long long)now,
+		(unsigned long long)d->last_token,
+		(unsigned long long)tokens,
+		(unsigned long long)d->bucket);
+    q->cur_tt = 0;
+    q->cur_drop = sz > d->bucket;
+    if (!q->cur_drop)
+	d->bucket -= sz;
+    //printf("%llu %llu\n", (unsigned long long)q->prod_now, (unsigned long long)d->bucket);
+    return 0;
 }
 
 static struct _cfg bw_cfg[] = {
@@ -1699,6 +3291,7 @@ static struct _cfg bw_cfg[] = {
 		"constant,bps", TLEM_CFG_END },
 	{ ether_bw_parse, ether_bw_run,
 		"ether,bps", TLEM_CFG_END },
+	{ avg_bw_parse, avg_bw_run, "avg,bps", TLEM_CFG_END },
 	{ NULL, NULL, NULL, TLEM_CFG_END }
 };
 
@@ -1708,35 +3301,39 @@ static struct _cfg bw_cfg[] = {
 static int
 const_plr_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
 {
-	double plr;
-	int err;
+    double plr;
+    int err;
+    uint64_t *d;
 
-	(void)q;
-	if (strcmp(av[0], "plr") != 0 && ac > 1)
-		return 2; /* unrecognised */
-	if (ac > 2)
-		return 1; /* error */
-	// XXX to be completed
-	plr = parse_gen(av[ac-1], NULL, &err);
-	if (err || plr < 0 || plr > 1)
-		return 1;
-	dst->d[0] = plr * (1<<24); /* scale is 16m */
-	if (plr != 0 && dst->d[0] == 0)
-		ED("WWW warning,  rounding %le down to 0", plr);
-	return 0;	/* success */
+    (void)q;
+    if (strcmp(av[0], "plr") != 0 && ac > 1)
+        return 2; /* unrecognised */
+    if (ac > 2)
+        return 1; /* error */
+    // XXX to be completed
+    plr = parse_gen(av[ac-1], NULL, &err);
+    if (err || plr < 0 || plr > 1)
+        return 1;
+    dst->arg = ec_alloc(q, dst->ec, 3 * sizeof(uint64_t));
+    if (dst->arg == NULL)
+        return 1;
+    d = dst->arg;
+    d[0] = plr * (1<<24); /* scale is 16m */
+    if (plr != 0 && d[0] == 0)
+        ED("WWW warning,  rounding %le down to 0", plr);
+    return 0;	/* success */
 }
 
 static int
 const_plr_run(struct _qs *q, struct _cfg *arg)
 {
-	(void)arg;
-	uint64_t r = my_random24();
-	q->cur_drop = r < arg->d[0];
+    uint64_t *d = arg->arg, r = my_random24(q);
+    q->cur_drop = r < d[0];
 #if 1	/* keep stats */
-	arg->d[1]++;
-	arg->d[2] += q->cur_drop;
+    d[1]++;
+    d[2] += q->cur_drop;
 #endif
-	return 0;
+    return 0;
 }
 
 
@@ -1748,61 +3345,64 @@ const_plr_run(struct _qs *q, struct _cfg *arg)
 static int
 const_ber_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
 {
-	double ber, ber8, cur;
-	int i, err;
-	uint32_t *plr;
-	const uint32_t mask = (1<<24) - 1;
-
-	(void)q;
-	if (strcmp(av[0], "ber") != 0)
-		return 2; /* unrecognised */
-	if (ac != 2)
-		return 1; /* error */
-	ber = parse_gen(av[ac-1], NULL, &err);
-	if (err || ber < 0 || ber > 1)
-		return 1;
-	dst->arg_len = MAX_PKT * sizeof(uint32_t);
-	plr = calloc(1, dst->arg_len);
-	if (plr == NULL)
-		return 1; /* no memory */
-	dst->arg = plr;
-	ber8 = 1 - ber;
-	ber8 *= ber8; /* **2 */
-	ber8 *= ber8; /* **4 */
-	ber8 *= ber8; /* **8 */
-	cur = 1;
-	for (i=0; i < MAX_PKT; i++, cur *= ber8) {
-		plr[i] = (mask + 1)*(1 - cur);
-		if (plr[i] > mask)
-			plr[i] = mask;
+    double ber, ber8, cur;
+    int i, err;
+    uint32_t *plr;
+    uint64_t *d;
+    const uint32_t mask = (1<<24) - 1;
+
+    (void)q;
+    if (strcmp(av[0], "ber") != 0)
+        return 2; /* unrecognised */
+    if (ac != 2)
+        return 1; /* error */
+    ber = parse_gen(av[ac-1], NULL, &err);
+    if (err || ber < 0 || ber > 1)
+        return 1;
+    dst->arg = ec_alloc(q, dst->ec,
+            3 * sizeof(uint64_t) + MAX_PKT * sizeof(uint32_t));
+    if (dst->arg == NULL)
+        return 1; /* no memory */
+    d = dst->arg;
+    plr = (uint32_t *)(d + 3);
+    ber8 = 1 - ber;
+    ber8 *= ber8; /* **2 */
+    ber8 *= ber8; /* **4 */
+    ber8 *= ber8; /* **8 */
+    cur = 1;
+    for (i=0; i < MAX_PKT; i++, cur *= ber8) {
+        plr[i] = (mask + 1)*(1 - cur);
+        if (plr[i] > mask)
+            plr[i] = mask;
 #if 0
-		if (i>= 60) //  && plr[i] < mask/2)
-			RD(50,"%4d: %le %ld", i, 1.0 - cur, (_P64)plr[i]);
+        if (i>= 60) //  && plr[i] < mask/2)
+            RD(50,"%4d: %le %ld", i, 1.0 - cur, (_P64)plr[i]);
 #endif
-	}
-	dst->d[0] = ber * (mask + 1);
-	return 0;	/* success */
+    }
+    d[0] = ber * (mask + 1);
+    return 0;	/* success */
 }
 
 static int
 const_ber_run(struct _qs *q, struct _cfg *arg)
 {
-	int l = q->cur_len;
-	uint64_t r = my_random24();
-	uint32_t *plr = arg->arg;
+    int l = q->cur_len;
+    uint64_t r = my_random24(q), *d = arg->arg;
+    uint32_t *plr = (uint32_t *)(d + 3);
 
-	if (l >= MAX_PKT) {
-		RD(5, "pkt len %d too large, trim to %d", l, MAX_PKT-1);
-		l = MAX_PKT-1;
-	}
-	q->cur_drop = r < plr[l];
+    if (l >= MAX_PKT) {
+        RD(5, "pkt len %d too large, trim to %d", l, MAX_PKT-1);
+        l = MAX_PKT-1;
+    }
+    q->cur_drop = r < plr[l];
 #if 1	/* keep stats */
-	arg->d[1] += l * 8;
-	arg->d[2] += q->cur_drop;
+    d[1] += l * 8;
+    d[2] += q->cur_drop;
 #endif
-	return 0;
+    return 0;
 }
 
+
 static struct _cfg loss_cfg[] = {
 	{ const_plr_parse, const_plr_run,
 		"plr,prob # 0 <= prob <= 1", TLEM_CFG_END },
@@ -1810,3 +3410,102 @@ static struct _cfg loss_cfg[] = {
 		"ber,prob # 0 <= prob <= 1", TLEM_CFG_END },
 	{ NULL, NULL, NULL, TLEM_CFG_END }
 };
+
+
+/*
+ * reordering
+ */
+
+static int
+update_max_hold_delay(struct _qs *q, uint64_t delay)
+{
+    if (q->ec->max_hold_delay) {
+        if (q->ec->max_hold_delay < delay) {
+            ED("invalid new hold delay %lld (max %lld)",
+                    (long long)delay,
+                    (long long)q->ec->max_hold_delay);
+            return 1;
+        }
+    } else {
+        q->ec->max_hold_delay = delay;
+    }
+    return 0;
+}
+
+static int
+const_reorder_parse(struct _qs *q, struct _cfg *dst, int ac, char *av[])
+{
+    double prob;
+    uint64_t delay, *d;
+    int err;
+
+    if (strcmp(av[0], "const") != 0 && ac != 2)
+        return 2; /* not recognized */
+    if (ac > 3)
+        return 1; /* error */
+    dst->arg = ec_alloc(q, dst->ec, 2 * sizeof(uint64_t));
+    if (dst->arg == NULL)
+        return 1; /* no memory */
+    prob = parse_gen(av[ac - 2], NULL, &err);
+    if (err || prob < 0 || prob > 1)
+        return 1;
+    d = dst->arg;
+    d[0] = prob * (1<<24);
+    if (prob != 0 && d[0] == 0)
+        ED("WWW warning,  rounding %le down to 0", prob);
+    delay = parse_time(av[ac - 1]);
+    if (delay == U_PARSE_ERR)
+        return 1;
+    if (update_max_hold_delay(q, delay))
+        return 1;
+    d[1] = delay;
+    return 0;
+}
+
+static int
+const_reorder_run(struct _qs *q, struct _cfg *arg)
+{
+    uint64_t r = my_random24(q), *d = arg->arg;
+    q->cur_hold_delay = (r < d[0] ? d[1] : 0);
+    return 0;
+}
+
+static struct _cfg reorder_cfg[] = {
+	{ const_reorder_parse, const_reorder_run,
+		"const,prob,delay # 0 <= prob <= 1", TLEM_CFG_END },
+	{ NULL, NULL, NULL, TLEM_CFG_END }
+};
+
+void
+ec_activate(struct _qs *q)
+{
+    int i = q->ec_active, j;
+    struct _eci *a = &q->ec->instances[i];
+
+    for (j = 0; j < I_NUM; j++) {
+	if (a->ec_imp[j].ec_valid) {
+	    q->c_imp[j] = all_cfgs[j].c[a->ec_imp[j].ec_index];
+	    q->c_imp[j].arg = &a->ec_data[a->ec_imp[j].ec_dataoff];
+	} else {
+	    switch (j) {
+	    case I_DELAY:
+	        q->cur_delay = 0;
+	        break;
+	    case I_BW:
+		q->cur_tt = 0;
+	        break;
+	    case I_LOSS:
+		q->cur_drop = 0;
+	        break;
+	    case I_REORDER:
+		q->cur_hold_delay = 0;
+	        break;
+	    }
+	    q->c_imp[j].run = null_run_fn;
+	}
+	q->c_imp[j].ec = &a->ec_imp[j];
+    }
+    q->allow_drop = a->ec_allow_drop;
+    q->delay_offset = a->ec_delay_offset;
+    q->qsize = a->ec_qsize;
+}
diff --git a/apps/vale-ctl/GNUmakefile b/apps/vale-ctl/GNUmakefile
index c417e92b6..d6e6acb56 100644
--- a/apps/vale-ctl/GNUmakefile
+++ b/apps/vale-ctl/GNUmakefile
@@ -11,21 +11,13 @@ VPATH = $(SRCDIR)/apps/vale-ctl
 NO_MAN=
 CFLAGS = -O2 -pipe
 CFLAGS += -Werror -Wall -Wunused-function
-CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include
+CFLAGS += -I $(SRCDIR)/sys -I $(SRCDIR)/apps/include -I $(SRCDIR)/libnetmap
 CFLAGS += -Wextra
 
-LDLIBS += -lpthread -lm
-ifeq ($(shell uname),Linux)
-	LDLIBS += -lrt	# on linux
-endif
-
-ifdef WITH_PCAP
-LDLIBS += -lpcap
-else
-CFLAGS += -DNO_PCAP
-endif
-
+LDFLAGS += -L $(BUILDDIR)/build-libnetmap
+LDLIBS = -lnetmap
 PREFIX ?= /usr/local
+MAN_PREFIX = $(if $(filter-out /,$(PREFIX)),$(PREFIX),/usr)/share/man
 
 all: $(PROGS)
 
@@ -37,3 +29,4 @@ install: $(PROGS:%=install-%)
 
 install-%:
 	install -D $* $(DESTDIR)/$(PREFIX)/bin/$*
+	-install -D -m 644 $(SRCDIR)/apps/vale-ctl/vale-ctl.4 $(DESTDIR)/$(MAN_PREFIX)/man4/vale-ctl.4
diff --git a/apps/vale-ctl/vale-ctl.8 b/apps/vale-ctl/vale-ctl.4
similarity index 52%
rename from apps/vale-ctl/vale-ctl.8
rename to apps/vale-ctl/vale-ctl.4
index a4f47c9de..0bfaea955 100644
--- a/apps/vale-ctl/vale-ctl.8
+++ b/apps/vale-ctl/vale-ctl.4
@@ -22,110 +22,137 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD$
-.\"
-.Dd December 12, 2016
-.Dt VALE-CTL 1
+.Dd March 31, 2020
+.Dt VALE-CTL 4
 .Os
 .Sh NAME
 .Nm vale-ctl
-.Nd manage the VALE switch from the netmap framework.
+.Nd manage VALE switches provided by netmap
 .Sh SYNOPSIS
 .Bk -words
 .Bl -tag -width "vale-ctl"
 .It Nm
-.Op Fl g Ar vale-port
-.Op Fl a Ar vale-port
-.Op Fl h Ar vale-port
-.Op Fl d Ar vale-port
+.Op Fl g Ar valeSSS:PPP
+.Op Fl a Ar valeSSS:interface
+.Op Fl h Ar valeSSS:interface
+.Op Fl d Ar valeSSS:interface
 .Op Fl n Ar interface
 .Op Fl r Ar interface
-.Op Fl l Ar vale-port
+.Op Fl l Ar valeSSS:PPP
 .Op Fl l
-.Op Fl p Ar vale-switch
-.Op Fl P Ar vale-switch
+.Op Fl p Ar valeSSS:PPP
+.Op Fl P Ar valeSSS:PPP
 .Op Fl C Ar spec
 .Op Fl m Ar memid
+.El
+.Ek
 .Sh DESCRIPTION
 .Nm
-manages VALE switches by attaching and detaching interfaces, creating
-and deleting persistent VALE ports, starting and stopping polling mode.
+manages and inspects
+.Xr vale 4
+switches, for instance attaching and detaching interfaces, creating
+and deleting persistent VALE ports, or listing the existing switches
+and their ports.
+In the following,
+.Ar valeSSS
+is the name of a VALE switch, while
+.Ar valeSSS:PPP
+is the name of a VALE port of
+.Ar valeSSS .
 .Pp
 When issued without options it lists all the existing switch ports together
 with their internal bridge number and port number.
 .Bl -tag -width Ds
-.It Fl g Ar vale-port
+.It Fl g Ar valeSSS:PPP
 Print the number of receive rings of
-.Ar vale-port.
-.It Fl a Ar switch:interface
-Attach the existing
+.Ar valeSSS:PPP .
+.It Fl a Ar valeSSS:interface
+Attach
 .Ar interface
-to
-.Ar switch
+(which must be an existing network interface) to
+.Ar valeSSS
 and detach it from the host stack.
-.It Fl h Ar switch:interface
-Attach the existing
+.It Fl h Ar valeSSS:interface
+Attach
 .Ar interface
-to
-.Ar switch
-while keeping it attached to the host stack. More precisely, packets coming from
+(which must be an existing network interface) to
+.Ar valeSSS
+while keeping it attached to the host stack.
+More precisely, packets coming from
 the host stack and directed to the interface will go through the switch, where
-they can still reach the interface if the switch rules allow it. Conversely,
-packets coming from the interface will go through the switch and, if appropriate,
-will reach the host stack.
-.It Fl d Ar switch:interface
+they can still reach the interface if the switch rules allow it.
+Conversely, packets coming from the interface will go through the switch and,
+if appropriate, will reach the host stack.
+.It Fl d Ar valeSSS:interface
 Detach
 .Ar interface
 from
-.Ar switch.
+.Ar valeSSS .
 .It Fl n Ar interface
 Create a new persistent VALE port with name
-.Ar interface.
-.It Fl d Ar interface
+.Ar interface .
+The name must be different from any other network interface
+already present in the system.
+.It Fl r Ar interface
 Destroy the persistent VALE port with name
-.Ar inteface.
-.It Fl l Ar switch:port
+.Ar interface .
+.It Fl l Ar valeSSS:PPP
 Show the internal bridge number and port number of the given switch port.
-.It Fl p Ar interface
-Start polling mode for
-.Ar interface.
-.It Fl P Ar interface
-Stop polling mode for
-.Ar interface.
+.It Fl p Ar valeSSS:PPP
+Enable polling mode for
+.Ar valeSSS:PPP .
+In polling mode, a dedicated kernel thread is spawned to handle packets
+received from
+.Ar valeSSS:PPP
+and push them into the switch.
+The kernel thread busy waits on the switch port rather than relying on
+interrupts or notifications.
+Polling mode can only be used on physical NICs attached to a VALE switch.
+.It Fl P Ar valeSSS:PPP
+Disable polling mode for
+.Ar valeSSS:PPP .
 .It Fl C Ar x | Ar x,y | Ar x,y,z | Ar x,y,z,w
 When used in conjunction with
 .Fl n
-it supplies the number of tx and rx rings and slots. The full format with four numbers
-gives, in order, numner of tx slots, number of rx slots, number of tx rings and number
-of rx rings. The form with three numbers uses
+it supplies the number of tx and rx rings and slots.
+The full format with four numbers gives, in order, number of tx slots, number
+of rx slots, number of tx rings and number of rx rings.
+The form with three numbers uses
 .Ar z
-for both the number of tx and the number of rx rings. The forms with less than two
-numbers use the default values for the number of rings. 
-The form with two numbers supplies the numbers of tx and rx slots. The form with only one number
-uses
+for both the number of tx and the number of rx rings.
+The forms with less than two numbers use the default values for the number
+of rings.
+The form with two numbers supplies the numbers of tx and rx slots.
+The form with only one number uses
 .Ar x
 for both the number of tx and the number of rx slots.
 .Pp
 When used in conjunction with
 .Fl p
-only the first three forms are used. The first number may be either 0 or 1.
+only the first three forms are used.
+The first number may be either 0 or 1.
 If 0, then all interface rings will be polled by a single thread, running
 on the core id given by the second number (the third number, if present,
-must be 1). If the first number is 1,
-then the ring identified by the second number will be polled by
-the core with the same id. If a third number is given, then this
-is repeated for as many consecutive rings and cores.
+must be 1).
+If the first number is 1, then the ring identified by the second number will
+be polled by the core with the same id.
+If a third number is given, then this is repeated for as many consecutive
+rings and cores.
 .It Fl m Ar memid
 Used in conjunction with
 .Fl n
 supplies the netmap memory region identifier to use together with the newly
-created persistent VALE port. These ports use a private memory region by
-default. Using this option you can let them share memory with other ports.
+created persistent VALE port.
+These ports use a private memory region by default.
+Using this option you can let them share memory with other ports.
 Pass 1 as
 .Ar memid
 to use the global memory region already shared by all
-harware netmap ports.
-.Pp
+hardware netmap ports.
+.El
+.Sh SEE ALSO
+.Xr netmap 4 ,
+.Xr vale 4
 .Sh AUTHORS
 .An -nosplit
 .Nm
diff --git a/apps/vale-ctl/vale-ctl.c b/apps/vale-ctl/vale-ctl.c
index 500429a21..1b8d74e61 100644
--- a/apps/vale-ctl/vale-ctl.c
+++ b/apps/vale-ctl/vale-ctl.c
@@ -23,11 +23,8 @@
  * SUCH DAMAGE.
  */
 
-/* $FreeBSD$ */
-
-#define NETMAP_WITH_LIBS
-#include 
-#include 
+#define LIBNETMAP_NOTHREADSAFE
+#include 
 
 #include 
 #include 
@@ -42,16 +39,58 @@
 #include 	/* basename */
 #include 	/* atoi, free */
 
-/* XXX cut and paste from pkt-gen.c because I'm not sure whether this
- * program may include nm_util.h
- */
-void parse_nmr_config(const char* conf, struct nmreq *nmr)
+int verbose;
+
+struct args {
+	const char *name;
+	const char *config;
+	const char *mem_id;
+
+	uint16_t nr_reqtype;
+	uint32_t nr_mode;
+};
+
+static void
+dump_port_info(struct nmreq_port_info_get *v)
+{
+	printf("memsize:    %"PRIu64"\n", v->nr_memsize);
+	printf("tx_slots:   %"PRIu32"\n", v->nr_tx_slots);
+	printf("rx_slots:   %"PRIu32"\n", v->nr_rx_slots);
+	printf("tx_rings:   %"PRIu16"\n", v->nr_tx_rings);
+	printf("rx_rings    %"PRIu16"\n", v->nr_rx_rings);
+	printf("mem_id:     %"PRIu16"\n", v->nr_mem_id);
+}
+
+static void
+dump_newif(struct nmreq_vale_newif *v)
+{
+	printf("tx_slots:   %"PRIu32"\n", v->nr_tx_slots);
+	printf("rx_slots:   %"PRIu32"\n", v->nr_rx_slots);
+	printf("tx_rings:   %"PRIu16"\n", v->nr_tx_rings);
+	printf("rx_ring:    %"PRIu16"\n", v->nr_rx_rings);
+	printf("mem_id:     %"PRIu16"\n", v->nr_mem_id);
+}
+
+static void
+dump_vale_list(struct nmreq_vale_list *v)
+{
+	printf("bridge_idx: %"PRIu16"\n", v->nr_bridge_idx);
+	printf("port_idx:   %"PRIu16"\n", v->nr_port_idx);
+}
+
+
+static void
+parse_ring_config(const char* conf,
+		uint32_t *nr_tx_slots,
+		uint32_t *nr_rx_slots,
+		uint16_t *nr_tx_rings,
+		uint16_t *nr_rx_rings)
 {
 	char *w, *tok;
 	int i, v;
 
-	nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
-	nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
+	*nr_tx_rings = *nr_rx_rings = 0;
+	*nr_tx_slots = *nr_rx_slots = 0;
 	if (conf == NULL || ! *conf)
 		return;
 	w = strdup(conf);
@@ -59,224 +98,346 @@ void parse_nmr_config(const char* conf, struct nmreq *nmr)
 		v = atoi(tok);
 		switch (i) {
 		case 0:
-			nmr->nr_tx_slots = nmr->nr_rx_slots = v;
+			*nr_tx_slots = *nr_rx_slots = v;
 			break;
 		case 1:
-			nmr->nr_rx_slots = v;
+			*nr_rx_slots = v;
 			break;
 		case 2:
-			nmr->nr_tx_rings = nmr->nr_rx_rings = v;
+			*nr_tx_rings = *nr_rx_rings = v;
 			break;
 		case 3:
-			nmr->nr_rx_rings = v;
+			*nr_rx_rings = v;
 			break;
 		default:
-			D("ignored config: %s", tok);
+			fprintf(stderr, "ignored config: %s", tok);
 			break;
 		}
 	}
-	D("txr %d txd %d rxr %d rxd %d",
-			nmr->nr_tx_rings, nmr->nr_tx_slots,
-			nmr->nr_rx_rings, nmr->nr_rx_slots);
+	ND("txr %d txd %d rxr %d rxd %d",
+			*nr_tx_rings, *nr_tx_slots,
+			*nr_rx_rings, *nr_rx_slots);
 	free(w);
 }
 
 static int
-bdg_ctl(const char *name, int nr_cmd, int nr_arg, char *nmr_config, int nr_arg2)
+parse_poll_config(const char *conf, struct nmreq_vale_polling *v)
 {
-	struct nmreq nmr;
-	int error = 0;
-	int fd = open("/dev/netmap", O_RDWR);
+	char *w, *tok;
+	int i, p;
 
-	if (fd == -1) {
-		D("Unable to open /dev/netmap");
+	if (conf == NULL || ! *conf) {
+		fprintf(stderr, "invalid null/empty config\n");
+		return -1;
+	}
+	w = strdup(conf);
+	for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
+		p = atoi(tok);
+		switch (i) {
+		case 0:
+			v->nr_mode = p ? NETMAP_POLLING_MODE_MULTI_CPU :
+				NETMAP_POLLING_MODE_SINGLE_CPU;
+			break;
+		case 1:
+			v->nr_first_cpu_id = p;
+			break;
+		case 2:
+			if (v->nr_mode != NETMAP_POLLING_MODE_MULTI_CPU) {
+				fprintf(stderr, "too many numbers in '%s'\n", conf);
+				return -1;
+			}
+			v->nr_num_polling_cpus = p;
+			break;
+		case 3:
+			fprintf(stderr, "too many numbers in '%s'\n", conf);
+			return -1;
+		}
+	}
+	free(w);
+	return 0;
+}
+
+static int32_t
+parse_mem_id(const char *mem_id)
+{
+	int32_t id;
+
+	if (mem_id == NULL)
+		return 0;
+	if (isdigit(*mem_id))
+		return atoi(mem_id);
+	id = nmreq_get_mem_id(&mem_id, nmctx_get());
+	if (id == 0) {
+		fprintf(stderr, "invalid format in '-m %s' (missing 'netmap:'?)\n", mem_id);
 		return -1;
 	}
+	return id;
+}
 
-	bzero(&nmr, sizeof(nmr));
-	nmr.nr_version = NETMAP_API;
-	if (name != NULL) /* might be NULL */
-		strncpy(nmr.nr_name, name, sizeof(nmr.nr_name));
-	nmr.nr_cmd = nr_cmd;
-	parse_nmr_config(nmr_config, &nmr);
-	nmr.nr_arg2 = nr_arg2;
-
-	switch (nr_cmd) {
-	case NETMAP_BDG_DELIF:
-	case NETMAP_BDG_NEWIF:
-		error = ioctl(fd, NIOCREGIF, &nmr);
-		if (error == -1) {
-			ND("Unable to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
-			perror(name);
-		} else {
-			ND("Success to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
+static int
+list_all(int fd, struct nmreq_header *hdr)
+{
+	int error;
+	struct nmreq_vale_list *vale_list =
+		(struct nmreq_vale_list *)(uintptr_t)hdr->nr_body;
+
+	for (;;) {
+		hdr->nr_name[0] = '\0';
+		error = ioctl(fd, NIOCCTRL, hdr);
+		if (error < 0) {
+			if (errno == ENOENT)
+				break;
+
+			fprintf(stderr, "failed to list all: %s\n", strerror(errno));
+			return 1;
 		}
+		printf("%s bridge_idx %"PRIu16" port_idx %"PRIu32"\n", hdr->nr_name,
+				vale_list->nr_bridge_idx, vale_list->nr_port_idx);
+		vale_list->nr_port_idx++;
+	}
+	return 1;
+}
+
+static int
+bdg_ctl(struct args *a)
+{
+	struct nmreq_header hdr;
+	struct nmreq_vale_attach   vale_attach;
+	struct nmreq_vale_detach   vale_detach;
+	struct nmreq_vale_newif    vale_newif;
+	struct nmreq_vale_list     vale_list;
+	struct nmreq_vale_polling  vale_polling;
+	struct nmreq_port_info_get port_info_get;
+	int error = 0;
+	int fd;
+	int32_t mem_id;
+	const char *action = NULL;
+
+	fd = open("/dev/netmap", O_RDWR);
+	if (fd == -1) {
+		perror("/dev/netmap");
+		return 1;
+	}
+
+	bzero(&hdr, sizeof(hdr));
+	hdr.nr_version = NETMAP_API;
+	if (a->name != NULL) { /* might be NULL */
+		strncpy(hdr.nr_name, a->name, NETMAP_REQ_IFNAMSIZ - 1);
+		hdr.nr_name[NETMAP_REQ_IFNAMSIZ - 1] = '\0';
+	}
+	hdr.nr_reqtype = a->nr_reqtype;
+
+	switch (a->nr_reqtype) {
+	case NETMAP_REQ_VALE_DELIF:
+		/* no body */
+		action = "remove";
 		break;
-	case NETMAP_BDG_ATTACH:
-	case NETMAP_BDG_DETACH:
-		nmr.nr_flags = NR_REG_ALL_NIC;
-		if (nr_arg && nr_arg != NETMAP_BDG_HOST) {
-			nmr.nr_flags = NR_REG_NIC_SW;
-			nr_arg = 0;
+
+	case NETMAP_REQ_VALE_NEWIF:
+		memset(&vale_newif, 0, sizeof(vale_newif));
+		hdr.nr_body = (uintptr_t)&vale_newif;
+		parse_ring_config(a->config,
+				&vale_newif.nr_tx_slots,
+				&vale_newif.nr_rx_slots,
+				&vale_newif.nr_tx_rings,
+				&vale_newif.nr_rx_rings);
+		mem_id = parse_mem_id(a->mem_id);
+		if (mem_id < 0)
+			return 1;
+		vale_newif.nr_mem_id = mem_id;
+		action = "create";
+		break;
+
+	case NETMAP_REQ_VALE_ATTACH:
+		memset(&vale_attach, 0, sizeof(vale_attach));
+		hdr.nr_body = (uintptr_t)&vale_attach;
+		vale_attach.reg.nr_mode = a->nr_mode;
+		parse_ring_config(a->config,
+				&vale_attach.reg.nr_tx_slots,
+				&vale_attach.reg.nr_rx_slots,
+				&vale_attach.reg.nr_tx_rings,
+				&vale_attach.reg.nr_rx_rings);
+		mem_id = parse_mem_id(a->mem_id);
+		if (mem_id < 0)
+			return 1;
+		vale_attach.reg.nr_mem_id = mem_id;
+		action = "attach";
+		break;
+
+	case NETMAP_REQ_VALE_DETACH:
+		memset(&vale_detach, 0, sizeof(vale_detach));
+		hdr.nr_body = (uintptr_t)&vale_detach;
+		action = "detach";
+		break;
+
+	case NETMAP_REQ_VALE_LIST:
+		memset(&vale_list, 0, sizeof(vale_list));
+		hdr.nr_body = (uintptr_t)&vale_list;
+		if (a->name == NULL) {
+			return list_all(fd, &hdr);
 		}
-		nmr.nr_arg1 = nr_arg;
-		error = ioctl(fd, NIOCREGIF, &nmr);
-		if (error == -1) {
-			ND("Unable to %s %s to the bridge", nr_cmd ==
-			    NETMAP_BDG_DETACH?"detach":"attach", name);
-			perror(name);
-		} else
-			ND("Success to %s %s to the bridge", nr_cmd ==
-			    NETMAP_BDG_DETACH?"detach":"attach", name);
+		action = "list";
 		break;
 
-	case NETMAP_BDG_LIST:
-		if (strlen(nmr.nr_name)) { /* name to bridge/port info */
-			error = ioctl(fd, NIOCGINFO, &nmr);
-			if (error) {
-				ND("Unable to obtain info for %s", name);
-				perror(name);
-			} else
-				D("%s at bridge:%d port:%d", name, nmr.nr_arg1,
-				    nmr.nr_arg2);
-			break;
+	case NETMAP_REQ_VALE_POLLING_ENABLE:
+		action = "enable polling on";
+		/* fall through */
+	case NETMAP_REQ_VALE_POLLING_DISABLE:
+		memset(&vale_polling, 0, sizeof(vale_polling));
+		hdr.nr_body = (uintptr_t)&vale_polling;
+		parse_poll_config(a->config, &vale_polling);
+		if (action == NULL)
+			action ="disable polling on";
+		break;
+
+	case NETMAP_REQ_PORT_INFO_GET:
+		memset(&port_info_get, 0, sizeof(port_info_get));
+		hdr.nr_body = (uintptr_t)&port_info_get;
+		action = "obtain info for";
+		break;
+	}
+	error = ioctl(fd, NIOCCTRL, &hdr);
+	if (error < 0) {
+		fprintf(stderr, "failed to %s %s: %s\n",
+				action, a->name, strerror(errno));
+		return 1;
+	}
+	switch (hdr.nr_reqtype) {
+	case NETMAP_REQ_VALE_NEWIF:
+		if (verbose) {
+			dump_newif(&vale_newif);
 		}
+		break;
 
-		/* scan all the bridges and ports */
-		nmr.nr_arg1 = nmr.nr_arg2 = 0;
-		for (; !ioctl(fd, NIOCGINFO, &nmr); nmr.nr_arg2++) {
-			D("bridge:%d port:%d %s", nmr.nr_arg1, nmr.nr_arg2,
-			    nmr.nr_name);
-			nmr.nr_name[0] = '\0';
+	case NETMAP_REQ_VALE_ATTACH:
+		if (verbose) {
+			printf("port_index: %"PRIu32"\n", vale_attach.port_index);
 		}
+		break;
 
+	case NETMAP_REQ_VALE_DETACH:
+		if (verbose) {
+			printf("port_index: %"PRIu32"\n", vale_detach.port_index);
+		}
 		break;
 
-	case NETMAP_BDG_POLLING_ON:
-	case NETMAP_BDG_POLLING_OFF:
-		/* We reuse nmreq fields as follows:
-		 *   nr_tx_slots: 0 and non-zero indicate REG_ALL_NIC
-		 *                REG_ONE_NIC, respectively.
-		 *   nr_rx_slots: CPU core index. This also indicates the
-		 *                first queue in the case of REG_ONE_NIC
-		 *   nr_tx_rings: (REG_ONE_NIC only) indicates the
-		 *                number of CPU cores or the last queue
-		 */
-		nmr.nr_flags |= nmr.nr_tx_slots ?
-			NR_REG_ONE_NIC : NR_REG_ALL_NIC;
-		nmr.nr_ringid = nmr.nr_rx_slots;
-		/* number of cores/rings */
-		if (nmr.nr_flags == NR_REG_ALL_NIC)
-			nmr.nr_arg1 = 1;
-		else
-			nmr.nr_arg1 = nmr.nr_tx_rings;
-
-		error = ioctl(fd, NIOCREGIF, &nmr);
-		if (!error)
-			D("polling on %s %s", nmr.nr_name,
-				nr_cmd == NETMAP_BDG_POLLING_ON ?
-				"started" : "stopped");
-		else
-			D("polling on %s %s (err %d)", nmr.nr_name,
-				nr_cmd == NETMAP_BDG_POLLING_ON ?
-				"couldn't start" : "couldn't stop", error);
+	case NETMAP_REQ_VALE_LIST:
+		dump_vale_list(&vale_list);
 		break;
 
-	default: /* GINFO */
-		nmr.nr_cmd = nmr.nr_arg1 = nmr.nr_arg2 = 0;
-		error = ioctl(fd, NIOCGINFO, &nmr);
-		if (error) {
-			ND("Unable to get if info for %s", name);
-			perror(name);
-		} else
-			D("%s: %d queues.", name, nmr.nr_rx_rings);
+	case NETMAP_REQ_PORT_INFO_GET:
+		dump_port_info(&port_info_get);
 		break;
 	}
 	close(fd);
 	return error;
 }
 
+static void
+usage(int errcode)
+{
+	fprintf(stderr,
+	    "Usage:\n"
+	    "vale-ctl [arguments]\n"
+	    "\t-g interface	interface name to get info\n"
+	    "\t-d interface	interface name to be detached\n"
+	    "\t-a interface	interface name to be attached\n"
+	    "\t-h interface	interface name to be attached with the host stack\n"
+	    "\t-n interface	interface name to be created\n"
+	    "\t-r interface	interface name to be deleted\n"
+	    "\t-l vale-port	show bridge and port indices\n"
+	    "\t-C string ring/slot setting of an interface creating by -n\n"
+	    "\t-p interface start polling. Additional -C x,y,z configures\n"
+	    "\t\t x: 0 (REG_ALL_NIC) or 1 (REG_ONE_NIC),\n"
+	    "\t\t y: CPU core id for ALL_NIC and core/ring for ONE_NIC\n"
+	    "\t\t z: (ONE_NIC only) num of total cores/rings\n"
+	    "\t-P interface stop polling\n"
+	    "\t-m memid to use when creating a new interface\n"
+	    "\t-v increase verbosity\n"
+	    "with no arguments: list all existing vale ports\n");
+	exit(errcode);
+}
+
 int
 main(int argc, char *argv[])
 {
-	int ch, nr_cmd = 0, nr_arg = 0;
-	const char *command = basename(argv[0]);
-	char *name = NULL, *nmr_config = NULL;
-	int nr_arg2 = 0;
-
-	if (argc > 5) {
-usage:
-		fprintf(stderr,
-			"Usage:\n"
-			"%s arguments\n"
-			"\t-g interface	interface name to get info\n"
-			"\t-d interface	interface name to be detached\n"
-			"\t-a interface	interface name to be attached\n"
-			"\t-h interface	interface name to be attached with the host stack\n"
-			"\t-n interface	interface name to be created\n"
-			"\t-r interface	interface name to be deleted\n"
-			"\t-l list all or specified bridge's interfaces (default)\n"
-			"\t-C string ring/slot setting of an interface creating by -n\n"
-			"\t-p interface start polling. Additional -C x,y,z configures\n"
-			"\t\t x: 0 (REG_ALL_NIC) or 1 (REG_ONE_NIC),\n"
-			"\t\t y: CPU core id for ALL_NIC and core/ring for ONE_NIC\n"
-			"\t\t z: (ONE_NIC only) num of total cores/rings\n"
-			"\t-P interface stop polling\n"
-			"\t-m memid to use when creating a new interface\n"
-			"", command);
-		return 0;
-	}
+	int ch;
+	struct args a = {
+		.name = NULL,
+		.config = NULL,
+		.mem_id = NULL,
+		.nr_reqtype = 0,
+		.nr_mode = NR_REG_ALL_NIC,
+	};
 
-	while ((ch = getopt(argc, argv, "d:a:h:g:l:n:r:C:p:P:m:")) != -1) {
-		if (ch != 'C' && ch != 'm')
-			name = optarg; /* default */
+	while ((ch = getopt(argc, argv, "d:a:h:g:l:n:r:C:p:P:m:v")) != -1) {
 		switch (ch) {
 		default:
 			fprintf(stderr, "bad option %c %s", ch, optarg);
-			goto usage;
+			usage(1);
+			break;
 		case 'd':
-			nr_cmd = NETMAP_BDG_DETACH;
+			a.nr_reqtype = NETMAP_REQ_VALE_DETACH;
+			a.name = optarg;
 			break;
 		case 'a':
-			nr_cmd = NETMAP_BDG_ATTACH;
+			a.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
+			a.nr_mode = NR_REG_ALL_NIC;
+			a.name = optarg;
 			break;
 		case 'h':
-			nr_cmd = NETMAP_BDG_ATTACH;
-			nr_arg = NETMAP_BDG_HOST;
+			a.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
+			a.nr_mode = NR_REG_NIC_SW;
+			a.name = optarg;
 			break;
 		case 'n':
-			nr_cmd = NETMAP_BDG_NEWIF;
+			a.nr_reqtype = NETMAP_REQ_VALE_NEWIF;
+			a.name = optarg;
 			break;
 		case 'r':
-			nr_cmd = NETMAP_BDG_DELIF;
+			a.nr_reqtype = NETMAP_REQ_VALE_DELIF;
+			a.name = optarg;
 			break;
 		case 'g':
-			nr_cmd = 0;
+			a.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
+			a.name = optarg;
 			break;
 		case 'l':
-			nr_cmd = NETMAP_BDG_LIST;
+			a.nr_reqtype = NETMAP_REQ_VALE_LIST;
+			a.name = optarg;
+			if (strncmp(a.name, NM_BDG_NAME, strlen(NM_BDG_NAME))) {
+				fprintf(stderr, "invalid vale port name: '%s'\n", a.name);
+				usage(1);
+			}
 			break;
 		case 'C':
-			nmr_config = strdup(optarg);
+			a.config = optarg;
 			break;
 		case 'p':
-			nr_cmd = NETMAP_BDG_POLLING_ON;
+			a.nr_reqtype = NETMAP_REQ_VALE_POLLING_ENABLE;
+			a.name = optarg;
 			break;
 		case 'P':
-			nr_cmd = NETMAP_BDG_POLLING_OFF;
+			a.nr_reqtype = NETMAP_REQ_VALE_POLLING_DISABLE;
+			a.name = optarg;
 			break;
 		case 'm':
-			nr_arg2 = atoi(optarg);
+			a.mem_id = optarg;
+			break;
+		case 'v':
+			verbose++;
 			break;
 		}
 	}
 	if (optind != argc) {
-		// fprintf(stderr, "optind %d argc %d\n", optind, argc);
-		goto usage;
+		usage(1);
 	}
 	if (argc == 1) {
-		nr_cmd = NETMAP_BDG_LIST;
-		name = NULL;
+		a.nr_reqtype = NETMAP_REQ_VALE_LIST;
+		a.name = NULL;
+	}
+	if (!a.nr_reqtype) {
+		usage(1);
 	}
-	return bdg_ctl(name, nr_cmd, nr_arg, nmr_config, nr_arg2) ? 1 : 0;
+	return bdg_ctl(&a);
 }
diff --git a/ci/build-linux b/ci/build-linux
new file mode 100755
index 000000000..2a0d9db07
--- /dev/null
+++ b/ci/build-linux
@@ -0,0 +1,109 @@
+#!/bin/bash -eu
+
+function kverval() {
+	local kver="$1"
+	local val="0"
+	local components
+
+	# Parse kernel version
+	IFS='.' read -ra components <<< "${kver}"
+	for x in ${components[@]}; do
+		val=$((val * 100))
+		val=$((val + x))
+	done
+	echo $val
+}
+
+function kverless() {
+	local kver1="$1"
+	local kver2="$2"
+
+	kval1=$(kverval $kver1)
+	kval2=$(kverval $kver2)
+
+	if [ "$kval1" -lt "$kval2" ]; then
+		echo "0"
+	else
+		echo "1"
+	fi
+}
+
+function set_config_variable()
+{
+	local varname=$1
+	local varvalue=$2
+
+	cnt=$(grep ${varname} .config || true)
+	if [ "$cnt" == 0 ]; then
+		echo "${varname}=${varvalue}" >> .config
+	else
+		sed -i "s/^${varname}=./${varname}=${varvalue}/" .config
+	fi
+	grep "${varname}=${varvalue}" .config || echo "Failed to modify .config"
+}
+
+set -o pipefail
+
+readonly KERNEL_VERSION=${1:?}
+readonly ARCH=${2:?}
+readonly GCC_MAJOR_VERSION=$(echo '#include 
+void main() { printf("%u\n", __GNUC__); }' | gcc -x c - -o /tmp/getgccversion  && /tmp/getgccversion)
+readonly PROC_COUNT=$(grep -c '^processor' /proc/cpuinfo)
+
+sudo apt-get -qq update
+sudo apt-get install libelf-dev
+if [ ${KERNEL_VERSION} == "local" ]; then
+    sudo apt-get install -y linux-headers-$(uname -r)
+    ./configure --no-drivers
+    make -j $PROC_COUNT
+    sudo make install
+    sudo depmod -a
+    exit 0
+fi
+
+# Fetch the kernel code
+wget https://www.kernel.org/pub/linux/kernel/v${KERNEL_VERSION:0:1}.x/linux-${KERNEL_VERSION}.tar.gz
+tar xzf linux-${KERNEL_VERSION}.tar.gz
+
+# Configure kernel
+pushd linux-${KERNEL_VERSION}
+compiler_file=compiler-gcc${GCC_MAJOR_VERSION}.h
+if [ ! -f include/linux/${compiler_file} -a ! -h include/linux/${compiler_file} ]
+then
+  # Fix compilation of old kernels with recent GCC
+  pushd include/linux
+  if [ -f compiler-gcc5.h -a $GCC_MAJOR_VERSION -gt 5 ]
+  then
+    ln -sv compiler-gcc5.h ${compiler_file}
+  else
+    ln -sv compiler-gcc4.h ${compiler_file}
+  fi
+  popd
+fi
+make mrproper
+make -j $PROC_COUNT ARCH=${ARCH} allmodconfig
+make -j $PROC_COUNT ARCH=${ARCH} modules_prepare
+popd
+
+# First build in-tree-only drivers
+echo "Building vanilla-only drivers"
+./configure --no-ext-drivers --driver-suffix=_netmap --kernel-dir=$PWD/linux-${KERNEL_VERSION} --drivers=r8169.c,virtio_net.c,forcedeth.c,veth.c,e1000,vmxnet3 --enable-ptnetmap
+make -j $PROC_COUNT
+
+# Then build external intel drivers
+make distclean
+echo "Building external intel drivers (and virtio_net.c)"
+EXTDRIVERS="e1000e,igb,ixgbe,i40e"
+# For kernels >= 3.13 we support virtio_net.c as an external driver
+cmp=$(kverless $KERNEL_VERSION 3.10)
+if [ "$cmp" == "1" ]; then
+	EXTDRIVERS="${EXTDRIVERS},virtio_net.c"
+fi
+./configure --kernel-dir=$PWD/linux-${KERNEL_VERSION} --driver-suffix=_netmap --drivers=${EXTDRIVERS}
+make -j $PROC_COUNT
+
+# Then build vanilla intel drivers
+make distclean
+echo "Building vanilla intel drivers"
+./configure --no-ext-drivers --kernel-dir=$PWD/linux-${KERNEL_VERSION} --driver-suffix=_netmap --drivers=e1000e,igb,ixgbe,i40e
+make -j $PROC_COUNT
diff --git a/ci/run-integration-tests b/ci/run-integration-tests
new file mode 100755
index 000000000..e2ddf0e59
--- /dev/null
+++ b/ci/run-integration-tests
@@ -0,0 +1,8 @@
+#!/bin/bash -eu
+
+sudo modprobe netmap
+sudo make unitest
+sudo rmmod netmap
+
+# Run integration tests
+sudo make intest
diff --git a/extra/python/netmap.c b/extra/python/netmap.c
index 08da3f521..f34d6c6d5 100644
--- a/extra/python/netmap.c
+++ b/extra/python/netmap.c
@@ -270,10 +270,6 @@ static struct NetmapConst netmap_constants[] = {
         .name = "RegExclusive",
         .value = NR_EXCLUSIVE,
     },
-    {
-        .name = "RegPTNetmapHost",
-        .value = NR_PTNETMAP_HOST,
-    },
     /* Add 'netmap_rings.flags' constants to the module. */
     {
         .name = "NrTimestamp",
diff --git a/extra/python/netmap_interface.c b/extra/python/netmap_interface.c
index 22650000e..2844bb0be 100644
--- a/extra/python/netmap_interface.c
+++ b/extra/python/netmap_interface.c
@@ -58,29 +58,23 @@ NetmapInterface_repr(NetmapInterface *self)
     }
 
     result = PyString_FromFormat(
-            "name: '%s'\n"
-            "version:    %u\n"
-            "flags:      0x%08x\n"
-            "tx_rings:   %u\n"
-            "rx_rings:   %u\n"
-            "bufs_head:  %u\n"
-            "spare1[0]:  0x%08x\n"
-            "spare1[1]:  0x%08x\n"
-            "spare1[2]:  0x%08x\n"
-            "spare1[3]:  0x%08x\n"
-            "spare1[4]:  0x%08x\n",
+            "name:           '%s'\n"
+            "version:        %u\n"
+            "flags:          0x%08x\n"
+            "tx_rings:       %u\n"
+            "rx_rings:       %u\n"
+            "bufs_head:      %u\n"
+            "host_tx_rings:  %u\n"
+            "host_rx_rings:  %u\n",
             nifp->ni_name,
             nifp->ni_version,
             nifp->ni_flags,
             nifp->ni_tx_rings,
             nifp->ni_rx_rings,
             nifp->ni_bufs_head,
-            nifp->ni_spare1[0],
-            nifp->ni_spare1[1],
-            nifp->ni_spare1[2],
-            nifp->ni_spare1[3],
-            nifp->ni_spare1[4]
-                );
+            nifp->ni_host_tx_rings,
+            nifp->ni_host_rx_rings
+            );
 
     return result;
 }
diff --git a/extra/python/netmap_ring.c b/extra/python/netmap_ring.c
index 5002679e9..124b6c149 100644
--- a/extra/python/netmap_ring.c
+++ b/extra/python/netmap_ring.c
@@ -73,7 +73,7 @@ NetmapRing_repr(NetmapRing *self)
                         sizeof(nr_flag_values)/sizeof(*nr_flag_values));
 
     result = PyString_FromFormat(
-            "buf_ofs:       0x%016x\n"
+            "buf_ofs:       0x%016lx\n"
             "num_slots:     %u\n"
             "nr_buf_size:   %u\n"
             "ringid:        %u\n"
@@ -82,10 +82,10 @@ NetmapRing_repr(NetmapRing *self)
             "cur:           %u\n"
             "tail:          %u\n"
             "flags:         [0x%08x] %s\n"
-            "tv_sec:        %u\n"
-            "tv_usec:       %u\n"
+            "tv_sec:        %ld\n"
+            "tv_usec:       %ld\n"
             /* TODO sem */,
-            ring->buf_ofs,
+            (long unsigned)ring->buf_ofs,
             ring->num_slots,
             ring->nr_buf_size,
             ring->ringid,
diff --git a/extra/python/pktman.py b/extra/python/pktman.py
index dce7189c3..a7eeb57c8 100755
--- a/extra/python/pktman.py
+++ b/extra/python/pktman.py
@@ -1,4 +1,10 @@
 #!/usr/bin/env python
+#
+# Packet generator written in Python, providing functionalities
+# similar to the netmap pkt-gen written in C
+#
+#   Author: Vincenzo Maffione
+#
 
 import netmap       # our module
 import time         # time measurements
@@ -48,10 +54,10 @@ def build_packet(args, parser):
     return ret
 
 
-def transmit(idx, suffix, args, parser, queue):
+def transmit(idx, ifname, args, parser, queue):
     # use nm_open() to open the netmap device and register an interface
     # using an extended interface name
-    nmd = netmap.NetmapDesc(args.interface + suffix)
+    nmd = netmap.NetmapDesc(ifname)
     time.sleep(args.wait_link)
 
     # build the packet that will be transmitted
@@ -94,10 +100,10 @@ def transmit(idx, suffix, args, parser, queue):
         pass
 
 
-def receive(idx, suffix, args, parser, queue):
+def receive(idx, ifname, args, parser, queue):
     # use nm_open() to open the netmap device and register an interface
     # using an extended interface name
-    nmd = netmap.NetmapDesc(args.interface + suffix)
+    nmd = netmap.NetmapDesc(ifname)
     time.sleep(args.wait_link)
 
     # select the right ring
@@ -139,39 +145,6 @@ def receive(idx, suffix, args, parser, queue):
         pass
 
 
-# How many netmap ring couples has 'ifname'?
-def netmap_max_rings(ifname):
-    if ifname.startswith('netmap:'):
-        ifname = ifname[7:]
-
-    nm = netmap.Netmap()
-    nm.open()
-    nm.if_name = ifname
-    nm.getinfo()
-
-    return nm.tx_rings
-
-# extract the (nr_ringid, nr_flags) specified by the extended
-# interface name (nm_open() ifname)
-def netmap_get_ringid(ifname):
-    if ifname.startswith('netmap:'):
-        ifname = ifname[7:]
-
-    nm = netmap.Netmap()
-    nm.open()
-    nm.if_name = ifname
-    nm.getinfo()
-
-    return nm.ringid, nm.flags
-
-def netmap_remove_ifname_suffix(ifname_ext):
-    m = re.match(r'\w+:\w+', ifname_ext)
-    if m == None:
-        return None
-
-    return m.group(0)
-
-
 ############################## MAIN ###########################
 
 if __name__ == '__main__':
@@ -194,7 +167,7 @@ def netmap_remove_ifname_suffix(ifname_ext):
                     choices = ['tx', 'rx'], default = 'rx')
     parser.add_argument('-b', '--batchsize', help = 'number of packets to send with each TXSYNC '
                     'operation', type=int, default = 512, dest = 'batch')
-    parser.add_argument('-l', '--length', help = 'lenght of the ethernet frame sent',
+    parser.add_argument('-l', '--length', help = 'length of the ethernet frame sent',
                     type = int, default = 60)
     parser.add_argument('-D', '--dstmac', help = 'destination MAC of tx packets',
                     default = 'ff:ff:ff:ff:ff:ff')
@@ -222,33 +195,24 @@ def netmap_remove_ifname_suffix(ifname_ext):
         print('Invalid number of threads\n')
         help_quit(parser)
 
-    try:
-        # compute 'ifname' removing the suffix from the extended name
-        # specified by the user
-        ifname = netmap_remove_ifname_suffix(args.interface)
-        if ifname == None:
-            print('Invalid ifname "%s"' % (args.interface, ))
-            help_quit(parser)
-
-        # compute 'max_couples', which is the number of tx/rx rings couples to be registered
-        # according to 'args.interface'
-        nr_ringid, nr_flags = netmap_get_ringid(args.interface)
-        if nr_flags in [netmap.RegAllNic, netmap.RegNicSw]:
-            # ask netmap for the number of available couples
-            max_couples = netmap_max_rings(args.interface)
-            suffix_required = True
-            ringid_offset = 0
-        else:
-            # all the others netmap.Reg* specifies just one couple of rings
-            max_couples = 1
-            suffix_required = False
-            ringid_offset = nr_ringid
-        if args.threads > max_couples:
-            print('You cannot use more than %s (tx,rx) rings couples with "%s"' % (max_couples, args.interface))
-            help_quit(parser)
-    except netmap.error as e:
-        print(e)
-        quit()
+    # Temporary open a netmap descriptor to get some info about
+    # number of involved rings or the specific ring couple involved
+    d = netmap.NetmapDesc(args.interface)
+    if d.getflags() in [netmap.RegAllNic, netmap.RegNicSw]:
+        max_couples = min(len(d.receive_rings), len(d.transmit_rings))
+        if d.getflags() == netmap.RegAllNic:
+            max_couples -= 1
+        ringid_offset = 0
+        suffix_required = True
+    else:
+        max_couples = 1
+        ringid_offset = d.getringid()
+        suffix_required = False
+    del d
+
+    if args.threads > max_couples:
+        print('You cannot use more than %s (tx,rx) rings couples with "%s"' % (max_couples, args.interface))
+        quit(1)
 
     jobs = []    # array of worker processes
     queues = []  # array of queues for IPC
@@ -256,21 +220,22 @@ def netmap_remove_ifname_suffix(ifname_ext):
         queue = multiprocessing.Queue()
         queues.append(queue)
 
-        # 'i_off' contains the ring idx on which the process below will operate
-        i_off = i + ringid_offset
+        # 'ring_id' contains the ring idx on which the process below will operate
+        ring_id = i + ringid_offset
         # it may also be necessary to add an extension suffix to the interface
         # name specified by the user
+        ifname = args.interface
         if suffix_required:
-            suffix = '-' + str(i_off)
-        else:
-            suffix = ''
+            ifname += '-' + str(ring_id)
+
+        print("Run worker #%d on %s, ring_id %d" % (i, ifname, ring_id))
 
         # create a new process that will execute the user-selected handler function,
         # with the arguments specified by the 'args' tuple
         job = multiprocessing.Process(name = 'worker-' + str(i),
                                         target = handler[args.function],
-                                        args = (i_off, suffix, args, parser, queue))
-        job.deamon = True   # ensure work termination
+                                        args = (ring_id, ifname, args, parser, queue))
+        job.daemon = True   # ensure work termination
         jobs.append(job)
 
     # start all the workers
diff --git a/libnetmap/GNUmakefile b/libnetmap/GNUmakefile
new file mode 100644
index 000000000..3828abed1
--- /dev/null
+++ b/libnetmap/GNUmakefile
@@ -0,0 +1,28 @@
+SRCDIR ?= ../
+PREFIX ?= usr/local
+CFLAGS=-O2 -pipe -Wall -Werror
+CFLAGS +=-g
+CFLAGS += -I $(SRCDIR)/sys
+VPATH = $(SRCDIR)/libnetmap
+SRCS=$(notdir $(wildcard $(SRCDIR)/libnetmap/*.c))
+OBJS=$(SRCS:.c=.o)
+
+all: libnetmap.a
+
+$(OBJS): libnetmap.h
+
+libnetmap.a: $(OBJS)
+	$(AR) r $@ $^
+
+.PHONY: clean distclean install
+clean:
+	rm -f *.o
+
+distclean: clean
+	rm -f libnetmap.a
+
+install:
+	install -D libnetmap.a $(DESTDIR)/$(PREFIX)/lib/libnetmap.a
+
++%:
+	@echo $*=$($*)
diff --git a/libnetmap/libnetmap.h b/libnetmap/libnetmap.h
new file mode 100644
index 000000000..280132035
--- /dev/null
+++ b/libnetmap/libnetmap.h
@@ -0,0 +1,716 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018 Universita` di Pisa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef LIBNETMAP_H_
+#define LIBNETMAP_H_
+/* if thread-safety is not needed, define LIBNETMAP_NOTHREADSAFE before including
+ * this file.
+ */
+
+/* NOTE: we include net/netmap_user.h without defining NETMAP_WITH_LIBS, which
+ * is deprecated. If you still need it, please define NETMAP_WITH_LIBS and
+ * include net/netmap_user.h before including this file.
+ */
+#include 
+
+struct nmctx;
+struct nmport_d;
+struct nmem_d;
+
+/*
+ * A port open specification (portspec for brevity) has the following syntax
+ * (square brackets delimit optional parts):
+ *
+ *     subsystem:vpname[mode][options]
+ *
+ *  The "subsystem" is denoted by a prefix, possibly followed by an identifier.
+ *  There can be several kinds of subsystems, each one selected by a unique
+ *  prefix.  Currently defined subsystems are:
+ *
+ *  netmap 		(no id allowed)
+ *  			the standard subsystem
+ *
+ *  vale 		(followed by a possibly empty id)
+ *  			the vpname is connected to a VALE switch identified by
+ *  			the id (an empty id selects the default switch)
+ *
+ *  The "vpname" has the following syntax:
+ *
+ *     identifier			or
+ *     identifier1{identifier2		or
+ *     identifier1}identifier2
+ *
+ *  Identifiers are sequences of alphanumeric characters. The part that begins
+ *  with either '{' or '}', when present, denotes a netmap pipe opened in the
+ *  same memory region as the subsystem:indentifier1 port.
+ *
+ * The "mode" can be one of the following:
+ *
+ *	^		bind all host (sw) ring pairs
+ *	^NN		bind individual host ring pair
+ *	*		bind host and NIC ring pairs
+ *	-NN		bind individual NIC ring pair
+ *	@NN		open the port in the NN memory region
+ *	a suffix starting with / and the following flags,
+ *	in any order:
+ *	x		exclusive access
+ *	z		zero copy monitor (both tx and rx)
+ *	t		monitor tx side (copy monitor)
+ *	r		monitor rx side (copy monitor)
+ *	R		bind only RX ring(s)
+ *	T		bind only TX ring(s)
+ *
+ *  The "options" start at the first '@' character not followed by a number.
+ *  Each option starts with '@' and has the following syntax:
+ *
+ *      option					(flag option)
+ *      option=value				(single key option)
+ *      option:key1=value1,key2=value2,...	(multi-key option)
+ *
+ *  For multi-key options, the keys can be assigned in any order, but they
+ *  cannot be assigned more than once. It is not necessary to assign all the
+ *  option keys: unmentioned keys will receive default values.  Some multi-key
+ *  options define a default key and also accept the single-key syntax, by
+ *  assigning the value to this key.
+ *
+ *  NOTE: Options may be silently ignored if the port is already open by some
+ *  other process.
+ *
+ *  The currently available options are (default keys, when defined, are marked
+ *  with '*'):
+ *
+ *  share (single-key)
+ *  			open the port in the same memory region used by the
+ *  			given port name (the port name must be given in
+ *  			subsystem:vpname form)
+ *
+ *  conf  (multi-key)
+ *  			specify the rings/slots numbers (effective only on
+ *  			ports that are created by the open operation itself,
+ *  			and ignored otherwise).
+ *
+ *			The keys are:
+ *
+ *  		       *rings		number of tx and rx rings
+ *  			tx-rings	number of tx rings
+ *  			rx-rings	number of rx rings
+ *			host-rings	number of tx and rx host rings
+ *  			host-tx-rings	number of host tx rings
+ *  			host-rx-rings	number of host rx rings
+ *  			slots		number of slots in each tx and rx
+ *  					ring
+ *  			tx-slots	number of slots in each tx ring
+ *  			rx-slots	number of slots in each rx ring
+ *
+ *  			(more specific keys override the less specific ones)
+ *			All keys default to zero if not assigned, and the
+ *			corresponding value will be chosen by netmap.
+ *
+ *  extmem (multi-key)
+ *			open the port in the memory region obtained by
+ *			mmap()ing the given file.
+ *
+ *			The keys are:
+ *
+ *		       *file		the file to mmap
+ *			if-num		number of pre-allocated netmap_if's
+ *			if-size		size of each netmap_if
+ *			ring-num	number of pre-allocated netmap_ring's
+ *			ring-size	size of each netmap_ring
+ *			buf-num		number of pre-allocated buffers
+ *			buf-size	size of each buffer
+ *
+ *			file must be assigned. The other keys default to zero,
+ *			causing netmap to take the corresponding values from
+ *			the priv_{if,ring,buf}_{num,size} sysctls.
+ *
+ *  offset (multi-key)
+ *			reserve (part of) the ptr fields as an offset field
+ *			and write an initial offset into them.
+ *
+ *			The keys are:
+ *
+ *		        bits		number of bits of ptr to use
+ *		       *initial		initial offset value
+ *
+ *		        initial must be assigned. If bits is omitted, it
+ *		        defaults to the entire ptr field. The max offset is set
+ *		        at the same value as the initial offset. Note that the
+ *		        actual values may be increased by the kernel.
+ *
+ *		        This option is disabled by default (see
+ *			nmport_enable_option() below)
+ */
+
+
+/* nmport manipulation */
+
+/* struct nmport_d - describes a netmap port */
+struct nmport_d {
+	/* see net/netmap.h for the definition of these fields */
+	struct nmreq_header hdr;
+	struct nmreq_register reg;
+
+	/* all the fields below should be considered read-only */
+
+	/* if the same context is used throughout the program, d1->mem ==
+	 * d2->mem iff d1 and d2 are using the memory region (i.e., zero
+	 * copy is possible between the two ports)
+	 */
+	struct nmem_d *mem;
+
+	/* the nmctx used when this nmport_d was created */
+	struct nmctx *ctx;
+
+	int register_done;	/* nmport_register() has been called */
+	int mmap_done;		/* nmport_mmap() has been called */
+	/* pointer to the extmem option contained in the hdr options, if any */
+	struct nmreq_opt_extmem *extmem;
+
+	/* the fields below are compatible with nm_open() */
+	int fd;				/* "/dev/netmap", -1 if not open */
+	struct netmap_if *nifp;		/* pointer to the netmap_if */
+	uint16_t first_tx_ring;
+	uint16_t last_tx_ring;
+	uint16_t first_rx_ring;
+	uint16_t last_rx_ring;
+	uint16_t cur_tx_ring;		/* used by nmport_inject */
+	uint16_t cur_rx_ring;
+
+	/* LIFO list of cleanup functions (used internally) */
+	struct nmport_cleanup_d *clist;
+};
+
+/* nmport_open - opens a port from a portspec
+ * @portspec	the port opening specification
+ *
+ * If successful, the function returns a new nmport_d describing a netmap
+ * port, opened according to the port specification, ready to be used for rx
+ * and/or tx.
+ *
+ * The rings available for tx are in the [first_tx_ring, last_tx_ring]
+ * interval, and similarly for rx. One or both intervals may be empty.
+ *
+ * When done using it, the nmport_d descriptor must be closed using
+ * nmport_close().
+ *
+ * In case of error, NULL is returned, errno is set to some error, and an
+ * error message is sent through the error() method of the current context.
+ */
+struct nmport_d * nmport_open(const char *portspec);
+
+/* nport_close - close a netmap port
+ * @d		the port we want to close
+ *
+ * Undoes the actions performed by the nmport_open that created d, then
+ * frees the descriptor.
+ */
+void nmport_close(struct nmport_d *d);
+
+/* nmport_inject - sends a packet
+ * @d		the port through which we want to send
+ * @buf		base address of the packet
+ * @size	its size in bytes
+ *
+ * Sends a packet using the cur_tx_ring and updates the index
+ * to use all available tx rings in turn. Note: the packet is copied.
+ *
+ * Returns 0 on success an -1 on error.
+ */
+int nmport_inject(struct nmport_d *d, const void *buf, size_t size);
+
+/*
+ * the functions below can be used to split the functionality of
+ * nmport_open when special features (e.g., extra buffers) are needed
+ *
+ * The relation among the functions is as follows:
+ *
+ *				   |nmport_new
+ * 		|nmport_prepare	 = |
+ *		|		   |nmport_parse
+ * nmport_open =|
+ *		|		   |nmport_register
+ *		|nmport_open_desc =|
+ *				   |nmport_mmap
+ *
+ */
+
+/* nmport_new - create a new nmport_d
+ *
+ * Creates a new nmport_d using the malloc() method of the current default
+ * context. Returns NULL on error, setting errno to an error value.
+ */
+struct nmport_d *nmport_new(void);
+
+/* nmport_parse - fills the nmport_d netmap-register request
+ * @d		the nmport to be filled
+ * @portspec	the port opening specification
+ *
+ * This function parses the portspec and initizalizes the @d->hdr and @d->reg
+ * fields. It may need to allocate a list of options. If an extmem option is
+ * found, it may also mmap() the corresponding file.
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+int nmport_parse(struct nmport_d *d, const char *portspec);
+
+/* nmport_register - registers the port with netmap
+ * @d		the nmport to be registered
+ *
+ * This function obtains a netmap file descriptor and registers the port with
+ * netmap. The @d->hdr and @d->reg data structures must have been previously
+ * initialized (via nmport_parse() or otherwise).
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+int nmport_register(struct nmport_d *);
+
+/* nmport_mmap - maps the port resources into the process memory
+ * @d		the nmport to be mapped
+ *
+ * The port must have been previously been registered using nmport_register.
+ *
+ * Note that if extmem is used (either via an option or by calling an
+ * nmport_extmem_* function before nmport_register()), no new mmap() is issued.
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+int nmport_mmap(struct nmport_d *);
+
+/* the following functions undo the actions of nmport_new(), nmport_parse(),
+ * nmport_register() and nmport_mmap(), respectively.
+ */
+void nmport_delete(struct nmport_d *);
+void nmport_undo_parse(struct nmport_d *);
+void nmport_undo_register(struct nmport_d *);
+void nmport_undo_mmap(struct nmport_d *);
+
+/* nmport_prepare - create a port descriptor, but do not open it
+ * @portspec	the port opening specification
+ *
+ * This functions creates a new nmport_d and initializes it according to
+ * @portspec. It is equivalent to nmport_new() followed by nmport_parse().
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+struct nmport_d *nmport_prepare(const char *portspec);
+
+/* nmport_open_desc - open an initialized port descriptor
+ * @d		the descriptor we want to open
+ *
+ * Registers the port with netmap and maps the rings and buffers into the
+ * process memory. It is equivalent to nmport_register() followed by
+ * nmport_mmap().
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+int nmport_open_desc(struct nmport_d *d);
+
+/* the following functions undo the actions of nmport_prepare()
+ * and nmport_open_desc(), respectively.
+ */
+void nmport_undo_prepare(struct nmport_d *);
+void nmport_undo_open_desc(struct nmport_d *);
+
+/* nmport_clone - copy an nmport_d
+ * @d		the nmport_d we want to copy
+ *
+ * Copying an nmport_d by hand should be avoided, since adjustments are needed
+ * and some part of the state cannot be easily duplicated. This function
+ * creates a copy of @d in a safe way. The returned nmport_d contains
+ * nmreq_header and nmreq_register structures equivalent to those contained in
+ * @d, except for the option list, which is ignored. The returned nmport_d is
+ * already nmport_prepare()d, but it must still be nmport_open_desc()ed. The
+ * new nmport_d uses the same nmctx as @d.
+ *
+ * If extmem was used for @d, then @d cannot be nmport_clone()d until it has
+ * been nmport_register()ed.
+ *
+ * In case of error, the function returns NULL, sets errno to an error value
+ * and sends an error message to the nmctx error() method.
+ */
+struct nmport_d *nmport_clone(struct nmport_d *);
+
+/* nmport_extmem - use extmem for this port
+ * @d		the port we want to use the extmem for
+ * @base	the base address of the extmem region
+ * @size	the size in bytes of the extmem region
+ *
+ * the memory that contains the netmap ifs, rings and buffers is usually
+ * allocated by netmap and later mmap()ed by the applications. It is sometimes
+ * useful to reverse this process, by having the applications allocate some
+ * memory (through mmap() or otherwise) and then let netmap use it.  The extmem
+ * option can be used to implement this latter strategy. The option can be
+ * passed through the portspec using the '@extmem:...' syntax, or
+ * programmatically by calling nmport_extmem() or nmport_extmem_from_file()
+ * between nmport_parse() and nmport_register() (or between nmport_prepare()
+ * and nmport_open_desc()).
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+int nmport_extmem(struct nmport_d *d, void *base, size_t size);
+
+/* nmport_extmem_from_file - use the extmem obtained by mapping a file
+ * @d		the port we want to use the extmem for
+ * @fname	path of the file we want to map
+ *
+ * This works like nmport_extmem, but the extmem memory is obtained by
+ * mmap()ping @fname. nmport_close() will also automatically munmap() the file.
+ *
+ * It returns 0 on success. On failure it returns -1, sets errno to an error
+ * value and sends an error message to the error() method of the context used
+ * when @d was created. Moreover, *@d is left unchanged.
+ */
+int nmport_extmem_from_file(struct nmport_d *d, const char *fname);
+
+/* nmport_extmem_getinfo - opbtai a pointer to the extmem configuration
+ * @d		the port we want to obtain the pointer from
+ *
+ * Returns a pointer to the nmreq_pools_info structure containing the
+ * configuration of the extmem attached to port @d, or NULL if no extmem
+ * is attached. This can be used to set the desired configuration before
+ * registering the port, or to read the actual configuration after
+ * registration.
+ */
+struct nmreq_pools_info* nmport_extmem_getinfo(struct nmport_d *d);
+
+
+/* nmport_offset - use offsets for this port
+ * @initial	the initial offset for all the slots
+ * @maxoff	the maximum offset
+ * @bits	the number of bits of slot->ptr to use for the offsets
+ * @mingap	the minimum gap between offsets (in shared buffers)
+ *
+ * With this option the lower @bits bits of the ptr field in the netmap_slot
+ * can be used to specify an offset into the buffer.  All offsets will be set
+ * to the @initial value by netmap.
+ *
+ * The offset field can be read and updated using the bitmask found in
+ * ring->offset_mask after a successful register.  netmap_user.h contains
+ * some helper macros (NETMAP_ROFFSET, NETMAP_WOFFSET and NETMAP_BUF_OFFSET).
+ *
+ * For RX rings, the user writes the offset o in an empty slot before passing
+ * it to netmap; then, netmap will write the incoming packet at an offset o' >=
+ * o in the buffer. o' may be larger than o because of, e.g., alignment
+ * constrains.  If o' > o netmap will also update the offset field in the slot.
+ * Note that large offsets may cause the port to split the packet over several
+ * slots, setting the NS_MOREFRAG flag accordingly.
+ *
+ * For TX rings, the user may prepare the packet to send at an offset o into
+ * the buffer and write o in the offset field. Netmap will send the packets
+ * starting o bytes in the buffer. Note that the address of the packet must
+ * comply with any alignment constraints that the port may have, or the result
+ * will be undefined. The user may read the alignment constraint in the new
+ * ring->buf_align field.  It is also possible that empty slots already come
+ * with a non-zero offset o specified in the offset field. In this case, the
+ * user will have to write the packet at an offset o' >= o.
+ *
+ * The user must also declare the @maxoff offset that she is going to use. Any
+ * offset larger than this will be truncated.
+ *
+ * The user may also declare a @mingap (ignored if zero) if she plans to use
+ * offsets to share the same buffer among several slots. Netmap will guarantee
+ * that it will never write more than @mingap bytes for each slot, irrespective
+ * of the buffer length.
+ */
+int nmport_offset(struct nmport_d *d, uint64_t initial, uint64_t maxoff,
+		uint64_t bits, uint64_t mingap);
+
+/* enable/disable options
+ *
+ * These functions can be used to disable options that the application cannot
+ * or doesn't want to handle, or to enable options that require special support
+ * from the application and are, therefore, disabled by default. Disabled
+ * options will cause an error if encountered during option parsing.
+ *
+ * If the option is unknown, nmport_disable_option is a NOP, while
+ * nmport_enable_option returns -1 and sets errno to EOPNOTSUPP.
+ *
+ * These functions are not threadsafe and are meant to be used at the beginning
+ * of the program.
+ */
+void nmport_disable_option(const char *opt);
+int nmport_enable_option(const char *opt);
+
+/* nmreq manipulation
+ *
+ * nmreq_header_init - initialize an nmreq_header
+ * @hdr		the nmreq_header to initialize
+ * @reqtype	the kind of netmap request
+ * @body	the body of the request
+ *
+ * Initialize the nr_version, nr_reqtype and nr_body fields of *@hdr.
+ * The other fields are set to zero.
+ */
+void nmreq_header_init(struct nmreq_header *hdr, uint16_t reqtype, void *body);
+
+/*
+ * These functions allow for finer grained parsing of portspecs.  They are used
+ * internally by nmport_parse().
+ */
+
+/* nmreq_header_decode - initialize an nmreq_header
+ * @ppspec:	(in/out) pointer to a pointer to the portspec
+ * @hdr:	pointer to the nmreq_header to be initialized
+ * @ctx:	pointer to the nmctx to use (for errors)
+ *
+ * This function fills the @hdr the nr_name field with the port name extracted
+ * from *@pifname.  The other fields of *@hdr are unchanged. The @pifname is
+ * updated to point at the first char past the port name.
+ *
+ * Returns 0 on success.  In case of error, -1 is returned with errno set to
+ * EINVAL, @pifname is unchanged, *@hdr is also unchanged, and an error message
+ * is sent through @ctx->error().
+ */
+int nmreq_header_decode(const char **ppspec, struct nmreq_header *hdr,
+		struct nmctx *ctx);
+
+/* nmreq_regiter_decode - initialize an nmreq_register
+ * @pmode:	(in/out) pointer to a pointer to an opening mode
+ * @reg:	pointer to the nmreq_register to be initialized
+ * @ctx:	pointer to the nmctx to use (for errors)
+ *
+ * This function fills the nr_mode, nr_ringid, nr_flags and nr_mem_id fields of
+ * the structure pointed by @reg, according to the opening mode specified by
+ * *@pmode. The other fields of *@reg are unchanged.  The @pmode is updated to
+ * point at the first char past the opening mode.
+ *
+ * If a '@' is encountered followed by something which is not a number, parsing
+ * stops (without error) and @pmode is left pointing at the '@' char. The
+ * nr_mode, nr_ringid and nr_flags fields are still updated, but nr_mem_id is
+ * not touched and the interpretation of the '@' field is left to the caller.
+ *
+ * Returns 0 on success.  In case of error, -1 is returned with errno set to
+ * EINVAL, @pmode is unchanged, *@reg is also unchanged, and an error message
+ * is sent through @ctx->error().
+ */
+int nmreq_register_decode(const char **pmode, struct nmreq_register *reg,
+		struct nmctx *ctx);
+
+/* nmreq_options_decode - parse the "options" part of the portspec
+ * @opt:	pointer to the option list
+ * @parsers:	list of option parsers
+ * @token:	token to pass to each parser
+ * @ctx:	pointer to the nmctx to use (for errors and malloc/free)
+ *
+ * This function parses each option in @opt. Each option is matched (based on
+ * the "option" prefix) to a corresponding parser in @parsers. The function
+ * checks that the syntax is appropriate for the parser and it assigns all the
+ * keys mentioned in the option. It then passes control to the parser, to
+ * interpret the keys values.
+ *
+ * Returns 0 on success. In case of error, -1 is returned, errno is set to an
+ * error value and a message is sent to @ctx->error(). The effects of partially
+ * interpreted options may not be undone.
+ */
+struct nmreq_opt_parser;
+int nmreq_options_decode(const char *opt, struct nmreq_opt_parser *parsers,
+		void *token, struct nmctx *ctx);
+
+struct nmreq_parse_ctx;
+/* type of the option-parsers callbacks */
+typedef int (*nmreq_opt_parser_cb)(struct nmreq_parse_ctx *);
+
+#define NMREQ_OPT_MAXKEYS 16	/* max nr of recognized keys per option */
+
+/* struct nmreq_opt_key - describes an option key */
+struct nmreq_opt_key {
+	const char *key;	/* the key name */
+	int id;			/* its position in the parse context */
+	unsigned int flags;
+#define NMREQ_OPTK_ALLOWEMPTY 	(1U << 0) /* =value may be omitted */
+#define NMREQ_OPTK_MUSTSET	(1U << 1) /* the key is mandatory */
+#define NMREQ_OPTK_DEFAULT	(1U << 2) /* this is the default key */
+};
+
+/* struct nmreq_opt_parser - describes an option parser */
+struct nmreq_opt_parser {
+	const char *prefix;	/* matches one option prefix */
+	nmreq_opt_parser_cb parse;	/* the parse callback */
+	int default_key;	/* which option is the default if the
+				   parser is multi-key (-1 if none) */
+	int nr_keys;
+	unsigned int flags;
+#define NMREQ_OPTF_DISABLED     (1U << 0)
+#define NMREQ_OPTF_ALLOWEMPTY	(1U << 1)	/* =value can be omitted */
+
+	struct nmreq_opt_parser *next;	/* list of options */
+
+	/* recognized keys */
+	struct nmreq_opt_key keys[NMREQ_OPT_MAXKEYS];
+} __attribute__((aligned(16)));
+
+/* struct nmreq_parse_ctx - the parse context received by the parse callback */
+struct nmreq_parse_ctx {
+	struct nmctx *ctx;	/* the nmctx for errors and malloc/free */
+	void *token;		/* the token passed to nmreq_options_parse */
+
+	/* the value (i.e., the part after the = sign) of each recognized key
+	 * is assigned to the corresponding entry in this array, based on the
+	 * key id. Unassigned keys are left at NULL.
+	 */
+	const char *keys[NMREQ_OPT_MAXKEYS];
+};
+
+/* nmreq_get_mem_id - get the mem_id of the given port
+ * @portname	pointer to a pointer to the portname
+ * @ctx		pointer to the nmctx to use (for errors)
+ *
+ * *@portname must point to a substem:vpname porname, possibly followed by
+ * something else.
+ *
+ * If successful, returns the mem_id of *@portname and moves @portname past the
+ * subsystem:vpname part of the input. In case of error it returns -1, sets
+ * errno to an error value and sends an error message to ctx->error().
+ */
+int32_t nmreq_get_mem_id(const char **portname, struct nmctx *ctx);
+
+/* option list manipulation */
+void nmreq_push_option(struct nmreq_header *, struct nmreq_option *);
+void nmreq_remove_option(struct nmreq_header *, struct nmreq_option *);
+struct nmreq_option *nmreq_find_option(struct nmreq_header *, uint32_t);
+void nmreq_free_options(struct nmreq_header *);
+const char* nmreq_option_name(uint32_t);
+#define nmreq_foreach_option(h_, o_) \
+	for ((o_) = (struct nmreq_option *)((uintptr_t)((h_)->nr_options));\
+	     (o_) != NULL;\
+	     (o_) = (struct nmreq_option *)((uintptr_t)((o_)->nro_next)))
+
+/* nmctx manipulation */
+
+/* the nmctx serves a few purposes:
+ *
+ * - maintain a list of all memory regions open by the program, so that two
+ *   ports that are using the same region (as identified by the mem_id) will
+ *   point to the same nmem_d instance.
+ *
+ * - allow the user to specify how to lock accesses to the above list, if
+ *   needed (lock() callback)
+ *
+ * - allow the user to specify how error messages should be delivered (error()
+ *   callback)
+ *
+ * - select the verbosity of the library (verbose field); if verbose==0, no
+ *   errors are sent to the error() callback
+ *
+ * - allow the user to override the malloc/free functions used by the library
+ *   (malloc() and free() callbacks)
+ *
+ */
+typedef void  (*nmctx_error_cb)(struct nmctx *, const char *);
+typedef void *(*nmctx_malloc_cb)(struct nmctx *,size_t);
+typedef void  (*nmctx_free_cb)(struct nmctx *,void *);
+typedef void  (*nmctx_lock_cb)(struct nmctx *, int);
+
+struct nmctx {
+	int verbose;
+	nmctx_error_cb 	error;
+	nmctx_malloc_cb	malloc;
+	nmctx_free_cb	free;
+	nmctx_lock_cb	lock;
+
+	struct nmem_d  *mem_descs;
+};
+
+/* nmctx_get - obtain a pointer to the current default context */
+struct nmctx *nmctx_get(void);
+
+/* nmctx_set_default - change the default context
+ * @ctx		pointer to the new context
+ *
+ * Returns a pointer to the previous default context.
+ */
+struct nmctx *nmctx_set_default(struct nmctx *ctx);
+
+/* internal functions and data structures */
+
+/* struct nmem_d - describes a memory region currently used */
+struct nmem_d {
+	uint16_t mem_id;	/* the region netmap identifier */
+	int refcount;		/* how many nmport_d's point here */
+	void *mem;		/* memory region base address */
+	size_t size;		/* memory region size */
+	int is_extmem;		/* was it obtained via extmem? */
+
+	/* pointers for the circular list implementation.
+	 * The list head is the mem_descs filed in the nmctx
+	 */
+	struct nmem_d *next;
+	struct nmem_d *prev;
+};
+
+/* a trick to force the inclusion of libpthread only if requested. If
+ * LIBNETMAP_NOTHREADSAFE is defined, no pthread symbol is imported.
+ *
+ * There is no need to actually call this function: the ((used)) attribute is
+ * sufficient to include it in the image.
+ */
+static  __attribute__((used)) void libnetmap_init(void)
+{
+#ifndef LIBNETMAP_NOTHREADSAFE
+	extern int nmctx_threadsafe;
+	/* dummy assignment to link-in the nmctx-pthread.o object.  The proper
+	 * inizialization is performed only once in the library constructor
+	 * defined there.
+	 */
+	nmctx_threadsafe = 1;
+#endif /* LIBNETMAP_NOTHREADSAFE */
+}
+
+/* nmctx_set_threadsafe - install a threadsafe default context
+ *
+ * called by the constructor in nmctx-pthread.o to initialize a lock and install
+ * the lock() callback in the default context.
+ */
+void nmctx_set_threadsafe(void);
+
+/* nmctx_ferror - format and send an error message */
+void nmctx_ferror(struct nmctx *, const char *, ...);
+/* nmctx_malloc - allocate memory */
+void *nmctx_malloc(struct nmctx *, size_t);
+/* nmctx_free - free memory allocated via nmctx_malloc */
+void nmctx_free(struct nmctx *, void *);
+/* nmctx_lock - lock the list of nmem_d */
+void nmctx_lock(struct nmctx *);
+/* nmctx_unlock - unlock the list of nmem_d */
+void nmctx_unlock(struct nmctx *);
+
+#endif /* LIBNETMAP_H_ */
diff --git a/libnetmap/nmctx-pthreads.c b/libnetmap/nmctx-pthreads.c
new file mode 100644
index 000000000..fdad20c7c
--- /dev/null
+++ b/libnetmap/nmctx-pthreads.c
@@ -0,0 +1,75 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018 Universita` di Pisa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "libnetmap.h"
+
+struct nmctx_pthread {
+	struct nmctx up;
+	pthread_mutex_t mutex;
+};
+
+static struct nmctx_pthread nmctx_pthreadsafe;
+
+static void
+nmctx_pthread_lock(struct nmctx *ctx, int lock)
+{
+	struct nmctx_pthread *ctxp =
+		(struct nmctx_pthread *)ctx;
+	if (lock) {
+		pthread_mutex_lock(&ctxp->mutex);
+	} else {
+		pthread_mutex_unlock(&ctxp->mutex);
+	}
+}
+
+void __attribute__ ((constructor))
+nmctx_set_threadsafe(void)
+{
+	struct nmctx *old;
+
+	pthread_mutex_init(&nmctx_pthreadsafe.mutex, NULL);
+	old = nmctx_set_default(&nmctx_pthreadsafe.up);
+	nmctx_pthreadsafe.up = *old;
+	nmctx_pthreadsafe.up.lock = nmctx_pthread_lock;
+}
+
+int nmctx_threadsafe;
diff --git a/libnetmap/nmctx.c b/libnetmap/nmctx.c
new file mode 100644
index 000000000..fe83b8864
--- /dev/null
+++ b/libnetmap/nmctx.c
@@ -0,0 +1,140 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018 Universita` di Pisa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#define LIBNETMAP_NOTHREADSAFE
+#include "libnetmap.h"
+
+static void
+nmctx_default_error(struct nmctx *ctx, const char *errmsg)
+{
+	(void)ctx;
+	fprintf(stderr, "%s\n", errmsg);
+}
+
+static void *
+nmctx_default_malloc(struct nmctx *ctx, size_t sz)
+{
+	(void)ctx;
+	return malloc(sz);
+}
+
+static void
+nmctx_default_free(struct nmctx *ctx, void *p)
+{
+	(void)ctx;
+	free(p);
+}
+
+static struct nmctx nmctx_global = {
+	.verbose = 1,
+	.error = nmctx_default_error,
+	.malloc = nmctx_default_malloc,
+	.free = nmctx_default_free,
+	.lock = NULL,
+};
+
+static struct nmctx *nmctx_default = &nmctx_global;
+
+struct nmctx *
+nmctx_get(void)
+{
+	return nmctx_default;
+}
+
+struct nmctx *
+nmctx_set_default(struct nmctx *ctx)
+{
+	struct nmctx *old = nmctx_default;
+	nmctx_default = ctx;
+	return old;
+}
+
+#define MAXERRMSG 1000
+void
+nmctx_ferror(struct nmctx *ctx, const char *fmt, ...)
+{
+	char errmsg[MAXERRMSG];
+	va_list ap;
+	int rv;
+
+	if (!ctx->verbose)
+		return;
+
+	va_start(ap, fmt);
+	rv = vsnprintf(errmsg, MAXERRMSG, fmt, ap);
+	va_end(ap);
+
+	if (rv > 0) {
+		if (rv < MAXERRMSG) {
+			ctx->error(ctx, errmsg);
+		} else {
+			ctx->error(ctx, "error message too long");
+		}
+	} else {
+		ctx->error(ctx, "internal error");
+	}
+}
+
+void *
+nmctx_malloc(struct nmctx *ctx, size_t sz)
+{
+	return ctx->malloc(ctx, sz);
+}
+
+void
+nmctx_free(struct nmctx *ctx, void *p)
+{
+	ctx->free(ctx, p);
+}
+
+void
+nmctx_lock(struct nmctx *ctx)
+{
+	if (ctx->lock != NULL)
+		ctx->lock(ctx, 1);
+}
+
+void
+nmctx_unlock(struct nmctx *ctx)
+{
+	if (ctx->lock != NULL)
+		ctx->lock(ctx, 0);
+}
diff --git a/libnetmap/nmport.c b/libnetmap/nmport.c
new file mode 100644
index 000000000..c892c94db
--- /dev/null
+++ b/libnetmap/nmport.c
@@ -0,0 +1,914 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018 Universita` di Pisa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#define LIBNETMAP_NOTHREADSAFE
+#include "libnetmap.h"
+
+struct nmport_cleanup_d {
+	struct nmport_cleanup_d *next;
+	void (*cleanup)(struct nmport_cleanup_d *, struct nmport_d *);
+};
+
+static void
+nmport_push_cleanup(struct nmport_d *d, struct nmport_cleanup_d *c)
+{
+	c->next = d->clist;
+	d->clist = c;
+}
+
+static void
+nmport_pop_cleanup(struct nmport_d *d)
+{
+	struct nmport_cleanup_d *top;
+
+	top = d->clist;
+	d->clist = d->clist->next;
+	(*top->cleanup)(top, d);
+	nmctx_free(d->ctx, top);
+}
+
+void nmport_do_cleanup(struct nmport_d *d)
+{
+	while (d->clist != NULL) {
+		nmport_pop_cleanup(d);
+	}
+}
+
+static struct nmport_d *
+nmport_new_with_ctx(struct nmctx *ctx)
+{
+	struct nmport_d *d;
+
+	/* allocate a descriptor */
+	d = nmctx_malloc(ctx, sizeof(*d));
+	if (d == NULL) {
+		nmctx_ferror(ctx, "cannot allocate nmport descriptor");
+		goto out;
+	}
+	memset(d, 0, sizeof(*d));
+
+	nmreq_header_init(&d->hdr, NETMAP_REQ_REGISTER, &d->reg);
+
+	d->ctx = ctx;
+	d->fd = -1;
+
+out:
+	return d;
+}
+
+struct nmport_d *
+nmport_new(void)
+{
+	struct nmctx *ctx = nmctx_get();
+	return nmport_new_with_ctx(ctx);
+}
+
+
+void
+nmport_delete(struct nmport_d *d)
+{
+	nmctx_free(d->ctx, d);
+}
+
+void
+nmport_extmem_cleanup(struct nmport_cleanup_d *c, struct nmport_d *d)
+{
+	(void)c;
+
+	if (d->extmem == NULL)
+		return;
+
+	nmreq_remove_option(&d->hdr, &d->extmem->nro_opt);
+	nmctx_free(d->ctx, d->extmem);
+	d->extmem = NULL;
+}
+
+
+int
+nmport_extmem(struct nmport_d *d, void *base, size_t size)
+{
+	struct nmctx *ctx = d->ctx;
+	struct nmport_cleanup_d *clnup = NULL;
+
+	if (d->register_done) {
+		nmctx_ferror(ctx, "%s: cannot set extmem of an already registered port", d->hdr.nr_name);
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (d->extmem != NULL) {
+		nmctx_ferror(ctx, "%s: extmem already in use", d->hdr.nr_name);
+		errno = EINVAL;
+		return -1;
+	}
+
+	clnup = (struct nmport_cleanup_d *)nmctx_malloc(ctx, sizeof(*clnup));
+	if (clnup == NULL) {
+		nmctx_ferror(ctx, "failed to allocate cleanup descriptor");
+		errno = ENOMEM;
+		return -1;
+	}
+
+	d->extmem = nmctx_malloc(ctx, sizeof(*d->extmem));
+	if (d->extmem == NULL) {
+		nmctx_ferror(ctx, "%s: cannot allocate extmem option", d->hdr.nr_name);
+		nmctx_free(ctx, clnup);
+		errno = ENOMEM;
+		return -1;
+	}
+	memset(d->extmem, 0, sizeof(*d->extmem));
+	d->extmem->nro_usrptr = (uintptr_t)base;
+	d->extmem->nro_opt.nro_reqtype = NETMAP_REQ_OPT_EXTMEM;
+	d->extmem->nro_info.nr_memsize = size;
+	nmreq_push_option(&d->hdr, &d->extmem->nro_opt);
+
+	clnup->cleanup = nmport_extmem_cleanup;
+	nmport_push_cleanup(d, clnup);
+
+	return 0;
+}
+
+struct nmport_extmem_from_file_cleanup_d {
+	struct nmport_cleanup_d up;
+	void *p;
+	size_t size;
+};
+
+void nmport_extmem_from_file_cleanup(struct nmport_cleanup_d *c,
+		struct nmport_d *d)
+{
+	(void)d;
+	struct nmport_extmem_from_file_cleanup_d *cc =
+		(struct nmport_extmem_from_file_cleanup_d *)c;
+
+	munmap(cc->p, cc->size);
+}
+
+int
+nmport_extmem_from_file(struct nmport_d *d, const char *fname)
+{
+	struct nmctx *ctx = d->ctx;
+	int fd = -1;
+	off_t mapsize;
+	void *p;
+	struct nmport_extmem_from_file_cleanup_d *clnup = NULL;
+
+	clnup = nmctx_malloc(ctx, sizeof(*clnup));
+	if (clnup == NULL) {
+		nmctx_ferror(ctx, "cannot allocate cleanup descriptor");
+		errno = ENOMEM;
+		goto fail;
+	}
+	clnup->up.cleanup = NULL;
+
+	fd = open(fname, O_RDWR);
+	if (fd < 0) {
+		nmctx_ferror(ctx, "cannot open '%s': %s", fname, strerror(errno));
+		goto fail;
+	}
+	mapsize = lseek(fd, 0, SEEK_END);
+	if (mapsize < 0) {
+		nmctx_ferror(ctx, "failed to obtain filesize of '%s': %s", fname, strerror(errno));
+		goto fail;
+	}
+	p = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+	if (p == MAP_FAILED) {
+		nmctx_ferror(ctx, "cannot mmap '%s': %s", fname, strerror(errno));
+		goto fail;
+	}
+	close(fd);
+	fd = -1;
+
+	clnup->p = p;
+	clnup->size = mapsize;
+	clnup->up.cleanup = nmport_extmem_from_file_cleanup;
+	nmport_push_cleanup(d, &clnup->up);
+
+	if (nmport_extmem(d, p, mapsize) < 0)
+		goto fail;
+
+	return 0;
+
+fail:
+	if (fd >= 0)
+		close(fd);
+	if (clnup != NULL) {
+		if (clnup->up.cleanup != NULL)
+			nmport_pop_cleanup(d);
+		else
+			nmctx_free(ctx, clnup);
+	}
+	return -1;
+}
+
+struct nmreq_pools_info*
+nmport_extmem_getinfo(struct nmport_d *d)
+{
+	if (d->extmem == NULL)
+		return NULL;
+	return &d->extmem->nro_info;
+}
+
+struct nmport_offset_cleanup_d {
+	struct nmport_cleanup_d up;
+	struct nmreq_opt_offsets *opt;
+};
+
+static void
+nmport_offset_cleanup(struct nmport_cleanup_d *c,
+		struct nmport_d *d)
+{
+	struct nmport_offset_cleanup_d *cc =
+		(struct nmport_offset_cleanup_d *)c;
+
+	nmreq_remove_option(&d->hdr, &cc->opt->nro_opt);
+	nmctx_free(d->ctx, cc->opt);
+}
+
+int
+nmport_offset(struct nmport_d *d, uint64_t initial,
+		uint64_t maxoff, uint64_t bits, uint64_t mingap)
+{
+	struct nmctx *ctx = d->ctx;
+	struct nmreq_opt_offsets *opt;
+	struct nmport_offset_cleanup_d *clnup = NULL;
+
+	clnup = nmctx_malloc(ctx, sizeof(*clnup));
+	if (clnup == NULL) {
+		nmctx_ferror(ctx, "cannot allocate cleanup descriptor");
+		errno = ENOMEM;
+		return -1;
+	}
+
+	opt = nmctx_malloc(ctx, sizeof(*opt));
+	if (opt == NULL) {
+		nmctx_ferror(ctx, "%s: cannot allocate offset option", d->hdr.nr_name);
+		nmctx_free(ctx, clnup);
+		errno = ENOMEM;
+		return -1;
+	}
+	memset(opt, 0, sizeof(*opt));
+	opt->nro_opt.nro_reqtype = NETMAP_REQ_OPT_OFFSETS;
+	opt->nro_offset_bits = bits;
+	opt->nro_initial_offset = initial;
+	opt->nro_max_offset = maxoff;
+	opt->nro_min_gap = mingap;
+	nmreq_push_option(&d->hdr, &opt->nro_opt);
+
+	clnup->up.cleanup = nmport_offset_cleanup;
+	clnup->opt = opt;
+	nmport_push_cleanup(d, &clnup->up);
+
+	return 0;
+}
+
+/* head of the list of options */
+static struct nmreq_opt_parser *nmport_opt_parsers;
+
+#define NPOPT_PARSER(o)		nmport_opt_##o##_parser
+#define NPOPT_DESC(o)		nmport_opt_##o##_desc
+#define NPOPT_NRKEYS(o)		(NPOPT_DESC(o).nr_keys)
+#define NPOPT_DECL(o, f)						\
+static int NPOPT_PARSER(o)(struct nmreq_parse_ctx *);			\
+static struct nmreq_opt_parser NPOPT_DESC(o) = {			\
+	.prefix = #o,							\
+	.parse = NPOPT_PARSER(o),					\
+	.flags = (f),							\
+	.default_key = -1,						\
+	.nr_keys = 0,							\
+	.next = NULL,							\
+};									\
+static void __attribute__((constructor))				\
+nmport_opt_##o##_ctor(void)						\
+{									\
+	NPOPT_DESC(o).next = nmport_opt_parsers;			\
+	nmport_opt_parsers = &NPOPT_DESC(o);				\
+}
+struct nmport_key_desc {
+	struct nmreq_opt_parser *option;
+	const char *key;
+	unsigned int flags;
+	int id;
+};
+static void
+nmport_opt_key_ctor(struct nmport_key_desc *k)
+{
+	struct nmreq_opt_parser *o = k->option;
+	struct nmreq_opt_key *ok;
+
+	k->id = o->nr_keys;
+	ok = &o->keys[k->id];
+	ok->key = k->key;
+	ok->id = k->id;
+	ok->flags = k->flags;
+	o->nr_keys++;
+	if (ok->flags & NMREQ_OPTK_DEFAULT)
+		o->default_key = ok->id;
+}
+#define NPKEY_DESC(o, k)	nmport_opt_##o##_key_##k##_desc
+#define NPKEY_ID(o, k)		(NPKEY_DESC(o, k).id)
+#define NPKEY_DECL(o, k, f)						\
+static struct nmport_key_desc NPKEY_DESC(o, k) = {			\
+	.option = &NPOPT_DESC(o),					\
+	.key = #k,							\
+	.flags = (f),							\
+	.id = -1,							\
+};									\
+static void __attribute__((constructor))				\
+nmport_opt_##o##_key_##k##_ctor(void)					\
+{									\
+	nmport_opt_key_ctor(&NPKEY_DESC(o, k));				\
+}
+#define nmport_key(p, o, k)	((p)->keys[NPKEY_ID(o, k)])
+#define nmport_defkey(p, o)	((p)->keys[NPOPT_DESC(o).default_key])
+
+NPOPT_DECL(share, 0)
+	NPKEY_DECL(share, port, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
+NPOPT_DECL(extmem, 0)
+	NPKEY_DECL(extmem, file, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
+	NPKEY_DECL(extmem, if_num, 0)
+	NPKEY_DECL(extmem, if_size, 0)
+	NPKEY_DECL(extmem, ring_num, 0)
+	NPKEY_DECL(extmem, ring_size, 0)
+	NPKEY_DECL(extmem, buf_num, 0)
+	NPKEY_DECL(extmem, buf_size, 0)
+NPOPT_DECL(conf, 0)
+	NPKEY_DECL(conf, rings, 0)
+	NPKEY_DECL(conf, host_rings, 0)
+	NPKEY_DECL(conf, slots, 0)
+	NPKEY_DECL(conf, tx_rings, 0)
+	NPKEY_DECL(conf, rx_rings, 0)
+	NPKEY_DECL(conf, host_tx_rings, 0)
+	NPKEY_DECL(conf, host_rx_rings, 0)
+	NPKEY_DECL(conf, tx_slots, 0)
+	NPKEY_DECL(conf, rx_slots, 0)
+NPOPT_DECL(offset, NMREQ_OPTF_DISABLED)
+	NPKEY_DECL(offset, initial, NMREQ_OPTK_DEFAULT|NMREQ_OPTK_MUSTSET)
+	NPKEY_DECL(offset, bits, 0)
+
+
+static int
+NPOPT_PARSER(share)(struct nmreq_parse_ctx *p)
+{
+	struct nmctx *ctx = p->ctx;
+	struct nmport_d *d = p->token;
+	int32_t mem_id;
+	const char *v = nmport_defkey(p, share);
+
+	mem_id = nmreq_get_mem_id(&v, ctx);
+	if (mem_id < 0)
+		return -1;
+	if (d->reg.nr_mem_id && d->reg.nr_mem_id != mem_id) {
+		nmctx_ferror(ctx, "cannot set mem_id to %"PRId32", already set to %"PRIu16"",
+				mem_id, d->reg.nr_mem_id);
+		errno = EINVAL;
+		return -1;
+	}
+	d->reg.nr_mem_id = mem_id;
+	return 0;
+}
+
+static int
+NPOPT_PARSER(extmem)(struct nmreq_parse_ctx *p)
+{
+	struct nmport_d *d;
+	struct nmreq_pools_info *pi;
+	int i;
+
+	d = p->token;
+
+	if (nmport_extmem_from_file(d, nmport_key(p, extmem, file)) < 0)
+		return -1;
+
+	pi = &d->extmem->nro_info;
+
+	for  (i = 0; i < NPOPT_NRKEYS(extmem); i++) {
+		const char *k = p->keys[i];
+		uint32_t v;
+
+		if (k == NULL)
+			continue;
+
+		v = atoi(k);
+		if (i == NPKEY_ID(extmem, if_num)) {
+			pi->nr_if_pool_objtotal = v;
+		} else if (i == NPKEY_ID(extmem, if_size)) {
+			pi->nr_if_pool_objsize = v;
+		} else if (i == NPKEY_ID(extmem, ring_num)) {
+			pi->nr_ring_pool_objtotal = v;
+		} else if (i == NPKEY_ID(extmem, ring_size)) {
+			pi->nr_ring_pool_objsize = v;
+		} else if (i == NPKEY_ID(extmem, buf_num)) {
+			pi->nr_buf_pool_objtotal = v;
+		} else if (i == NPKEY_ID(extmem, buf_size)) {
+			pi->nr_buf_pool_objsize = v;
+		}
+	}
+	return 0;
+}
+
+static int
+NPOPT_PARSER(conf)(struct nmreq_parse_ctx *p)
+{
+	struct nmport_d *d;
+
+	d = p->token;
+
+	if (nmport_key(p, conf, rings) != NULL) {
+		uint16_t nr_rings = atoi(nmport_key(p, conf, rings));
+		d->reg.nr_tx_rings = nr_rings;
+		d->reg.nr_rx_rings = nr_rings;
+	}
+	if (nmport_key(p, conf, host_rings) != NULL) {
+		uint16_t nr_rings = atoi(nmport_key(p, conf, host_rings));
+		d->reg.nr_host_tx_rings = nr_rings;
+		d->reg.nr_host_rx_rings = nr_rings;
+	}
+	if (nmport_key(p, conf, slots) != NULL) {
+		uint32_t nr_slots = atoi(nmport_key(p, conf, slots));
+		d->reg.nr_tx_slots = nr_slots;
+		d->reg.nr_rx_slots = nr_slots;
+	}
+	if (nmport_key(p, conf, tx_rings) != NULL) {
+		d->reg.nr_tx_rings = atoi(nmport_key(p, conf, tx_rings));
+	}
+	if (nmport_key(p, conf, rx_rings) != NULL) {
+		d->reg.nr_rx_rings = atoi(nmport_key(p, conf, rx_rings));
+	}
+	if (nmport_key(p, conf, host_tx_rings) != NULL) {
+		d->reg.nr_host_tx_rings = atoi(nmport_key(p, conf, host_tx_rings));
+	}
+	if (nmport_key(p, conf, host_rx_rings) != NULL) {
+		d->reg.nr_host_rx_rings = atoi(nmport_key(p, conf, host_rx_rings));
+	}
+	if (nmport_key(p, conf, tx_slots) != NULL) {
+		d->reg.nr_tx_slots = atoi(nmport_key(p, conf, tx_slots));
+	}
+	if (nmport_key(p, conf, rx_slots) != NULL) {
+		d->reg.nr_rx_slots = atoi(nmport_key(p, conf, rx_slots));
+	}
+	return 0;
+}
+
+static int
+NPOPT_PARSER(offset)(struct nmreq_parse_ctx *p)
+{
+	struct nmport_d *d;
+	uint64_t initial, bits;
+
+	d = p->token;
+
+	initial = atoi(nmport_key(p, offset, initial));
+	bits = 0;
+	if (nmport_key(p, offset, bits) != NULL)
+		bits = atoi(nmport_key(p, offset, bits));
+
+	return nmport_offset(d, initial, initial, bits, 0);
+}
+
+
+void
+nmport_disable_option(const char *opt)
+{
+	struct nmreq_opt_parser *p;
+
+	for (p = nmport_opt_parsers; p != NULL; p = p->next) {
+		if (!strcmp(p->prefix, opt)) {
+			p->flags |= NMREQ_OPTF_DISABLED;
+		}
+	}
+}
+
+int
+nmport_enable_option(const char *opt)
+{
+	struct nmreq_opt_parser *p;
+
+	for (p = nmport_opt_parsers; p != NULL; p = p->next) {
+		if (!strcmp(p->prefix, opt)) {
+			p->flags &= ~NMREQ_OPTF_DISABLED;
+			return 0;
+		}
+	}
+	errno = EOPNOTSUPP;
+	return -1;
+}
+
+
+int
+nmport_parse(struct nmport_d *d, const char *ifname)
+{
+	const char *scan = ifname;
+
+	if (nmreq_header_decode(&scan, &d->hdr, d->ctx) < 0) {
+		goto err;
+	}
+
+	/* parse the register request */
+	if (nmreq_register_decode(&scan, &d->reg, d->ctx) < 0) {
+		goto err;
+	}
+
+	/* parse the options, if any */
+	if (nmreq_options_decode(scan, nmport_opt_parsers, d, d->ctx) < 0) {
+		goto err;
+	}
+	return 0;
+
+err:
+	nmport_undo_parse(d);
+	return -1;
+}
+
+void
+nmport_undo_parse(struct nmport_d *d)
+{
+	nmport_do_cleanup(d);
+	memset(&d->reg, 0, sizeof(d->reg));
+	memset(&d->hdr, 0, sizeof(d->hdr));
+}
+
+struct nmport_d *
+nmport_prepare(const char *ifname)
+{
+	struct nmport_d *d;
+
+	/* allocate a descriptor */
+	d = nmport_new();
+	if (d == NULL)
+		goto err;
+
+	/* parse the header */
+	if (nmport_parse(d, ifname) < 0)
+		goto err;
+
+	return d;
+
+err:
+	nmport_undo_prepare(d);
+	return NULL;
+}
+
+void
+nmport_undo_prepare(struct nmport_d *d)
+{
+	if (d == NULL)
+		return;
+	nmport_undo_parse(d);
+	nmport_delete(d);
+}
+
+int
+nmport_register(struct nmport_d *d)
+{
+	struct nmctx *ctx = d->ctx;
+
+	if (d->register_done) {
+		errno = EINVAL;
+		nmctx_ferror(ctx, "%s: already registered", d->hdr.nr_name);
+		return -1;
+	}
+
+	d->fd = open("/dev/netmap", O_RDWR);
+	if (d->fd < 0) {
+		nmctx_ferror(ctx, "/dev/netmap: %s", strerror(errno));
+		goto err;
+	}
+
+	if (ioctl(d->fd, NIOCCTRL, &d->hdr) < 0) {
+		struct nmreq_option *o;
+		int option_errors = 0;
+
+		nmreq_foreach_option(&d->hdr, o) {
+			if (o->nro_status) {
+				nmctx_ferror(ctx, "%s: option %s: %s",
+						d->hdr.nr_name,
+						nmreq_option_name(o->nro_reqtype),
+						strerror(o->nro_status));
+				option_errors++;
+			}
+
+		}
+		if (!option_errors)
+			nmctx_ferror(ctx, "%s: %s", d->hdr.nr_name, strerror(errno));
+		goto err;
+	}
+
+	d->register_done = 1;
+
+	return 0;
+
+err:
+	nmport_undo_register(d);
+	return -1;
+}
+
+void
+nmport_undo_register(struct nmport_d *d)
+{
+	if (d->fd >= 0)
+		close(d->fd);
+	d->fd = -1;
+	d->register_done = 0;
+}
+
+/* lookup the mem_id in the mem-list: do a new mmap() if
+ * not found, reuse existing otherwise
+ */
+int
+nmport_mmap(struct nmport_d *d)
+{
+	struct nmctx *ctx = d->ctx;
+	struct nmem_d *m = NULL;
+	u_int num_tx, num_rx;
+	unsigned int i;
+
+	if (d->mmap_done) {
+		errno = EINVAL;
+		nmctx_ferror(ctx, "%s: already mapped", d->hdr.nr_name);
+		return -1;
+	}
+
+	if (!d->register_done) {
+		errno = EINVAL;
+		nmctx_ferror(ctx, "cannot map unregistered port");
+		return -1;
+	}
+
+	nmctx_lock(ctx);
+
+	for (m = ctx->mem_descs; m != NULL; m = m->next)
+		if (m->mem_id == d->reg.nr_mem_id)
+			break;
+
+	if (m == NULL) {
+		m = nmctx_malloc(ctx, sizeof(*m));
+		if (m == NULL) {
+			nmctx_ferror(ctx, "cannot allocate memory descriptor");
+			goto err;
+		}
+		memset(m, 0, sizeof(*m));
+		if (d->extmem != NULL) {
+			m->mem = (void *)((uintptr_t)d->extmem->nro_usrptr);
+			m->size = d->extmem->nro_info.nr_memsize;
+			m->is_extmem = 1;
+		} else {
+			m->mem = mmap(NULL, d->reg.nr_memsize, PROT_READ|PROT_WRITE,
+					MAP_SHARED, d->fd, 0);
+			if (m->mem == MAP_FAILED) {
+				nmctx_ferror(ctx, "mmap: %s", strerror(errno));
+				goto err;
+			}
+			m->size = d->reg.nr_memsize;
+		}
+		m->mem_id = d->reg.nr_mem_id;
+		m->next = ctx->mem_descs;
+		if (ctx->mem_descs != NULL)
+			ctx->mem_descs->prev = m;
+		ctx->mem_descs = m;
+	}
+	m->refcount++;
+
+	nmctx_unlock(ctx);
+
+	d->mem = m;
+
+	d->nifp = NETMAP_IF(m->mem, d->reg.nr_offset);
+
+	num_tx = d->reg.nr_tx_rings + d->nifp->ni_host_tx_rings;
+	for (i = 0; i < num_tx && !d->nifp->ring_ofs[i]; i++)
+		;
+	d->cur_tx_ring = d->first_tx_ring = i;
+	for ( ; i < num_tx && d->nifp->ring_ofs[i]; i++)
+		;
+	d->last_tx_ring = i - 1;
+
+	num_rx = d->reg.nr_rx_rings + d->nifp->ni_host_rx_rings;
+	for (i = 0; i < num_rx && !d->nifp->ring_ofs[i + num_tx]; i++)
+		;
+	d->cur_rx_ring = d->first_rx_ring = i;
+	for ( ; i < num_rx && d->nifp->ring_ofs[i + num_tx]; i++)
+		;
+	d->last_rx_ring = i - 1;
+
+	d->mmap_done = 1;
+
+	return 0;
+
+err:
+	nmctx_unlock(ctx);
+	nmport_undo_mmap(d);
+	return -1;
+}
+
+void
+nmport_undo_mmap(struct nmport_d *d)
+{
+	struct nmem_d *m;
+	struct nmctx *ctx = d->ctx;
+
+	m = d->mem;
+	if (m == NULL)
+		return;
+	nmctx_lock(ctx);
+	m->refcount--;
+	if (m->refcount <= 0) {
+		if (!m->is_extmem && m->mem != MAP_FAILED)
+			munmap(m->mem, m->size);
+		/* extract from the list and free */
+		if (m->next != NULL)
+			m->next->prev = m->prev;
+		if (m->prev != NULL)
+			m->prev->next = m->next;
+		else
+			ctx->mem_descs = m->next;
+		nmctx_free(ctx, m);
+		d->mem = NULL;
+	}
+	nmctx_unlock(ctx);
+	d->mmap_done = 0;
+	d->mem = NULL;
+	d->nifp = NULL;
+	d->first_tx_ring = 0;
+	d->last_tx_ring = 0;
+	d->first_rx_ring = 0;
+	d->last_rx_ring = 0;
+	d->cur_tx_ring = 0;
+	d->cur_rx_ring = 0;
+}
+
+int
+nmport_open_desc(struct nmport_d *d)
+{
+	if (nmport_register(d) < 0)
+		goto err;
+
+	if (nmport_mmap(d) < 0)
+		goto err;
+
+	return 0;
+err:
+	nmport_undo_open_desc(d);
+	return -1;
+}
+
+void
+nmport_undo_open_desc(struct nmport_d *d)
+{
+	nmport_undo_mmap(d);
+	nmport_undo_register(d);
+}
+
+
+struct nmport_d *
+nmport_open(const char *ifname)
+{
+	struct nmport_d *d;
+
+	/* prepare the descriptor */
+	d = nmport_prepare(ifname);
+	if (d == NULL)
+		goto err;
+
+	/* open netmap and register */
+	if (nmport_open_desc(d) < 0)
+		goto err;
+
+	return d;
+
+err:
+	nmport_close(d);
+	return NULL;
+}
+
+void
+nmport_close(struct nmport_d *d)
+{
+	if (d == NULL)
+		return;
+	nmport_undo_open_desc(d);
+	nmport_undo_prepare(d);
+}
+
+struct nmport_d *
+nmport_clone(struct nmport_d *d)
+{
+	struct nmport_d *c;
+	struct nmctx *ctx;
+
+	ctx = d->ctx;
+
+	if (d->extmem != NULL && !d->register_done) {
+		errno = EINVAL;
+		nmctx_ferror(ctx, "cannot clone unregistered port that is using extmem");
+		return NULL;
+	}
+
+	c = nmport_new_with_ctx(ctx);
+	if (c == NULL)
+		return NULL;
+	/* copy the output of parse */
+	c->hdr = d->hdr;
+	/* redirect the pointer to the body */
+	c->hdr.nr_body = (uintptr_t)&c->reg;
+	/* options are not cloned */
+	c->hdr.nr_options = 0;
+	c->reg = d->reg; /* this also copies the mem_id */
+	/* put the new port in an un-registered, unmapped state */
+	c->fd = -1;
+	c->nifp = NULL;
+	c->register_done = 0;
+	c->mem = NULL;
+	c->extmem = NULL;
+	c->mmap_done = 0;
+	c->first_tx_ring = 0;
+	c->last_tx_ring = 0;
+	c->first_rx_ring = 0;
+	c->last_rx_ring = 0;
+	c->cur_tx_ring = 0;
+	c->cur_rx_ring = 0;
+
+	return c;
+}
+
+int
+nmport_inject(struct nmport_d *d, const void *buf, size_t size)
+{
+	u_int c, n = d->last_tx_ring - d->first_tx_ring + 1,
+		ri = d->cur_tx_ring;
+
+	for (c = 0; c < n ; c++, ri++) {
+		/* compute current ring to use */
+		struct netmap_ring *ring;
+		uint32_t i, j, idx;
+		size_t rem;
+
+		if (ri > d->last_tx_ring)
+			ri = d->first_tx_ring;
+		ring = NETMAP_TXRING(d->nifp, ri);
+		rem = size;
+		j = ring->cur;
+		while (rem > ring->nr_buf_size && j != ring->tail) {
+			rem -= ring->nr_buf_size;
+			j = nm_ring_next(ring, j);
+		}
+		if (j == ring->tail && rem > 0)
+			continue;
+		i = ring->cur;
+		while (i != j) {
+			idx = ring->slot[i].buf_idx;
+			ring->slot[i].len = ring->nr_buf_size;
+			ring->slot[i].flags = NS_MOREFRAG;
+			nm_pkt_copy(buf, NETMAP_BUF(ring, idx), ring->nr_buf_size);
+			i = nm_ring_next(ring, i);
+			buf = (char *)buf + ring->nr_buf_size;
+		}
+		idx = ring->slot[i].buf_idx;
+		ring->slot[i].len = rem;
+		ring->slot[i].flags = 0;
+		nm_pkt_copy(buf, NETMAP_BUF(ring, idx), rem);
+		ring->head = ring->cur = nm_ring_next(ring, i);
+		d->cur_tx_ring = ri;
+		return size;
+	}
+	return 0; /* fail */
+}
diff --git a/libnetmap/nmreq.c b/libnetmap/nmreq.c
new file mode 100644
index 000000000..a083099f0
--- /dev/null
+++ b/libnetmap/nmreq.c
@@ -0,0 +1,716 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018 Universita` di Pisa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+//#define NMREQ_DEBUG
+#ifdef NMREQ_DEBUG
+#define NETMAP_WITH_LIBS
+#define ED(...)	D(__VA_ARGS__)
+#else
+#define ED(...)
+/* an identifier is a possibly empty sequence of alphanum characters and
+ * underscores
+ */
+static int
+nm_is_identifier(const char *s, const char *e)
+{
+	for (; s != e; s++) {
+		if (!isalnum(*s) && *s != '_') {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+#endif /* NMREQ_DEBUG */
+
+#include 
+#define LIBNETMAP_NOTHREADSAFE
+#include "libnetmap.h"
+
+void
+nmreq_push_option(struct nmreq_header *h, struct nmreq_option *o)
+{
+	o->nro_next = h->nr_options;
+	h->nr_options = (uintptr_t)o;
+}
+
+struct nmreq_prefix {
+	const char *prefix;		/* the constant part of the prefix */
+	size_t	    len;		/* its strlen() */
+	uint32_t    flags;
+#define	NR_P_ID		(1U << 0)	/* whether an identifier is needed */
+#define NR_P_SKIP	(1U << 1)	/* whether the scope must be passed to netmap */
+#define NR_P_EMPTYID	(1U << 2)	/* whether an empty identifier is allowed */
+};
+
+#define declprefix(prefix, flags)	{ (prefix), (sizeof(prefix) - 1), (flags) }
+
+static struct nmreq_prefix nmreq_prefixes[] = {
+	declprefix("netmap", NR_P_SKIP),
+	declprefix(NM_BDG_NAME,	NR_P_ID|NR_P_EMPTYID),
+	{ NULL } /* terminate the list */
+};
+
+void
+nmreq_header_init(struct nmreq_header *h, uint16_t reqtype, void *body)
+{
+	memset(h, 0, sizeof(*h));
+	h->nr_version = NETMAP_API;
+	h->nr_reqtype = reqtype;
+	h->nr_body = (uintptr_t)body;
+}
+
+int
+nmreq_header_decode(const char **pifname, struct nmreq_header *h, struct nmctx *ctx)
+{
+	const char *scan = NULL;
+	const char *vpname = NULL;
+	const char *pipesep = NULL;
+	u_int namelen;
+	const char *ifname = *pifname;
+	struct nmreq_prefix *p;
+
+	scan = ifname;
+	for (p = nmreq_prefixes; p->prefix != NULL; p++) {
+		if (!strncmp(scan, p->prefix, p->len))
+			break;
+	}
+	if (p->prefix == NULL) {
+		nmctx_ferror(ctx, "%s: invalid request, prefix unknown or missing", *pifname);
+		goto fail;
+	}
+	scan += p->len;
+
+	vpname = index(scan, ':');
+	if (vpname == NULL) {
+		nmctx_ferror(ctx, "%s: missing ':'", ifname);
+		goto fail;
+	}
+	if (vpname != scan) {
+		/* there is an identifier, can we accept it? */
+		if (!(p->flags & NR_P_ID)) {
+			nmctx_ferror(ctx, "%s: no identifier allowed between '%s' and ':'", *pifname, p->prefix);
+			goto fail;
+		}
+
+		if (!nm_is_identifier(scan, vpname)) {
+			nmctx_ferror(ctx, "%s: invalid identifier '%.*s'", *pifname, vpname - scan, scan);
+			goto fail;
+		}
+	} else {
+		if ((p->flags & NR_P_ID) && !(p->flags & NR_P_EMPTYID)) {
+			nmctx_ferror(ctx, "%s: identifier is missing between '%s' and ':'", *pifname, p->prefix);
+			goto fail;
+		}
+	}
+	++vpname; /* skip the colon */
+	if (p->flags & NR_P_SKIP)
+		ifname = vpname;
+	scan = vpname;
+
+	/* scan for a separator */
+	for (; *scan && !index("-*^/@", *scan); scan++)
+		;
+
+	/* search for possible pipe indicators */
+	for (pipesep = vpname; pipesep != scan && !index("{}", *pipesep); pipesep++)
+		;
+
+	if (pipesep != scan) {
+		pipesep++;
+		if (*pipesep == '\0') {
+			nmctx_ferror(ctx, "%s: invalid empty pipe name", *pifname);
+			goto fail;
+		}
+		if (!nm_is_identifier(pipesep, scan)) {
+			nmctx_ferror(ctx, "%s: invalid pipe name '%.*s'", *pifname, scan - pipesep, pipesep);
+			goto fail;
+		}
+	}
+
+	namelen = scan - ifname;
+	if (namelen >= sizeof(h->nr_name)) {
+		nmctx_ferror(ctx, "name '%.*s' too long", namelen, ifname);
+		goto fail;
+	}
+	if (namelen == 0) {
+		nmctx_ferror(ctx, "%s: invalid empty port name", *pifname);
+		goto fail;
+	}
+
+	/* fill the header */
+	memcpy(h->nr_name, ifname, namelen);
+	h->nr_name[namelen] = '\0';
+	ED("name %s", h->nr_name);
+
+	*pifname = scan;
+
+	return 0;
+fail:
+	errno = EINVAL;
+	return -1;
+}
+
+
+/*
+ * 0 not recognized
+ * -1 error
+ *  >= 0 mem_id
+ */
+int32_t
+nmreq_get_mem_id(const char **pifname, struct nmctx *ctx)
+{
+	int fd = -1;
+	struct nmreq_header gh;
+	struct nmreq_port_info_get gb;
+	const char *ifname;
+
+	errno = 0;
+	ifname = *pifname;
+
+	if (ifname == NULL)
+		goto fail;
+
+	/* try to look for a netmap port with this name */
+	fd = open("/dev/netmap", O_RDWR);
+	if (fd < 0) {
+		nmctx_ferror(ctx, "cannot open /dev/netmap: %s", strerror(errno));
+		goto fail;
+	}
+	nmreq_header_init(&gh, NETMAP_REQ_PORT_INFO_GET, &gb);
+	if (nmreq_header_decode(&ifname, &gh, ctx) < 0) {
+		goto fail;
+	}
+	memset(&gb, 0, sizeof(gb));
+	if (ioctl(fd, NIOCCTRL, &gh) < 0) {
+		nmctx_ferror(ctx, "cannot get info for '%s': %s", *pifname, strerror(errno));
+		goto fail;
+	}
+	*pifname = ifname;
+	close(fd);
+	return gb.nr_mem_id;
+
+fail:
+	if (fd >= 0)
+		close(fd);
+	if (!errno)
+		errno = EINVAL;
+	return -1;
+}
+
+
+int
+nmreq_register_decode(const char **pifname, struct nmreq_register *r, struct nmctx *ctx)
+{
+	enum { P_START, P_RNGSFXOK, P_GETNUM, P_FLAGS, P_FLAGSOK, P_MEMID, P_ONESW } p_state;
+	long num;
+	const char *scan = *pifname;
+	uint32_t nr_mode;
+	uint16_t nr_mem_id;
+	uint16_t nr_ringid;
+	uint64_t nr_flags;
+
+	errno = 0;
+
+	/* fill the request */
+
+	p_state = P_START;
+	/* defaults */
+	nr_mode = NR_REG_ALL_NIC; /* default for no suffix */
+	nr_mem_id = r->nr_mem_id; /* if non-zero, further updates are disabled */
+	nr_ringid = 0;
+	nr_flags = 0;
+	while (*scan) {
+		switch (p_state) {
+		case P_START:
+			switch (*scan) {
+			case '^': /* only SW ring */
+				nr_mode = NR_REG_SW;
+				p_state = P_ONESW;
+				break;
+			case '*': /* NIC and SW */
+				nr_mode = NR_REG_NIC_SW;
+				p_state = P_RNGSFXOK;
+				break;
+			case '-': /* one NIC ring pair */
+				nr_mode = NR_REG_ONE_NIC;
+				p_state = P_GETNUM;
+				break;
+			case '/': /* start of flags */
+				p_state = P_FLAGS;
+				break;
+			case '@': /* start of memid */
+				p_state = P_MEMID;
+				break;
+			default:
+				nmctx_ferror(ctx, "unknown modifier: '%c'", *scan);
+				goto fail;
+			}
+			scan++;
+			break;
+		case P_RNGSFXOK:
+			switch (*scan) {
+			case '/':
+				p_state = P_FLAGS;
+				break;
+			case '@':
+				p_state = P_MEMID;
+				break;
+			default:
+				nmctx_ferror(ctx, "unexpected character: '%c'", *scan);
+				goto fail;
+			}
+			scan++;
+			break;
+		case P_GETNUM:
+			if (!isdigit(*scan)) {
+				nmctx_ferror(ctx, "got '%s' while expecting a number", scan);
+				goto fail;
+			}
+			num = strtol(scan, (char **)&scan, 10);
+			if (num < 0 || num >= NETMAP_RING_MASK) {
+				nmctx_ferror(ctx, "'%ld' out of range [0, %d)",
+						num, NETMAP_RING_MASK);
+				goto fail;
+			}
+			nr_ringid = num & NETMAP_RING_MASK;
+			p_state = P_RNGSFXOK;
+			break;
+		case P_FLAGS:
+		case P_FLAGSOK:
+			switch (*scan) {
+			case '@':
+				p_state = P_MEMID;
+				scan++;
+				continue;
+			case 'x':
+				nr_flags |= NR_EXCLUSIVE;
+				break;
+			case 'z':
+				nr_flags |= NR_ZCOPY_MON;
+				break;
+			case 't':
+				nr_flags |= NR_MONITOR_TX;
+				break;
+			case 'r':
+				nr_flags |= NR_MONITOR_RX;
+				break;
+			case 'R':
+				nr_flags |= NR_RX_RINGS_ONLY;
+				break;
+			case 'T':
+				nr_flags |= NR_TX_RINGS_ONLY;
+				break;
+			default:
+				nmctx_ferror(ctx, "unrecognized flag: '%c'", *scan);
+				goto fail;
+			}
+			scan++;
+			p_state = P_FLAGSOK;
+			break;
+		case P_MEMID:
+			if (!isdigit(*scan)) {
+				scan--;	/* escape to options */
+				goto out;
+			}
+			num = strtol(scan, (char **)&scan, 10);
+			if (num <= 0) {
+				nmctx_ferror(ctx, "invalid mem_id: '%ld'", num);
+				goto fail;
+			}
+			if (nr_mem_id && nr_mem_id != num) {
+				nmctx_ferror(ctx, "invalid setting of mem_id to %ld (already set to %"PRIu16")", num, nr_mem_id);
+				goto fail;
+			}
+			nr_mem_id = num;
+			p_state = P_RNGSFXOK;
+			break;
+		case P_ONESW:
+			if (!isdigit(*scan)) {
+				p_state = P_RNGSFXOK;
+			} else {
+				nr_mode = NR_REG_ONE_SW;
+				p_state = P_GETNUM;
+			}
+			break;
+		}
+	}
+	if (p_state == P_MEMID && !*scan) {
+		nmctx_ferror(ctx, "invalid empty mem_id");
+		goto fail;
+	}
+	if (p_state != P_START && p_state != P_RNGSFXOK &&
+	    p_state != P_FLAGSOK && p_state != P_MEMID && p_state != P_ONESW) {
+		nmctx_ferror(ctx, "unexpected end of request");
+		goto fail;
+	}
+out:
+	ED("flags: %s %s %s %s %s %s",
+			(nr_flags & NR_EXCLUSIVE) ? "EXCLUSIVE" : "",
+			(nr_flags & NR_ZCOPY_MON) ? "ZCOPY_MON" : "",
+			(nr_flags & NR_MONITOR_TX) ? "MONITOR_TX" : "",
+			(nr_flags & NR_MONITOR_RX) ? "MONITOR_RX" : "",
+			(nr_flags & NR_RX_RINGS_ONLY) ? "RX_RINGS_ONLY" : "",
+			(nr_flags & NR_TX_RINGS_ONLY) ? "TX_RINGS_ONLY" : "");
+	r->nr_mode = nr_mode;
+	r->nr_ringid = nr_ringid;
+	r->nr_flags = nr_flags;
+	r->nr_mem_id = nr_mem_id;
+	*pifname = scan;
+	return 0;
+
+fail:
+	if (!errno)
+		errno = EINVAL;
+	return -1;
+}
+
+
+static int
+nmreq_option_parsekeys(const char *prefix, char *body, struct nmreq_opt_parser *p,
+		struct nmreq_parse_ctx *pctx)
+{
+	char *scan;
+	char delim1;
+	struct nmreq_opt_key *k;
+
+	scan = body;
+	delim1 = *scan;
+	while (delim1 != '\0') {
+		char *key, *value;
+		char delim;
+		size_t vlen;
+
+		key = scan;
+		for ( scan++; *scan != '\0' && *scan != '=' && *scan != ','; scan++) {
+			if (*scan == '-')
+				*scan = '_';
+		}
+		delim = *scan;
+		*scan = '\0';
+		scan++;
+		for (k = p->keys; (k - p->keys) < NMREQ_OPT_MAXKEYS && k->key != NULL;
+				k++) {
+			if (!strcmp(k->key, key))
+				goto found;
+
+		}
+		nmctx_ferror(pctx->ctx, "unknown key: '%s'", key);
+		errno = EINVAL;
+		return -1;
+	found:
+		if (pctx->keys[k->id] != NULL) {
+			nmctx_ferror(pctx->ctx, "option '%s': duplicate key '%s', already set to '%s'",
+					prefix, key, pctx->keys[k->id]);
+			errno = EINVAL;
+			return -1;
+		}
+		value = scan;
+		for ( ; *scan != '\0' && *scan != ','; scan++)
+			;
+		delim1 = *scan;
+		*scan = '\0';
+		vlen = scan - value;
+		scan++;
+		if (delim == '=') {
+			pctx->keys[k->id] = (vlen ? value : NULL);
+		} else {
+			if (!(k->flags & NMREQ_OPTK_ALLOWEMPTY)) {
+				nmctx_ferror(pctx->ctx, "option '%s': missing '=value' for key '%s'",
+						prefix, key);
+				errno = EINVAL;
+				return -1;
+			}
+			pctx->keys[k->id] = key;
+		}
+	}
+	/* now check that all no-default keys have been assigned */
+	for (k = p->keys; (k - p->keys) < NMREQ_OPT_MAXKEYS && k->key != NULL; k++) {
+		if ((k->flags & NMREQ_OPTK_MUSTSET) && pctx->keys[k->id] == NULL) {
+			nmctx_ferror(pctx->ctx, "option '%s': mandatory key '%s' not assigned",
+					prefix, k->key);
+			errno = EINVAL;
+			return -1;
+		}
+	}
+	return 0;
+}
+
+
+static int
+nmreq_option_decode1(char *opt, struct nmreq_opt_parser *parsers,
+		void *token, struct nmctx *ctx)
+{
+	struct nmreq_opt_parser *p;
+	const char *prefix;
+	char *scan;
+	char delim;
+	struct nmreq_parse_ctx pctx;
+	int i;
+
+	prefix = opt;
+	/* find the delimiter */
+	for (scan = opt; *scan != '\0' && *scan != ':' && *scan != '='; scan++)
+		;
+	delim = *scan;
+	*scan = '\0';
+	scan++;
+	/* find the prefix */
+	for (p = parsers; p != NULL; p = p->next) {
+		if (!strcmp(prefix, p->prefix))
+			break;
+	}
+	if (p == NULL) {
+		nmctx_ferror(ctx, "unknown option: '%s'", prefix);
+		errno = EINVAL;
+		return -1;
+	}
+	if (p->flags & NMREQ_OPTF_DISABLED) {
+		nmctx_ferror(ctx, "option '%s' is not supported", prefix);
+		errno = EOPNOTSUPP;
+		return -1;
+	}
+	/* prepare the parse context */
+	pctx.ctx = ctx;
+	pctx.token = token;
+	for (i = 0; i < NMREQ_OPT_MAXKEYS; i++)
+		pctx.keys[i] = NULL;
+	switch (delim) {
+	case '\0':
+		/* no body */
+		if (!(p->flags & NMREQ_OPTF_ALLOWEMPTY)) {
+			nmctx_ferror(ctx, "syntax error: missing body after '%s'",
+					prefix);
+			errno = EINVAL;
+			return -1;
+		}
+		break;
+	case '=': /* the body goes to the default option key, if any */
+		if (p->default_key < 0 || p->default_key >= NMREQ_OPT_MAXKEYS) {
+			nmctx_ferror(ctx, "syntax error: '=' not valid after '%s'",
+					prefix);
+			errno = EINVAL;
+			return -1;
+		}
+		if (*scan == '\0') {
+			nmctx_ferror(ctx, "missing value for option '%s'", prefix);
+			errno = EINVAL;
+			return -1;
+		}
+		pctx.keys[p->default_key] = scan;
+		break;
+	case ':': /* parse 'key=value' strings */
+		if (nmreq_option_parsekeys(prefix, scan, p, &pctx) < 0)
+			return -1;
+		break;
+	}
+	return p->parse(&pctx);
+}
+
+int
+nmreq_options_decode(const char *opt, struct nmreq_opt_parser parsers[],
+		void *token, struct nmctx *ctx)
+{
+	const char *scan, *opt1;
+	char *w;
+	size_t len;
+	int ret;
+
+	if (*opt == '\0')
+		return 0; /* empty list, OK */
+
+	if (*opt != '@') {
+		nmctx_ferror(ctx, "option list does not start with '@'");
+		errno = EINVAL;
+		return -1;
+	}
+
+	scan = opt;
+	do {
+		scan++; /* skip the plus */
+		opt1 = scan; /* start of option */
+		/* find the end of the option */
+		for ( ; *scan != '\0' && *scan != '@'; scan++)
+			;
+		len = scan - opt1;
+		if (len == 0) {
+			nmctx_ferror(ctx, "invalid empty option");
+			errno = EINVAL;
+			return -1;
+		}
+		w = nmctx_malloc(ctx, len + 1);
+		if (w == NULL) {
+			nmctx_ferror(ctx, "out of memory");
+			errno = ENOMEM;
+			return -1;
+		}
+		memcpy(w, opt1, len);
+		w[len] = '\0';
+		ret = nmreq_option_decode1(w, parsers, token, ctx);
+		nmctx_free(ctx, w);
+		if (ret < 0)
+			return -1;
+	} while (*scan != '\0');
+
+	return 0;
+}
+
+struct nmreq_option *
+nmreq_find_option(struct nmreq_header *h, uint32_t t)
+{
+	struct nmreq_option *o = NULL;
+
+	nmreq_foreach_option(h, o) {
+		if (o->nro_reqtype == t)
+			break;
+	}
+	return o;
+}
+
+void
+nmreq_remove_option(struct nmreq_header *h, struct nmreq_option *o)
+{
+	struct nmreq_option **nmo;
+
+	for (nmo = (struct nmreq_option **)&h->nr_options; *nmo != NULL;
+	    nmo = (struct nmreq_option **)&(*nmo)->nro_next) {
+		if (*nmo == o) {
+			*((uint64_t *)nmo) = o->nro_next;
+			o->nro_next = (uint64_t)(uintptr_t)NULL;
+			break;
+		}
+	}
+}
+
+void
+nmreq_free_options(struct nmreq_header *h)
+{
+	struct nmreq_option *o, *next;
+
+	/*
+	 * Note: can't use nmreq_foreach_option() here; it frees the
+	 * list as it's walking and nmreq_foreach_option() isn't
+	 * modification-safe.
+	 */
+	for (o = (struct nmreq_option *)(uintptr_t)h->nr_options; o != NULL;
+	    o = next) {
+		next = (struct nmreq_option *)(uintptr_t)o->nro_next;
+		free(o);
+	}
+}
+
+const char*
+nmreq_option_name(uint32_t nro_reqtype)
+{
+	switch (nro_reqtype) {
+	case NETMAP_REQ_OPT_EXTMEM:
+		return "extmem";
+	case NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS:
+		return "sync-kloop-eventfds";
+	case NETMAP_REQ_OPT_CSB:
+		return "csb";
+	case NETMAP_REQ_OPT_SYNC_KLOOP_MODE:
+		return "sync-kloop-mode";
+	case NETMAP_REQ_OPT_OFFSETS:
+		return "offsets";
+	default:
+		return "unknown";
+	}
+}
+
+#if 0
+#include 
+static void
+nmreq_dump(struct nmport_d *d)
+{
+	printf("header:\n");
+	printf("   nr_version:  %"PRIu16"\n", d->hdr.nr_version);
+	printf("   nr_reqtype:  %"PRIu16"\n", d->hdr.nr_reqtype);
+	printf("   nr_reserved: %"PRIu32"\n", d->hdr.nr_reserved);
+	printf("   nr_name:     %s\n", d->hdr.nr_name);
+	printf("   nr_options:  %lx\n", (unsigned long)d->hdr.nr_options);
+	printf("   nr_body:     %lx\n", (unsigned long)d->hdr.nr_body);
+	printf("\n");
+	printf("register (%p):\n", (void *)d->hdr.nr_body);
+	printf("   nr_mem_id:   %"PRIu16"\n", d->reg.nr_mem_id);
+	printf("   nr_ringid:   %"PRIu16"\n", d->reg.nr_ringid);
+	printf("   nr_mode:     %lx\n", (unsigned long)d->reg.nr_mode);
+	printf("   nr_flags:    %lx\n", (unsigned long)d->reg.nr_flags);
+	printf("\n");
+	if (d->hdr.nr_options) {
+		struct nmreq_opt_extmem *e = (struct nmreq_opt_extmem *)d->hdr.nr_options;
+		printf("opt_extmem (%p):\n", e);
+		printf("   nro_opt.nro_next:    %lx\n", (unsigned long)e->nro_opt.nro_next);
+		printf("   nro_opt.nro_reqtype: %"PRIu32"\n", e->nro_opt.nro_reqtype);
+		printf("   nro_usrptr:          %lx\n", (unsigned long)e->nro_usrptr);
+		printf("   nro_info.nr_memsize  %"PRIu64"\n", e->nro_info.nr_memsize);
+	}
+	printf("\n");
+	printf("mem (%p):\n", d->mem);
+	printf("   refcount:   %d\n", d->mem->refcount);
+	printf("   mem:        %p\n", d->mem->mem);
+	printf("   size:       %zu\n", d->mem->size);
+	printf("\n");
+	printf("rings:\n");
+	printf("   tx:   [%d, %d]\n", d->first_tx_ring, d->last_tx_ring);
+	printf("   rx:   [%d, %d]\n", d->first_rx_ring, d->last_rx_ring);
+}
+int
+main(int argc, char *argv[])
+{
+	struct nmport_d *d;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s netmap-expr\n", argv[0]);
+		return 1;
+	}
+
+	d = nmport_open(argv[1]);
+	if (d != NULL) {
+		nmreq_dump(d);
+		nmport_close(d);
+	}
+
+	return 0;
+}
+#endif
diff --git a/pre-commit b/pre-commit
new file mode 100755
index 000000000..6e9b08561
--- /dev/null
+++ b/pre-commit
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+if git rev-parse --verify HEAD >/dev/null 2>&1
+then
+	against=HEAD
+else
+	# Initial commit: diff against an empty tree object
+	against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+fi
+
+# If you want to allow non-ASCII filenames set this variable to true.
+allownonascii=$(git config --bool hooks.allownonascii)
+
+# Redirect output to stderr.
+exec 1>&2
+
+# Cross platform projects tend to avoid non-ASCII filenames; prevent
+# them from being added to the repository. We exploit the fact that the
+# printable range starts at the space character and ends with tilde.
+if [ "$allownonascii" != "true" ] &&
+	# Note that the use of brackets around a tr range is ok here, (it's
+	# even required, for portability to Solaris 10's /usr/bin/tr), since
+	# the square bracket bytes happen to fall in the designated range.
+	test $(git diff --cached --name-only --diff-filter=A -z $against |
+	  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
+then
+	cat <<\EOF
+Error: Attempt to add a non-ASCII file name.
+
+This can cause problems if you want to work with people on other platforms.
+
+To be portable it is advisable to rename the file.
+
+If you know what you are doing you can disable this check using:
+
+  git config hooks.allownonascii true
+EOF
+	exit 1
+fi
+
+########### netmap specific checks ############
+files="sys/dev/netmap/netmap_vale.c sys/dev/netmap/netmap_monitor.c sys/dev/netmap/netmap_pipe.c sys/dev/netmap/netmap.c sys/dev/netmap/netmap_generic.c sys/dev/netmap/netmap_freebsd.c sys/dev/netmap/netmap_legacy.c LINUX/netmap_linux.c LINUX/bsd_glue.h LINUX/i40e_netmap_linux.h LINUX/if_e1000_netmap.h LINUX/if_e1000e_netmap.h LINUX/if_igb_netmap.h LINUX/if_re_netmap_linux.h LINUX/ixgbe_netmap_linux.h LINUX/veth_netmap.h LINUX/virtio_netmap.h LINUX/netmap_ptnet.c LINUX/forcedeth_netmap.h apps/bridge/bridge.c apps/lb/lb.c apps/vale-ctl/vale-ctl.c apps/dedup/dedup.c apps/include/ctrs.h"
+
+for f in $files; do
+	ERR=$(git grep --line-number "^\(     \)\+" $f | head -n1)
+	if [ "$ERR" != "" ]; then
+		echo "Wrong indentation in $f"
+		echo "$ERR"
+		exit 1
+	fi
+done
+
+for f in $files; do
+	ERR=$(git grep --line-number " \+$" $f | head -n1)
+	if [ "$ERR" != "" ]; then
+		echo "Trailing whitespaces in $f"
+		echo "$ERR"
+		exit 1
+	fi
+done
+
+for f in $files; do
+	ERR=$(git grep --line-number "^[$(printf '\t') ]\+$" $f | head -n1)
+	if [ "$ERR" != "" ]; then
+		echo "Line with only spaces or tabs in $f"
+		echo "$ERR"
+		exit 1
+	fi
+done
+########### end of netmap specific checks ############
+
+# If there are whitespace errors, print the offending file names and fail.
+exec git diff-index --check --cached $against ":(exclude)LINUX/final-patches"
diff --git a/private/.gitattributes b/private/.gitattributes
deleted file mode 100644
index 43d156222..000000000
--- a/private/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-* merge=ignore -diff
diff --git a/share/man/man4/netmap.4 b/share/man/man4/netmap.4
index 726180800..3efa34c29 100644
--- a/share/man/man4/netmap.4
+++ b/share/man/man4/netmap.4
@@ -25,20 +25,12 @@
 .\" This document is derived in part from the enet man page (enet.4)
 .\" distributed with 4.3BSD Unix.
 .\"
-.\" $FreeBSD$
-.\"
-.Dd December 14, 2015
+.Dd October 3, 2020
 .Dt NETMAP 4
 .Os
 .Sh NAME
 .Nm netmap
 .Nd a framework for fast packet I/O
-.br
-.Nm VALE
-.Nd a fast VirtuAl Local Ethernet using the netmap API
-.br
-.Nm netmap pipes
-.Nd a shared memory packet transport channel
 .Sh SYNOPSIS
 .Cd device netmap
 .Sh DESCRIPTION
@@ -46,7 +38,7 @@
 is a framework for extremely fast and efficient packet I/O
 for userspace and kernel clients, and for Virtual Machines.
 It runs on
-.Fx
+.Fx ,
 Linux and some versions of Windows, and supports a variety of
 .Nm netmap ports ,
 including
@@ -61,7 +53,7 @@ implementing a very fast and modular in-kernel software switch/dataplane;
 a shared memory packet transport channel;
 .It Nm netmap monitors
 a mechanism similar to
-.Xr bpf
+.Xr bpf 4
 to capture traffic
 .El
 .Pp
@@ -80,12 +72,12 @@ with much less than one core on 10 Gbit/s NICs;
 35-40 Mpps on 40 Gbit/s NICs (limited by the hardware);
 about 20 Mpps per core for VALE ports;
 and over 100 Mpps for
-.Nm netmap pipes.
+.Nm netmap pipes .
 NICs without native
 .Nm
 support can still use the API in emulated mode,
 which uses unmodified device drivers and is 3-5 times faster than
-.Xr bpf
+.Xr bpf 4
 or raw sockets.
 .Pp
 Userspace clients can dynamically switch NICs into
@@ -109,9 +101,9 @@ synchronization and blocking I/O through a file descriptor
 and standard OS mechanisms such as
 .Xr select 2 ,
 .Xr poll 2 ,
-.Xr epoll 2 ,
+.Xr kqueue 2
 and
-.Xr kqueue 2 .
+.Xr epoll 7 .
 All types of
 .Nm netmap ports
 and the
@@ -175,8 +167,9 @@ ports (including
 and
 .Nm netmap pipe
 ports).
-Simpler, higher level functions are described in section
-.Xr LIBRARIES .
+Simpler, higher level functions are described in the
+.Sx LIBRARIES
+section.
 .Pp
 Ports and rings are created and controlled through a file descriptor,
 created by opening a special device
@@ -191,14 +184,14 @@ argument.
 .Va arg.nr_name
 specifies the netmap port name, as follows:
 .Bl -tag -width XXXX
-.It Dv OS network interface name (e.g. 'em0', 'eth1', ... )
+.It Dv OS network interface name (e.g., 'em0', 'eth1', ... )
 the data path of the NIC is disconnected from the host stack,
 and the file descriptor is bound to the NIC (one or all queues),
 or to the host stack;
 .It Dv valeSSS:PPP
 the file descriptor is bound to port PPP of VALE switch SSS.
 Switch instances and ports are dynamically created if necessary.
-.br
+.Pp
 Both SSS and PPP have the form [0-9a-zA-Z_]+ , the string
 cannot exceed IFNAMSIZ characters, and PPP cannot
 be the name of any existing OS network interface.
@@ -218,12 +211,6 @@ Non-blocking I/O is done with special
 and
 .Xr poll 2
 on the file descriptor permit blocking I/O.
-.Xr epoll 2
-and
-.Xr kqueue 2
-are not supported on
-.Nm
-file descriptors.
 .Pp
 While a NIC is in
 .Nm
@@ -244,7 +231,7 @@ which is the ultimate reference for the
 API.
 The main structures and fields are indicated below:
 .Bl -tag -width XXX
-.It Dv struct netmap_if (one per interface)
+.It Dv struct netmap_if (one per interface )
 .Bd -literal
 struct netmap_if {
     ...
@@ -267,14 +254,30 @@ NICs also have an extra tx/rx ring pair connected to the host stack.
 .Em NIOCREGIF
 can also request additional unbound buffers in the same memory space,
 to be used as temporary storage for packets.
+The number of extra
+buffers is specified in the
+.Va arg.nr_arg3
+field.
+On success, the kernel writes back to
+.Va arg.nr_arg3
+the number of extra buffers actually allocated (they may be less
+than the amount requested if the memory space ran out of buffers).
 .Pa ni_bufs_head
-contains the index of the first of these free rings,
+contains the index of the first of these extra buffers,
 which are connected in a list (the first uint32_t of each
 buffer being the index of the next buffer in the list).
 A
 .Dv 0
 indicates the end of the list.
-.It Dv struct netmap_ring (one per ring)
+The application is free to modify
+this list and use the buffers (i.e., binding them to the slots of a
+netmap ring).
+When closing the netmap file descriptor,
+the kernel frees the buffers contained in the list pointed by
+.Pa ni_bufs_head
+, irrespectively of the buffers originally provided by the kernel on
+.Em NIOCREGIF .
+.It Dv struct netmap_ring (one per ring )
 .Bd -literal
 struct netmap_ring {
     ...
@@ -296,7 +299,7 @@ Implements transmit and receive rings, with read/write
 pointers, metadata and an array of
 .Em slots
 describing the buffers.
-.It Dv struct netmap_slot (one per buffer)
+.It Dv struct netmap_slot (one per buffer )
 .Bd -literal
 struct netmap_slot {
     uint32_t buf_idx;           /* buffer index                 */
@@ -340,14 +343,14 @@ should not be assumed to be a power of two.
 .Pp
 .Va head
 is the first slot available to userspace;
-.br
+.Pp
 .Va cur
 is the wakeup point:
 select/poll will unblock when
 .Va tail
 passes
 .Va cur ;
-.br
+.Pp
 .Va tail
 is the first slot reserved to the kernel.
 .Pp
@@ -371,7 +374,6 @@ during the execution of a netmap-related system call.
 The only exception are slots (and buffers) in the range
 .Va tail\  . . . head-1 ,
 that are explicitly assigned to the kernel.
-.Pp
 .Ss TRANSMIT RINGS
 On transmit rings, after a
 .Nm
@@ -419,7 +421,7 @@ Below is an example of the evolution of a TX ring:
 .Fn select
 and
 .Fn poll
-will block if there is no space in the ring, i.e.
+will block if there is no space in the ring, i.e.,
 .Dl ring->cur == ring->tail
 and return when new slots have become available.
 .Pp
@@ -453,7 +455,7 @@ slots up to
 are returned to the kernel for further receives, and
 .Va tail
 may advance to report new incoming packets.
-.br
+.Pp
 Below is an example of the evolution of an RX ring:
 .Bd -literal
     after the syscall, there are some (h)eld and some (R)eceived slots
@@ -499,8 +501,8 @@ This flag helps detect
 when packets have been sent and a file descriptor can be closed.
 .It NS_FORWARD
 When a ring is in 'transparent' mode,
-packets marked with this flag are forwarded to the other endpoint
-at the next system call, thus restoring (in a selective way)
+packets marked with this flag by the user application are forwarded to the
+other endpoint at the next system call, thus restoring (in a selective way)
 the connection between a NIC and the host stack.
 .It NS_NO_LEARN
 tells the forwarding code that the source MAC address for this
@@ -509,7 +511,7 @@ packet must not be used in the learning bridge code.
 indicates that the packet's payload is in a user-supplied buffer
 whose user virtual address is in the 'ptr' field of the slot.
 The size can reach 65535 bytes.
-.br
+.Pp
 This is only supported on the transmit ring of
 .Nm VALE
 ports, and it helps reducing data copies in the interconnection
@@ -591,8 +593,8 @@ indicate the size of transmit and receive rings.
 indicate the number of transmit
 and receive rings.
 Both ring number and sizes may be configured at runtime
-using interface-specific functions (e.g.
-.Xr ethtool
+using interface-specific functions (e.g.,
+.Xr ethtool 8
 ).
 .El
 .It Dv NIOCREGIF
@@ -610,7 +612,7 @@ The recommended way to bind a file descriptor to a port is
 to use function
 .Va nm_open(..)
 (see
-.Xr LIBRARIES )
+.Sx LIBRARIES )
 which parses names to access specific port types and
 enable features.
 In the following we document the main features.
@@ -651,7 +653,7 @@ In the example below, "netmap:foo" is any valid netmap port name.
 (default) all hardware ring pairs
 .It NR_REG_SW            "netmap:foo^"
 the ``host rings'', connecting to the host stack.
-.It NR_REG_NIC_SW        "netmap:foo+"
+.It NR_REG_NIC_SW        "netmap:foo*"
 all hardware rings and the host rings
 .It NR_REG_ONE_NIC       "netmap:foo-i"
 only the i-th hardware ring pair, where the number is in
@@ -668,7 +670,7 @@ and does not need to be sequential.
 On return the pipe
 will only have a single ring pair with index 0,
 irrespective of the value of
-.Va i.
+.Va i .
 .El
 .Pp
 By default, a
@@ -680,14 +682,17 @@ no write events are specified.
 The feature can be disabled by or-ing
 .Va NETMAP_NO_TX_POLL
 to the value written to
-.Va nr_ringid.
+.Va nr_ringid .
 When this feature is used,
 packets are transmitted only on
 .Va ioctl(NIOCTXSYNC)
-or select()/poll() are called with a write event (POLLOUT/wfdset) or a full ring.
+or
+.Va select() /
+.Va poll()
+are called with a write event (POLLOUT/wfdset) or a full ring.
 .Pp
 When registering a virtual interface that is dynamically created to a
-.Xr vale 4
+.Nm VALE
 switch, we can specify the desired number of rings (1 by default,
 and currently up to 16) on it using nr_tx_rings and nr_rx_rings fields.
 .It Dv NIOCTXSYNC
@@ -697,7 +702,7 @@ number of slots available for transmission.
 tells the hardware of consumed packets, and asks for newly available
 packets.
 .El
-.Sh SELECT, POLL, EPOLL, KQUEUE.
+.Sh SELECT, POLL, EPOLL, KQUEUE
 .Xr select 2
 and
 .Xr poll 2
@@ -711,7 +716,7 @@ respectively when write (POLLOUT) and read (POLLIN) events are requested.
 Both block if no slots are available in the ring
 .Va ( ring->cur == ring->tail ) .
 Depending on the platform,
-.Xr epoll 2
+.Xr epoll 7
 and
 .Xr kqueue 2
 are supported too.
@@ -730,7 +735,10 @@ Passing the
 .Dv NETMAP_DO_RX_POLL
 flag to
 .Em NIOCREGIF updates receive rings even without read events.
-Note that on epoll and kqueue,
+Note that on
+.Xr epoll 7
+and
+.Xr kqueue 2 ,
 .Dv NETMAP_NO_TX_POLL
 and
 .Dv NETMAP_DO_RX_POLL
@@ -758,9 +766,9 @@ before
 .Pp
 The following functions are available:
 .Bl -tag -width XXXXX
-.It Va  struct nm_desc * nm_open(const char *ifname, const struct nmreq *req, uint64_t flags, const struct nm_desc *arg)
+.It Va  struct nm_desc * nm_open(const char *ifname, const struct nmreq *req, uint64_t flags, const struct nm_desc *arg )
 similar to
-.Xr pcap_open ,
+.Xr pcap_open_live 3 ,
 binds a file descriptor to a port.
 .Bl -tag -width XX
 .It Va ifname
@@ -773,7 +781,7 @@ The nm_flags and nm_ringid values are overwritten by parsing
 ifname and flags, and other fields can be overridden through
 the other two arguments.
 .It Va arg
-points to a struct nm_desc containing arguments (e.g. from a previously
+points to a struct nm_desc containing arguments (e.g., from a previously
 open file descriptor) that should override the defaults.
 The fields are used as described below
 .It Va flags
@@ -781,44 +789,50 @@ can be set to a combination of the following flags:
 .Va NETMAP_NO_TX_POLL ,
 .Va NETMAP_DO_RX_POLL
 (copied into nr_ringid);
-.Va NM_OPEN_NO_MMAP (if arg points to the same memory region,
+.Va NM_OPEN_NO_MMAP
+(if arg points to the same memory region,
 avoids the mmap and uses the values from it);
-.Va NM_OPEN_IFNAME (ignores ifname and uses the values in arg);
+.Va NM_OPEN_IFNAME
+(ignores ifname and uses the values in arg);
 .Va NM_OPEN_ARG1 ,
 .Va NM_OPEN_ARG2 ,
-.Va NM_OPEN_ARG3 (uses the fields from arg);
-.Va NM_OPEN_RING_CFG (uses the ring number and sizes from arg).
+.Va NM_OPEN_ARG3
+(uses the fields from arg);
+.Va NM_OPEN_RING_CFG
+(uses the ring number and sizes from arg).
 .El
-.It Va int nm_close(struct nm_desc *d)
+.It Va int nm_close(struct nm_desc *d )
 closes the file descriptor, unmaps memory, frees resources.
-.It Va int nm_inject(struct nm_desc *d, const void *buf, size_t size)
-similar to pcap_inject(), pushes a packet to a ring, returns the size
+.It Va int nm_inject(struct nm_desc *d, const void *buf, size_t size )
+similar to
+.Va pcap_inject() ,
+pushes a packet to a ring, returns the size
 of the packet is successful, or 0 on error;
-.It Va int nm_dispatch(struct nm_desc *d, int cnt, nm_cb_t cb, u_char *arg)
-similar to pcap_dispatch(), applies a callback to incoming packets
-.It Va u_char * nm_nextpkt(struct nm_desc *d, struct nm_pkthdr *hdr)
-similar to pcap_next(), fetches the next packet
+.It Va int nm_dispatch(struct nm_desc *d, int cnt, nm_cb_t cb, u_char *arg )
+similar to
+.Va pcap_dispatch() ,
+applies a callback to incoming packets
+.It Va u_char * nm_nextpkt(struct nm_desc *d, struct nm_pkthdr *hdr )
+similar to
+.Va pcap_next() ,
+fetches the next packet
 .El
 .Sh SUPPORTED DEVICES
 .Nm
 natively supports the following devices:
 .Pp
-On FreeBSD:
+On
+.Fx :
 .Xr cxgbe 4 ,
 .Xr em 4 ,
-.Xr igb 4 ,
+.Xr iflib 4
+.Pq providing Xr igb 4 and Xr em 4 ,
 .Xr ixgbe 4 ,
 .Xr ixl 4 ,
-.Xr lem 4 ,
-.Xr re 4 .
-.Pp
-On Linux
-.Xr e1000 4 ,
-.Xr e1000e 4 ,
-.Xr i40e 4 ,
-.Xr igb 4 ,
-.Xr ixgbe 4 ,
-.Xr r8169 4 .
+.Xr re 4 ,
+.Xr vtnet 4 .
+.Pp
+On Linux e1000, e1000e, i40e, igb, ixgbe, ixgbevf, r8169, virtio_net, vmxnet3.
 .Pp
 NICs without native support can still be used in
 .Nm
@@ -829,10 +843,11 @@ mode but still significantly higher than various raw socket types
 Note that for slow devices (such as 1 Gbit/s and slower NICs,
 or several 10 Gbit/s NICs whose hardware is unable to sustain line rate),
 emulated and native mode will likely have similar or same throughput.
-.br
+.Pp
 When emulation is in use, packet sniffer programs such as tcpdump
-could see received packets before they are diverted by netmap. This behaviour
-is not intentional, being just an artifact of the implementation of emulation.
+could see received packets before they are diverted by netmap.
+This behaviour is not intentional, being just an artifact of the implementation
+of emulation.
 Note that in case the netmap application subsequently moves packets received
 from the emulated adapter onto the host RX ring, the sniffer will intercept
 those packets again, since the packets are injected to the host stack as they
@@ -844,34 +859,38 @@ The sysctl variable
 .Va dev.netmap.admode
 globally controls how netmap mode is implemented.
 .Sh SYSCTL VARIABLES AND MODULE PARAMETERS
-Some aspect of the operation of
+Some aspects of the operation of
 .Nm
-are controlled through sysctl variables on FreeBSD
+and
+.Nm VALE
+are controlled through sysctl variables on
+.Fx
 .Em ( dev.netmap.* )
 and module parameters on Linux
-.Em ( /sys/module/netmap_lin/parameters/* ) :
+.Em ( /sys/module/netmap/parameters/* ) :
 .Bl -tag -width indent
 .It Va dev.netmap.admode: 0
 Controls the use of native or emulated adapter mode.
-.br
+.Pp
 0 uses the best available option;
-.br
+.Pp
 1 forces native mode and fails if not available;
-.br
+.Pp
 2 forces emulated hence never fails.
+.It Va dev.netmap.generic_rings: 1
+Number of rings used for emulated netmap mode
 .It Va dev.netmap.generic_ringsize: 1024
 Ring size used for emulated netmap mode
 .It Va dev.netmap.generic_mit: 100000
 Controls interrupt moderation for emulated mode
-.It Va dev.netmap.mmap_unreg: 0
 .It Va dev.netmap.fwd: 0
 Forces NS_FORWARD mode
-.It Va dev.netmap.flags: 0
 .It Va dev.netmap.txsync_retry: 2
+Number of txsync loops in the
+.Nm VALE
+flush function
 .It Va dev.netmap.no_pendintr: 1
 Forces recovery of transmit buffers on system calls
-.It Va dev.netmap.mitigate: 1
-Propagates interrupt mitigation to user processes
 .It Va dev.netmap.no_timestamp: 0
 Disables the update of the timestamp in the netmap ring
 .It Va dev.netmap.verbose: 0
@@ -894,19 +913,38 @@ as it impacts the total amount of memory used by netmap.
 .It Va dev.netmap.if_curr_num: 0
 .It Va dev.netmap.if_curr_size: 0
 Actual values in use.
+.It Va dev.netmap.priv_buf_num: 4098
+.It Va dev.netmap.priv_buf_size: 2048
+.It Va dev.netmap.priv_ring_num: 4
+.It Va dev.netmap.priv_ring_size: 20480
+.It Va dev.netmap.priv_if_num: 2
+.It Va dev.netmap.priv_if_size: 1024
+Sizes and number of objects (netmap_if, netmap_ring, buffers)
+for private memory regions.
+A separate memory region is used for each
+.Nm VALE
+port and each pair of
+.Nm netmap pipes .
 .It Va dev.netmap.bridge_batch: 1024
 Batch size used when moving packets across a
 .Nm VALE
 switch.
 Values above 64 generally guarantee good
 performance.
+.It Va dev.netmap.max_bridges: 8
+Max number of
+.Nm VALE
+switches that can be created. This tunable can be specified
+at loader time.
+.It Va dev.netmap.ptnet_vnet_hdr: 1
+Allow ptnet devices to use virtio-net headers
 .El
 .Sh SYSTEM CALLS
 .Nm
 uses
 .Xr select 2 ,
 .Xr poll 2 ,
-.Xr epoll 2
+.Xr epoll 7
 and
 .Xr kqueue 2
 to wake up processes when significant events occur, and
@@ -938,7 +976,7 @@ directory in
 .Fx
 distributions.
 .Pp
-.Xr pkt-gen
+.Xr pkt-gen 8
 is a general purpose traffic source/sink.
 .Pp
 As an example
@@ -949,21 +987,21 @@ is a traffic sink.
 Both print traffic statistics, to help monitor
 how the system performs.
 .Pp
-.Xr pkt-gen
+.Xr pkt-gen 8
 has many options can be uses to set packet sizes, addresses,
 rates, and use multiple send/receive threads and cores.
 .Pp
-.Xr bridge
+.Xr bridge 4
 is another test program which interconnects two
 .Nm
 ports.
-It can be used for zero-copy forwarding between
+It can be used for transparent forwarding between
 interfaces, as in
-.Dl bridge -i ix0 -i ix1
+.Dl bridge -i netmap:ix0 -i netmap:ix1
 or even connect the NIC to the host stack using netmap
-.Dl bridge -i ix0 -i ix0
+.Dl bridge -i netmap:ix0
 .Ss USING THE NATIVE API
-The following code implements a traffic generator
+The following code implements a traffic generator:
 .Pp
 .Bd -literal -compact
 #include 
@@ -998,7 +1036,8 @@ void sender(void)
 }
 .Ed
 .Ss HELPER FUNCTIONS
-A simple receiver can be implemented using the helper functions
+A simple receiver can be implemented using the helper functions:
+.Pp
 .Bd -literal -compact
 #define NETMAP_WITH_LIBS
 #include 
@@ -1016,7 +1055,7 @@ void receiver(void)
     for (;;) {
 	poll(&fds, 1, -1);
         while ( (buf = nm_nextpkt(d, &h)) )
-	    consume_pkt(buf, h->len);
+	    consume_pkt(buf, h.len);
     }
     nm_close(d);
 }
@@ -1027,6 +1066,7 @@ it is possible to do packet forwarding between ports
 swapping buffers.
 The buffer from the transmit ring is used
 to replenish the receive ring:
+.Pp
 .Bd -literal -compact
     uint32_t tmp;
     struct netmap_slot *src, *dst;
@@ -1045,7 +1085,7 @@ to replenish the receive ring:
 .Ed
 .Ss ACCESSING THE HOST STACK
 The host stack is for all practical purposes just a regular ring pair,
-which you can access with the netmap API (e.g. with
+which you can access with the netmap API (e.g., with
 .Dl nm_open("netmap:eth0^", ... ) ;
 All packets that the host would send to an interface in
 .Nm
@@ -1055,22 +1095,29 @@ TX ring are send up to the host stack.
 A simple way to test the performance of a
 .Nm VALE
 switch is to attach a sender and a receiver to it,
-e.g. running the following in two different terminals:
+e.g., running the following in two different terminals:
 .Dl pkt-gen -i vale1:a -f rx # receiver
 .Dl pkt-gen -i vale1:b -f tx # sender
 The same example can be used to test netmap pipes, by simply
-changing port names, e.g.
+changing port names, e.g.,
 .Dl pkt-gen -i vale2:x{3 -f rx # receiver on the master side
 .Dl pkt-gen -i vale2:x}3 -f tx # sender on the slave side
 .Pp
 The following command attaches an interface and the host stack
 to a switch:
-.Dl vale-ctl -h vale2:em0
+.Dl valectl -h vale2:em0
 Other
 .Nm
 clients attached to the same switch can now communicate
 with the network card or the host.
 .Sh SEE ALSO
+.Xr vale 4 ,
+.Xr bridge 8 ,
+.Xr valectl 8 ,
+.Xr lb 8 ,
+.Xr nmreplay 8 ,
+.Xr pkt-gen 8
+.Pp
 .Pa http://info.iet.unipi.it/~luigi/netmap/
 .Pp
 Luigi Rizzo, Revisiting network I/O APIs: the netmap framework,
@@ -1127,7 +1174,7 @@ multiqueue, schedulers, packet filters.
 Multiple transmit and receive rings are supported natively
 and can be configured with ordinary OS tools,
 such as
-.Xr ethtool
+.Xr ethtool 8
 or
 device-specific sysctl variables.
 The same goes for Receive Packet Steering (RPS)
diff --git a/share/man/man4/ptnet.4 b/share/man/man4/ptnet.4
new file mode 100644
index 000000000..71efb3cdd
--- /dev/null
+++ b/share/man/man4/ptnet.4
@@ -0,0 +1,138 @@
+.\" Copyright (c) 2018 Vincenzo Maffione
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd December 11, 2018
+.Dt PTNET 4
+.Os
+.Sh NAME
+.Nm ptnet
+.Nd Ethernet driver for passed-through netmap ports
+.Sh SYNOPSIS
+This network driver is included in
+.Xr netmap 4 ,
+and it can be compiled into the kernel by adding the following
+line in your kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device netmap"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+device driver provides direct access to host netmap ports,
+from within a Virtual Machine (VM).
+Applications running inside
+the VM can access the TX/RX rings and buffers of a netmap port
+that the hypervisor has passed-through to the VM.
+Hypervisor support for
+.Nm
+is currently available for QEMU/KVM.
+Any
+.Xr netmap 4
+port can be passed-through, including physical NICs,
+.Xr vale 4
+ports, netmap pipes, etc.
+.Pp
+The main use-case for netmap passthrough is Network Function
+Virtualization (NFV), where middlebox applications running within
+VMs may want to process very high packet rates (e.g., 1-10 millions
+packets per second or more).
+Note, however, that those applications
+must use the device in netmap mode in order to achieve such rates.
+In addition to the general advantages of netmap, the improved
+performance of
+.Nm
+when compared to hypervisor device emulation or paravirtualization (e.g.,
+.Xr vtnet 4 ,
+.Xr vmx 4 )
+comes from the hypervisor being completely bypassed in the data-path.
+For example, when using
+.Xr vtnet 4
+the VM has to convert each
+.Xr mbuf 9
+to a VirtIO-specific packet representation
+and publish that to a VirtIO queue; on the hypervisor side, the
+packet is extracted from the VirtIO queue and converted to a
+hypervisor-specific packet representation.
+The overhead of format conversions (and packet copies, in same cases) is not
+incured by
+.Nm
+in netmap mode, because mbufs are not used at all, and the packet format
+is the one defined by netmap (e.g.,
+.Ar struct netmap_slot )
+along the whole data-path.
+No format conversions or copies happen.
+.Pp
+It is also possible to use a
+.Nm
+device like a regular network interface, which interacts with the
+.Fx
+network stack (i.e., not in netmap mode).
+However, in that case it is necessary to pay the cost of data copies
+between mbufs and netmap buffers, which generally results in lower
+TCP/UDP performance than
+.Xr vtnet 4
+or other paravirtualized network devices.
+If the passed-through netmap port supports the VirtIO network header,
+.Nm
+is able to use it, and support TCP/UDP checksum offload (for both transmit
+and receive), TCP segmentation offload (TSO) and TCP large receive offload
+(LRO).
+Currently,
+.Xr vale 4
+ports support the header.
+Note that the VirtIO network header is generally not used in NFV
+use-cases, because middleboxes are not endpoints of TCP/UDP connections.
+.Sh TUNABLES
+Tunables can be set at the
+.Xr loader 8
+prompt before booting the kernel or stored in
+.Xr loader.conf 5 .
+.Bl -tag -width "xxxxxx"
+.It Va dev.netmap.ptnet_vnet_hdr
+This tunable enables (1) or disables (0) the VirtIO network header.
+If enabled,
+.Nm
+uses the same header used by
+.Xr vtnet 4
+to exchange offload metadata with the hypervisor.
+If disabled, no header is prepended to transmitted and received
+packets.
+The metadata is necessary to support TCP/UDP checksum offloads,
+TSO, and LRO.
+The default value is 1.
+.El
+.Sh SEE ALSO
+.Xr netintro 4 ,
+.Xr netmap 4 ,
+.Xr vale 4 ,
+.Xr virtio 4 ,
+.Xr vmx 4 ,
+.Xr ifconfig 8
+.Sh HISTORY
+The
+.Nm
+driver was written by
+.An Vincenzo Maffione Aq Mt vmaffione@FreeBSD.org .
+It first appeared in
+.Fx 12.0 .
diff --git a/share/man/man4/vale.4 b/share/man/man4/vale.4
new file mode 100644
index 000000000..85204de68
--- /dev/null
+++ b/share/man/man4/vale.4
@@ -0,0 +1,121 @@
+.\" Copyright (c) 2012 Luigi Rizzo, Universita` di Pisa
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" This document is derived in part from the enet man page (enet.4)
+.\" distributed with 4.3BSD Unix.
+.\"
+.\" $Id: $
+.\"
+.Dd February 6, 2020
+.Dt VALE 4
+.Os
+.Sh NAME
+.Nm vale
+.Nd a very fast Virtual Local Ethernet using the netmap API
+.Sh SYNOPSIS
+.Cd device netmap
+.Sh DESCRIPTION
+.Nm
+is a feature of the
+.Xr netmap 4
+module that implements multiple Virtual switches that can
+be used to interconnect netmap clients, including traffic
+sources and sinks, packet forwarders, userspace firewalls,
+and so on.
+.Pp
+.Nm
+is implemented completely in software, and is extremely fast.
+On a modern machine it can move almost 20 Million packets per
+second (Mpps) per core with small frames, and about 70 Gbit/s
+with 1500 byte frames.
+.Sh OPERATION
+.Nm
+dynamically creates switches and ports as clients connect
+to it using the
+.Xr netmap 4
+API.
+.Pp
+.Nm
+ports are named
+.Pa valeSSS:PPP
+where
+.Pa vale
+is the prefix indicating a VALE switch rather than a standard interface,
+.Pa SSS
+indicates a specific switch (the colon is a separator),
+and
+.Pa PPP
+indicates a port within the switch.
+Both SSS and PPP have the form [0-9a-zA-Z_]+ , the string cannot
+exceed IFNAMSIZ characters, and PPP cannot be the name of any
+existing OS network interface.
+.Pp
+See
+.Xr netmap 4
+for details on the API.
+.Ss LIMITS
+.Nm
+currently supports up to 254 ports per switch. The maximum
+number of switches defaults to 8 but can be changed by
+means of the max_bridges sysctl variable.
+.Sh SYSCTL VARIABLES
+See
+.Xr netmap 4
+for a list of sysctl variables that affect
+.Nm
+bridges.
+.Sh EXAMPLES
+Create one switch, with a traffic generator connected to one
+port, and a netmap-enabled tcpdump instance on another port:
+.Bd -literal -offset indent
+tcpdump -ni valea:1 &
+pkt-gen  -i valea:0 -f tx &
+.Ed
+.Pp
+Create two switches,
+each connected to two qemu machines on different ports.
+.Bd -literal -offset indent
+qemu -net nic -net netmap,ifname=vale1:a ... &
+qemu -net nic -net netmap,ifname=vale1:b ... &
+qemu -net nic -net netmap,ifname=vale2:c ... &
+qemu -net nic -net netmap,ifname=vale2:d ... &
+.Ed
+.Sh SEE ALSO
+.Xr netmap 4
+.Pp
+Luigi Rizzo, Giuseppe Lettieri: VALE, a switched ethernet for virtual machines,
+June 2012, http://info.iet.unipi.it/~luigi/vale/
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+switch was designed and implemented in 2012 by
+.An Luigi Rizzo
+and
+.An Giuseppe Lettieri
+at the Universita` di Pisa.
+.Pp
+.Nm
+was funded by the European Commission within FP7 Projects
+CHANGE (257422) and OPENLAB (287581).
diff --git a/sys/dev/netmap/if_em_netmap.h b/sys/dev/netmap/if_em_netmap.h
index 5a66f0e04..cda0ee38f 100644
--- a/sys/dev/netmap/if_em_netmap.h
+++ b/sys/dev/netmap/if_em_netmap.h
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,8 +26,6 @@
  */
 
 /*
- * $FreeBSD: head/sys/dev/netmap/if_em_netmap.h 238985 2012-08-02 11:59:43Z luigi $
- *
  * netmap support for: em.
  *
  * For more details on netmap support please see ixgbe_netmap.h
@@ -188,8 +188,8 @@ em_netmap_txsync(struct netmap_kring *kring, int flags)
 	if (flags & NAF_FORCE_RECLAIM || nm_kr_txempty(kring)) {
 		/* record completed transmissions using TDH */
 		nic_i = E1000_READ_REG(&adapter->hw, E1000_TDH(kring->ring_id));
-		if (nic_i >= kring->nkr_num_slots) { /* XXX can it happen ? */
-			D("TDH wrap %d", nic_i);
+		if (unlikely(nic_i >= kring->nkr_num_slots)) {
+			nm_prerr("TDH wrap at idx %d", nic_i);
 			nic_i -= kring->nkr_num_slots;
 		}
 		if (nic_i != txr->next_to_clean) {
@@ -233,8 +233,6 @@ em_netmap_rxsync(struct netmap_kring *kring, int flags)
 	 * First part: import newly received packets.
 	 */
 	if (netmap_no_pendintr || force_update) {
-		uint16_t slot_flags = kring->nkr_slot_flags;
-
 		nic_i = rxr->next_to_check;
 		nm_i = netmap_idx_n2k(kring, nic_i);
 
@@ -245,7 +243,7 @@ em_netmap_rxsync(struct netmap_kring *kring, int flags)
 			if ((staterr & E1000_RXD_STAT_DD) == 0)
 				break;
 			ring->slot[nm_i].len = le16toh(curr->wb.upper.length);
-			ring->slot[nm_i].flags = slot_flags;
+			ring->slot[nm_i].flags = 0;
 			bus_dmamap_sync(rxr->rxtag, rxr->rx_buffers[nic_i].map,
 				BUS_DMASYNC_POSTREAD);
 			nm_i = nm_next(nm_i, lim);
diff --git a/sys/dev/netmap/if_igb_netmap.h b/sys/dev/netmap/if_igb_netmap.h
index 884785f71..b10348e7d 100644
--- a/sys/dev/netmap/if_igb_netmap.h
+++ b/sys/dev/netmap/if_igb_netmap.h
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2011-2014 Universita` di Pisa. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,8 +26,6 @@
  */
 
 /*
- * $FreeBSD: head/sys/dev/netmap/if_igb_netmap.h 256200 2013-10-09 17:32:52Z jfv $
- *
  * Netmap support for igb, partly contributed by Ahmed Kooli
  * For details on netmap support please see ixgbe_netmap.h
  */
@@ -172,8 +172,8 @@ igb_netmap_txsync(struct netmap_kring *kring, int flags)
 	if (flags & NAF_FORCE_RECLAIM || nm_kr_txempty(kring)) {
 		/* record completed transmissions using TDH */
 		nic_i = E1000_READ_REG(&adapter->hw, E1000_TDH(kring->ring_id));
-		if (nic_i >= kring->nkr_num_slots) { /* XXX can it happen ? */
-			D("TDH wrap %d", nic_i);
+		if (unlikely(nic_i >= kring->nkr_num_slots)) {
+			nm_prerr("TDH wrap at idx %d", nic_i);
 			nic_i -= kring->nkr_num_slots;
 		}
 		txr->next_to_clean = nic_i;
@@ -215,8 +215,6 @@ igb_netmap_rxsync(struct netmap_kring *kring, int flags)
 	 * First part: import newly received packets.
 	 */
 	if (netmap_no_pendintr || force_update) {
-		uint16_t slot_flags = kring->nkr_slot_flags;
-
 		nic_i = rxr->next_to_check;
 		nm_i = netmap_idx_n2k(kring, nic_i);
 
@@ -227,7 +225,7 @@ igb_netmap_rxsync(struct netmap_kring *kring, int flags)
 			if ((staterr & E1000_RXD_STAT_DD) == 0)
 				break;
 			ring->slot[nm_i].len = le16toh(curr->wb.upper.length);
-			ring->slot[nm_i].flags = slot_flags;
+			ring->slot[nm_i].flags = 0;
 			bus_dmamap_sync(rxr->ptag,
 			    rxr->rx_buffers[nic_i].pmap, BUS_DMASYNC_POSTREAD);
 			nm_i = nm_next(nm_i, lim);
diff --git a/sys/dev/netmap/if_ixl_netmap.h b/sys/dev/netmap/if_ixl_netmap.h
index f6aebbb1a..43ea9fb48 100644
--- a/sys/dev/netmap/if_ixl_netmap.h
+++ b/sys/dev/netmap/if_ixl_netmap.h
@@ -24,8 +24,6 @@
  */
 
 /*
- * $FreeBSD: head/sys/dev/netmap/if_ixl_netmap.h 279232 2015-02-24 06:20:50Z luigi $
- *
  * netmap support for: ixl
  *
  * derived from ixgbe
@@ -67,6 +65,7 @@ extern int ixl_rx_miss, ixl_rx_miss_bufs, ixl_crcstrip;
  * ixl_rx_miss, ixl_rx_miss_bufs:
  *	count packets that might be missed due to lost interrupts.
  */
+int ixl_rx_miss, ixl_rx_miss_bufs, ixl_crcstrip = 1;
 SYSCTL_DECL(_dev_netmap);
 /*
  * The xl driver by default strips CRCs and we do not override it.
@@ -128,12 +127,8 @@ ixl_netmap_attach(struct ixl_vsi *vsi)
 
 	na.ifp = vsi->ifp;
 	na.na_flags = NAF_BDG_MAYSLEEP;
-	// XXX check that queues is set.
-	printf("queues is %p\n", vsi->queues);
-	if (vsi->queues) {
-		na.num_tx_desc = vsi->queues[0].num_desc;
-		na.num_rx_desc = vsi->queues[0].num_desc;
-	}
+	na.num_tx_desc = vsi->num_tx_desc;
+	na.num_rx_desc = vsi->num_rx_desc;
 	na.nm_txsync = ixl_netmap_txsync;
 	na.nm_rxsync = ixl_netmap_rxsync;
 	na.nm_register = ixl_netmap_reg;
@@ -265,8 +260,10 @@ ixl_netmap_txsync(struct netmap_kring *kring, int flags)
 	/*
 	 * Second part: reclaim buffers for completed transmissions.
 	 */
-	nic_i = LE32_TO_CPU(*(volatile __le32 *)&txr->base[que->num_desc]);
-	if (nic_i != txr->next_to_clean) {
+	nic_i = LE32_TO_CPU(*(volatile __le32 *)&txr->base[que->num_tx_desc]);
+	if (unlikely(nic_i >= que->num_tx_desc)) {
+		nm_prerr("error: invalid value of hw head index %u", nic_i);
+	} else if (nic_i != txr->next_to_clean) {
 		/* some tx completed, increment avail */
 		txr->next_to_clean = nic_i;
 		kring->nr_hwtail = nm_prev(netmap_idx_n2k(kring, nic_i), lim);
@@ -331,7 +328,6 @@ ixl_netmap_rxsync(struct netmap_kring *kring, int flags)
 	 */
 	if (netmap_no_pendintr || force_update) {
 		int crclen = ixl_crcstrip ? 0 : 4;
-		uint16_t slot_flags = kring->nkr_slot_flags;
 
 		nic_i = rxr->next_check; // or also k2n(kring->nr_hwtail)
 		nm_i = netmap_idx_n2k(kring, nic_i);
@@ -346,7 +342,7 @@ ixl_netmap_rxsync(struct netmap_kring *kring, int flags)
 				break;
 			ring->slot[nm_i].len = ((qword & I40E_RXD_QW1_LENGTH_PBUF_MASK)
 			    >> I40E_RXD_QW1_LENGTH_PBUF_SHIFT) - crclen;
-			ring->slot[nm_i].flags = slot_flags;
+			ring->slot[nm_i].flags = 0;
 			bus_dmamap_sync(rxr->ptag,
 			    rxr->buffers[nic_i].pmap, BUS_DMASYNC_POSTREAD);
 			nm_i = nm_next(nm_i, lim);
diff --git a/sys/dev/netmap/if_lem_netmap.h b/sys/dev/netmap/if_lem_netmap.h
index d8c590145..9842f9714 100644
--- a/sys/dev/netmap/if_lem_netmap.h
+++ b/sys/dev/netmap/if_lem_netmap.h
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,8 +27,6 @@
 
 
 /*
- * $FreeBSD: head/sys/dev/netmap/if_lem_netmap.h 271849 2014-09-19 03:51:26Z glebius $
- *
  * netmap support for: lem
  *
  * For details on netmap support please see ixgbe_netmap.h
@@ -172,8 +172,8 @@ lem_netmap_txsync(struct netmap_kring *kring, int flags)
 		kring->last_reclaim = ticks;
 		/* record completed transmissions using TDH */
 		nic_i = E1000_READ_REG(&adapter->hw, E1000_TDH(0));
-		if (nic_i >= kring->nkr_num_slots) { /* XXX can it happen ? */
-			D("TDH wrap %d", nic_i);
+		if (unlikely(nic_i >= kring->nkr_num_slots)) {
+			nm_prerr("TDH wrap at idx %d", nic_i);
 			nic_i -= kring->nkr_num_slots;
 		}
 		adapter->next_tx_to_clean = nic_i;
@@ -214,8 +214,6 @@ lem_netmap_rxsync(struct netmap_kring *kring, int flags)
 	 * First part: import newly received packets.
 	 */
 	if (netmap_no_pendintr || force_update) {
-		uint16_t slot_flags = kring->nkr_slot_flags;
-
 		nic_i = adapter->next_rx_desc_to_check;
 		nm_i = netmap_idx_n2k(kring, nic_i);
 
@@ -228,11 +226,11 @@ lem_netmap_rxsync(struct netmap_kring *kring, int flags)
 				break;
 			len = le16toh(curr->length) - 4; // CRC
 			if (len < 0) {
-				RD(5, "bogus pkt (%d) size %d nic idx %d", n, len, nic_i);
+				nm_prlim(2, "bogus pkt (%d) size %d nic idx %d", n, len, nic_i);
 				len = 0;
 			}
 			ring->slot[nm_i].len = len;
-			ring->slot[nm_i].flags = slot_flags;
+			ring->slot[nm_i].flags = 0;
 			bus_dmamap_sync(adapter->rxtag,
 				adapter->rx_buffer_area[nic_i].map,
 				BUS_DMASYNC_POSTREAD);
@@ -240,7 +238,7 @@ lem_netmap_rxsync(struct netmap_kring *kring, int flags)
 			nic_i = nm_next(nic_i, lim);
 		}
 		if (n) { /* update the state variables */
-			ND("%d new packets at nic %d nm %d tail %d",
+			nm_prdis("%d new packets at nic %d nm %d tail %d",
 				n,
 				adapter->next_rx_desc_to_check,
 				netmap_idx_n2k(kring, adapter->next_rx_desc_to_check),
diff --git a/sys/dev/netmap/if_ptnet.c b/sys/dev/netmap/if_ptnet.c
index 4c7072774..56d853eb7 100644
--- a/sys/dev/netmap/if_ptnet.c
+++ b/sys/dev/netmap/if_ptnet.c
@@ -22,8 +22,6 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $FreeBSD$
  */
 
 /* Driver for ptnet paravirtualized network device. */
@@ -68,7 +66,6 @@
 #include 
 #include 
 #include 
-#include 
 
 #include 
 #include 
@@ -88,20 +85,13 @@
 #include 
 #include 
 
-#ifndef PTNET_CSB_ALLOC
-#error "No support for on-device CSB"
-#endif
+#ifdef WITH_PTNETMAP
 
 #ifndef INET
 #error "INET not defined, cannot support offloadings"
 #endif
 
-#if __FreeBSD_version >= 1100000
 static uint64_t	ptnet_get_counter(if_t, ift_counter);
-#else
-typedef struct ifnet *if_t;
-#define if_getsoftc(_ifp)   (_ifp)->if_softc
-#endif
 
 //#define PTNETMAP_STATS
 //#define DEBUG
@@ -132,7 +122,8 @@ struct ptnet_queue {
 	struct				resource *irq;
 	void				*cookie;
 	int				kring_id;
-	struct ptnet_ring		*ptring;
+	struct nm_csb_atok		*atok;
+	struct nm_csb_ktoa		*ktoa;
 	unsigned int			kick;
 	struct mtx			lock;
 	struct buf_ring			*bufring; /* for TX queues */
@@ -169,7 +160,8 @@ struct ptnet_softc {
 	unsigned int		num_tx_rings;
 	struct ptnet_queue	*queues;
 	struct ptnet_queue	*rxqueues;
-	struct ptnet_csb	*csb;
+	struct nm_csb_atok	*csb_gh;
+	struct nm_csb_ktoa	*csb_hg;
 
 	unsigned int		min_tx_space;
 
@@ -211,13 +203,14 @@ static void	ptnet_tick(void *opaque);
 static int	ptnet_irqs_init(struct ptnet_softc *sc);
 static void	ptnet_irqs_fini(struct ptnet_softc *sc);
 
-static uint32_t ptnet_nm_ptctl(if_t ifp, uint32_t cmd);
-static int	ptnet_nm_config(struct netmap_adapter *na, unsigned *txr,
-				unsigned *txd, unsigned *rxr, unsigned *rxd);
+static uint32_t ptnet_nm_ptctl(struct ptnet_softc *sc, uint32_t cmd);
+static int      ptnet_nm_config(struct netmap_adapter *na,
+				struct nm_config_info *info);
 static void	ptnet_update_vnet_hdr(struct ptnet_softc *sc);
 static int	ptnet_nm_register(struct netmap_adapter *na, int onoff);
 static int	ptnet_nm_txsync(struct netmap_kring *kring, int flags);
 static int	ptnet_nm_rxsync(struct netmap_kring *kring, int flags);
+static void	ptnet_nm_intr(struct netmap_adapter *na, int onoff);
 
 static void	ptnet_tx_intr(void *opaque);
 static void	ptnet_rx_intr(void *opaque);
@@ -249,9 +242,8 @@ static driver_t ptnet_driver = {
 };
 
 /* We use (SI_ORDER_MIDDLE+2) here, see DEV_MODULE_ORDERED() invocation. */
-static devclass_t ptnet_devclass;
-DRIVER_MODULE_ORDERED(ptnet, pci, ptnet_driver, ptnet_devclass,
-		      NULL, NULL, SI_ORDER_MIDDLE + 2);
+DRIVER_MODULE_ORDERED(ptnet, pci, ptnet_driver, NULL, NULL,
+		      SI_ORDER_MIDDLE + 2);
 
 static int
 ptnet_probe(device_t dev)
@@ -282,9 +274,8 @@ static inline void ptnet_kick(struct ptnet_queue *pq)
 #define PTNET_HDR_SIZE		sizeof(struct virtio_net_hdr_mrg_rxbuf)
 #define PTNET_MAX_PKT_SIZE	65536
 
-#define PTNET_CSUM_OFFLOAD	(CSUM_TCP | CSUM_UDP | CSUM_SCTP)
-#define PTNET_CSUM_OFFLOAD_IPV6	(CSUM_TCP_IPV6 | CSUM_UDP_IPV6 |\
-				 CSUM_SCTP_IPV6)
+#define PTNET_CSUM_OFFLOAD	(CSUM_TCP | CSUM_UDP)
+#define PTNET_CSUM_OFFLOAD_IPV6	(CSUM_TCP_IPV6 | CSUM_UDP_IPV6)
 #define PTNET_ALL_OFFLOAD	(CSUM_TSO | PTNET_CSUM_OFFLOAD |\
 				 PTNET_CSUM_OFFLOAD_IPV6)
 
@@ -323,33 +314,48 @@ ptnet_attach(device_t dev)
 	ptfeatures = bus_read_4(sc->iomem, PTNET_IO_PTFEAT); /* acked */
 	sc->ptfeatures = ptfeatures;
 
-	/* Allocate CSB and carry out CSB allocation protocol (CSBBAH first,
-	 * then CSBBAL). */
-	sc->csb = malloc(sizeof(struct ptnet_csb), M_DEVBUF,
-			 M_NOWAIT | M_ZERO);
-	if (sc->csb == NULL) {
+	num_tx_rings = bus_read_4(sc->iomem, PTNET_IO_NUM_TX_RINGS);
+	num_rx_rings = bus_read_4(sc->iomem, PTNET_IO_NUM_RX_RINGS);
+	sc->num_rings = num_tx_rings + num_rx_rings;
+	sc->num_tx_rings = num_tx_rings;
+
+	if (sc->num_rings * sizeof(struct nm_csb_atok) > PAGE_SIZE) {
+		device_printf(dev, "CSB cannot handle that many rings (%u)\n",
+				sc->num_rings);
+		err = ENOMEM;
+		goto err_path;
+	}
+
+	/* Allocate CSB and carry out CSB allocation protocol. */
+	sc->csb_gh = contigmalloc(2*PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO,
+				  (size_t)0, -1UL, PAGE_SIZE, 0);
+	if (sc->csb_gh == NULL) {
 		device_printf(dev, "Failed to allocate CSB\n");
 		err = ENOMEM;
 		goto err_path;
 	}
+	sc->csb_hg = (struct nm_csb_ktoa *)(((char *)sc->csb_gh) + PAGE_SIZE);
 
 	{
 		/*
 		 * We use uint64_t rather than vm_paddr_t since we
 		 * need 64 bit addresses even on 32 bit platforms.
 		 */
-		uint64_t paddr = vtophys(sc->csb);
+		uint64_t paddr = vtophys(sc->csb_gh);
 
-		bus_write_4(sc->iomem, PTNET_IO_CSBBAH,
-			    (paddr >> 32) & 0xffffffff);
-		bus_write_4(sc->iomem, PTNET_IO_CSBBAL, paddr & 0xffffffff);
+		/* CSB allocation protocol: write to BAH first, then
+		 * to BAL (for both GH and HG sections). */
+		bus_write_4(sc->iomem, PTNET_IO_CSB_GH_BAH,
+				(paddr >> 32) & 0xffffffff);
+		bus_write_4(sc->iomem, PTNET_IO_CSB_GH_BAL,
+				paddr & 0xffffffff);
+		paddr = vtophys(sc->csb_hg);
+		bus_write_4(sc->iomem, PTNET_IO_CSB_HG_BAH,
+				(paddr >> 32) & 0xffffffff);
+		bus_write_4(sc->iomem, PTNET_IO_CSB_HG_BAL,
+				paddr & 0xffffffff);
 	}
 
-	num_tx_rings = bus_read_4(sc->iomem, PTNET_IO_NUM_TX_RINGS);
-	num_rx_rings = bus_read_4(sc->iomem, PTNET_IO_NUM_RX_RINGS);
-	sc->num_rings = num_tx_rings + num_rx_rings;
-	sc->num_tx_rings = num_tx_rings;
-
 	/* Allocate and initialize per-queue data structures. */
 	sc->queues = malloc(sizeof(struct ptnet_queue) * sc->num_rings,
 			    M_DEVBUF, M_NOWAIT | M_ZERO);
@@ -365,7 +371,8 @@ ptnet_attach(device_t dev)
 		pq->sc = sc;
 		pq->kring_id = i;
 		pq->kick = PTNET_IO_KICK_BASE + 4 * i;
-		pq->ptring = sc->csb->rings + i;
+		pq->atok = sc->csb_gh + i;
+		pq->ktoa = sc->csb_hg + i;
 		snprintf(pq->lock_name, sizeof(pq->lock_name), "%s-%d",
 			 device_get_nameunit(dev), i);
 		mtx_init(&pq->lock, pq->lock_name, NULL, MTX_DEF);
@@ -399,16 +406,14 @@ ptnet_attach(device_t dev)
 	}
 
 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
-	ifp->if_baudrate = IF_Gbps(10);
-	ifp->if_softc = sc;
-	ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
-	ifp->if_init = ptnet_init;
-	ifp->if_ioctl = ptnet_ioctl;
-#if __FreeBSD_version >= 1100000
-	ifp->if_get_counter = ptnet_get_counter;
-#endif
-	ifp->if_transmit = ptnet_transmit;
-	ifp->if_qflush = ptnet_qflush;
+	if_setbaudrate(ifp, IF_Gbps(10));
+	if_setsoftc(ifp, sc);
+	if_setflags(ifp, IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX);
+	if_setinitfn(ifp, ptnet_init);
+	if_setioctlfn(ifp, ptnet_ioctl);
+	if_setget_counter(ifp, ptnet_get_counter);
+	if_settransmitfn(ifp, ptnet_transmit);
+	if_setqflushfn(ifp, ptnet_qflush);
 
 	ifmedia_init(&sc->media, IFM_IMASK, ptnet_media_change,
 		     ptnet_media_status);
@@ -426,25 +431,25 @@ ptnet_attach(device_t dev)
 
 	ether_ifattach(ifp, sc->hwaddr);
 
-	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
-	ifp->if_capabilities |= IFCAP_JUMBO_MTU | IFCAP_VLAN_MTU;
+	if_setifheaderlen(ifp, sizeof(struct ether_vlan_header));
+	if_setcapabilitiesbit(ifp, IFCAP_JUMBO_MTU | IFCAP_VLAN_MTU, 0);
 
 	if (sc->ptfeatures & PTNETMAP_F_VNET_HDR) {
 		/* Similarly to what the vtnet driver does, we can emulate
 		 * VLAN offloadings by inserting and removing the 802.1Q
 		 * header during transmit and receive. We are then able
 		 * to do checksum offloading of VLAN frames. */
-		ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6
+		if_setcapabilitiesbit(ifp, IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6
 					| IFCAP_VLAN_HWCSUM
 					| IFCAP_TSO | IFCAP_LRO
 					| IFCAP_VLAN_HWTSO
-					| IFCAP_VLAN_HWTAGGING;
+					| IFCAP_VLAN_HWTAGGING, 0);
 	}
 
-	ifp->if_capenable = ifp->if_capabilities;
+	if_setcapenable(ifp, if_getcapabilities(ifp));
 #ifdef DEVICE_POLLING
 	/* Don't enable polling by default. */
-	ifp->if_capabilities |= IFCAP_POLLING;
+	if_setcapabilitiesbit(ifp, IFCAP_POLLING, 0);
 #endif
 	snprintf(sc->lock_name, sizeof(sc->lock_name),
 		 "%s", device_get_nameunit(dev));
@@ -463,11 +468,12 @@ ptnet_attach(device_t dev)
 	na_arg.nm_krings_create = ptnet_nm_krings_create;
 	na_arg.nm_krings_delete = ptnet_nm_krings_delete;
 	na_arg.nm_dtor = ptnet_nm_dtor;
+	na_arg.nm_intr = ptnet_nm_intr;
 	na_arg.nm_register = ptnet_nm_register;
 	na_arg.nm_txsync = ptnet_nm_txsync;
 	na_arg.nm_rxsync = ptnet_nm_rxsync;
 
-	netmap_pt_guest_attach(&na_arg, sc->csb, nifp_offset,
+	netmap_pt_guest_attach(&na_arg, nifp_offset,
                                 bus_read_4(sc->iomem, PTNET_IO_HOSTMEMID));
 
 	/* Now a netmap adapter for this ifp has been allocated, and it
@@ -489,14 +495,27 @@ ptnet_attach(device_t dev)
 	return err;
 }
 
+/* Stop host sync-kloop if it was running. */
+static void
+ptnet_device_shutdown(struct ptnet_softc *sc)
+{
+	ptnet_nm_ptctl(sc, PTNETMAP_PTCTL_DELETE);
+	bus_write_4(sc->iomem, PTNET_IO_CSB_GH_BAH, 0);
+	bus_write_4(sc->iomem, PTNET_IO_CSB_GH_BAL, 0);
+	bus_write_4(sc->iomem, PTNET_IO_CSB_HG_BAH, 0);
+	bus_write_4(sc->iomem, PTNET_IO_CSB_HG_BAL, 0);
+}
+
 static int
 ptnet_detach(device_t dev)
 {
 	struct ptnet_softc *sc = device_get_softc(dev);
 	int i;
 
+	ptnet_device_shutdown(sc);
+
 #ifdef DEVICE_POLLING
-	if (sc->ifp->if_capenable & IFCAP_POLLING) {
+	if (if_getcapenable(sc->ifp) & IFCAP_POLLING) {
 		ether_poll_deregister(sc->ifp);
 	}
 #endif
@@ -526,11 +545,10 @@ ptnet_detach(device_t dev)
 
 	ptnet_irqs_fini(sc);
 
-	if (sc->csb) {
-		bus_write_4(sc->iomem, PTNET_IO_CSBBAH, 0);
-		bus_write_4(sc->iomem, PTNET_IO_CSBBAL, 0);
-		free(sc->csb, M_DEVBUF);
-		sc->csb = NULL;
+	if (sc->csb_gh) {
+		contigfree(sc->csb_gh, 2*PAGE_SIZE, M_DEVBUF);
+		sc->csb_gh = NULL;
+		sc->csb_hg = NULL;
 	}
 
 	if (sc->queues) {
@@ -564,9 +582,8 @@ ptnet_detach(device_t dev)
 static int
 ptnet_suspend(device_t dev)
 {
-	struct ptnet_softc *sc;
+	struct ptnet_softc *sc = device_get_softc(dev);
 
-	sc = device_get_softc(dev);
 	(void)sc;
 
 	return (0);
@@ -575,9 +592,8 @@ ptnet_suspend(device_t dev)
 static int
 ptnet_resume(device_t dev)
 {
-	struct ptnet_softc *sc;
+	struct ptnet_softc *sc = device_get_softc(dev);
 
-	sc = device_get_softc(dev);
 	(void)sc;
 
 	return (0);
@@ -586,11 +602,11 @@ ptnet_resume(device_t dev)
 static int
 ptnet_shutdown(device_t dev)
 {
-	/*
-	 * Suspend already does all of what we need to
-	 * do here; we just never expect to be resumed.
-	 */
-	return (ptnet_suspend(dev));
+	struct ptnet_softc *sc = device_get_softc(dev);
+
+	ptnet_device_shutdown(sc);
+
+	return (0);
 }
 
 static int
@@ -669,11 +685,12 @@ ptnet_irqs_init(struct ptnet_softc *sc)
 	cpu_cur = CPU_FIRST();
 	for (i = 0; i < nvecs; i++) {
 		struct ptnet_queue *pq = sc->queues + i;
-		static void (*handler)(void *context, int pending);
 
-		handler = (i < sc->num_tx_rings) ? ptnet_tx_task : ptnet_rx_task;
+		if (i < sc->num_tx_rings)
+			TASK_INIT(&pq->task, 0, ptnet_tx_task, pq);
+		else
+			NET_TASK_INIT(&pq->task, 0, ptnet_rx_task, pq);
 
-		TASK_INIT(&pq->task, 0, handler, pq);
 		pq->taskq = taskqueue_create_fast("ptnet_queue", M_NOWAIT,
 					taskqueue_thread_enqueue, &pq->taskq);
 		taskqueue_start_threads(&pq->taskq, 1, PI_NET, "%s-pq-%d",
@@ -738,13 +755,13 @@ ptnet_ioctl(if_t ifp, u_long cmd, caddr_t data)
 	struct ptnet_softc *sc = if_getsoftc(ifp);
 	device_t dev = sc->dev;
 	struct ifreq *ifr = (struct ifreq *)data;
-	int mask, err = 0;
+	int mask __unused, err = 0;
 
 	switch (cmd) {
 	case SIOCSIFFLAGS:
-		device_printf(dev, "SIOCSIFFLAGS %x\n", ifp->if_flags);
+		device_printf(dev, "SIOCSIFFLAGS %x\n", if_getflags(ifp));
 		PTNET_CORE_LOCK(sc);
-		if (ifp->if_flags & IFF_UP) {
+		if (if_getflags(ifp) & IFF_UP) {
 			/* Network stack wants the iff to be up. */
 			err = ptnet_init_locked(sc);
 		} else {
@@ -758,8 +775,8 @@ ptnet_ioctl(if_t ifp, u_long cmd, caddr_t data)
 
 	case SIOCSIFCAP:
 		device_printf(dev, "SIOCSIFCAP %x %x\n",
-			      ifr->ifr_reqcap, ifp->if_capenable);
-		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+			      ifr->ifr_reqcap, if_getcapenable(ifp));
+		mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
 #ifdef DEVICE_POLLING
 		if (mask & IFCAP_POLLING) {
 			struct ptnet_queue *pq;
@@ -771,13 +788,13 @@ ptnet_ioctl(if_t ifp, u_long cmd, caddr_t data)
 					break;
 				}
 				/* Stop queues and sync with taskqueues. */
-				ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+				if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
 				for (i = 0; i < sc->num_rings; i++) {
 					pq = sc-> queues + i;
 					/* Make sure the worker sees the
 					 * IFF_DRV_RUNNING down. */
 					PTNET_Q_LOCK(pq);
-					pq->ptring->guest_need_kick = 0;
+					pq->atok->appl_need_kick = 0;
 					PTNET_Q_UNLOCK(pq);
 					/* Wait for rescheduling to finish. */
 					if (pq->taskq) {
@@ -785,19 +802,19 @@ ptnet_ioctl(if_t ifp, u_long cmd, caddr_t data)
 								&pq->task);
 					}
 				}
-				ifp->if_drv_flags |= IFF_DRV_RUNNING;
+				if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
 			} else {
 				err = ether_poll_deregister(ifp);
 				for (i = 0; i < sc->num_rings; i++) {
 					pq = sc-> queues + i;
 					PTNET_Q_LOCK(pq);
-					pq->ptring->guest_need_kick = 1;
+					pq->atok->appl_need_kick = 1;
 					PTNET_Q_UNLOCK(pq);
 				}
 			}
 		}
 #endif  /* DEVICE_POLLING */
-		ifp->if_capenable = ifr->ifr_reqcap;
+		if_setcapenable(ifp, ifr->ifr_reqcap);
 		break;
 
 	case SIOCSIFMTU:
@@ -807,7 +824,7 @@ ptnet_ioctl(if_t ifp, u_long cmd, caddr_t data)
 			err = EINVAL;
 		} else {
 			PTNET_CORE_LOCK(sc);
-			ifp->if_mtu = ifr->ifr_mtu;
+			if_setmtu(ifp, ifr->ifr_mtu);
 			PTNET_CORE_UNLOCK(sc);
 		}
 		break;
@@ -834,22 +851,22 @@ ptnet_init_locked(struct ptnet_softc *sc)
 	unsigned int nm_buf_size;
 	int ret;
 
-	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
 		return 0; /* nothing to do */
 	}
 
 	device_printf(sc->dev, "%s\n", __func__);
 
 	/* Translate offload capabilities according to if_capenable. */
-	ifp->if_hwassist = 0;
-	if (ifp->if_capenable & IFCAP_TXCSUM)
-		ifp->if_hwassist |= PTNET_CSUM_OFFLOAD;
-	if (ifp->if_capenable & IFCAP_TXCSUM_IPV6)
-		ifp->if_hwassist |= PTNET_CSUM_OFFLOAD_IPV6;
-	if (ifp->if_capenable & IFCAP_TSO4)
-		ifp->if_hwassist |= CSUM_IP_TSO;
-	if (ifp->if_capenable & IFCAP_TSO6)
-		ifp->if_hwassist |= CSUM_IP6_TSO;
+	if_sethwassist(ifp, 0);
+	if (if_getcapenable(ifp) & IFCAP_TXCSUM)
+		if_sethwassistbits(ifp, PTNET_CSUM_OFFLOAD, 0);
+	if (if_getcapenable(ifp) & IFCAP_TXCSUM_IPV6)
+		if_sethwassistbits(ifp, PTNET_CSUM_OFFLOAD_IPV6, 0);
+	if (if_getcapenable(ifp) & IFCAP_TSO4)
+		if_sethwassistbits(ifp, CSUM_IP_TSO, 0);
+	if (if_getcapenable(ifp) & IFCAP_TSO6)
+		if_sethwassistbits(ifp, CSUM_IP6_TSO, 0);
 
 	/*
 	 * Prepare the interface for netmap mode access.
@@ -862,7 +879,7 @@ ptnet_init_locked(struct ptnet_softc *sc)
 		return ret;
 	}
 
-	if (sc->ptna->backend_regifs == 0) {
+	if (sc->ptna->backend_users == 0) {
 		ret = ptnet_nm_krings_create(na_nm);
 		if (ret) {
 			device_printf(sc->dev, "ptnet_nm_krings_create() "
@@ -900,7 +917,7 @@ ptnet_init_locked(struct ptnet_softc *sc)
 	callout_reset(&sc->tick, hz, ptnet_tick, sc);
 #endif
 
-	ifp->if_drv_flags |= IFF_DRV_RUNNING;
+	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
 
 	return 0;
 
@@ -927,14 +944,14 @@ ptnet_stop(struct ptnet_softc *sc)
 
 	device_printf(sc->dev, "%s\n", __func__);
 
-	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+	if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING)) {
 		return 0; /* nothing to do */
 	}
 
 	/* Clear the driver-ready flag, and synchronize with all the queues,
 	 * so that after this loop we are sure nobody is working anymore with
 	 * the device. This scheme is taken from the vtnet driver. */
-	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
 	callout_stop(&sc->tick);
 	for (i = 0; i < sc->num_rings; i++) {
 		PTNET_Q_LOCK(sc->queues + i);
@@ -943,7 +960,7 @@ ptnet_stop(struct ptnet_softc *sc)
 
 	ptnet_nm_register(na_dr, 0 /* off */);
 
-	if (sc->ptna->backend_regifs == 0) {
+	if (sc->ptna->backend_users == 0) {
 		netmap_mem_rings_delete(na_dr);
 		ptnet_nm_krings_delete(na_nm);
 	}
@@ -988,7 +1005,6 @@ ptnet_media_change(if_t ifp)
 	return 0;
 }
 
-#if __FreeBSD_version >= 1100000
 static uint64_t
 ptnet_get_counter(if_t ifp, ift_counter cnt)
 {
@@ -1026,7 +1042,6 @@ ptnet_get_counter(if_t ifp, ift_counter cnt)
 		return (if_get_counter_default(ifp, cnt));
 	}
 }
-#endif
 
 
 #ifdef PTNETMAP_STATS
@@ -1073,9 +1088,8 @@ ptnet_media_status(if_t ifp, struct ifmediareq *ifmr)
 }
 
 static uint32_t
-ptnet_nm_ptctl(if_t ifp, uint32_t cmd)
+ptnet_nm_ptctl(struct ptnet_softc *sc, uint32_t cmd)
 {
-	struct ptnet_softc *sc = if_getsoftc(ifp);
 	/*
 	 * Write a command and read back error status,
 	 * with zero meaning success.
@@ -1085,18 +1099,20 @@ ptnet_nm_ptctl(if_t ifp, uint32_t cmd)
 }
 
 static int
-ptnet_nm_config(struct netmap_adapter *na, unsigned *txr, unsigned *txd,
-		unsigned *rxr, unsigned *rxd)
+ptnet_nm_config(struct netmap_adapter *na, struct nm_config_info *info)
 {
 	struct ptnet_softc *sc = if_getsoftc(na->ifp);
 
-	*txr = bus_read_4(sc->iomem, PTNET_IO_NUM_TX_RINGS);
-	*rxr = bus_read_4(sc->iomem, PTNET_IO_NUM_RX_RINGS);
-	*txd = bus_read_4(sc->iomem, PTNET_IO_NUM_TX_SLOTS);
-	*rxd = bus_read_4(sc->iomem, PTNET_IO_NUM_RX_SLOTS);
+	info->num_tx_rings = bus_read_4(sc->iomem, PTNET_IO_NUM_TX_RINGS);
+	info->num_rx_rings = bus_read_4(sc->iomem, PTNET_IO_NUM_RX_RINGS);
+	info->num_tx_descs = bus_read_4(sc->iomem, PTNET_IO_NUM_TX_SLOTS);
+	info->num_rx_descs = bus_read_4(sc->iomem, PTNET_IO_NUM_RX_SLOTS);
+	info->rx_buf_maxsize = NETMAP_BUF_SIZE(na);
 
-	device_printf(sc->dev, "txr %u, rxr %u, txd %u, rxd %u\n",
-		      *txr, *rxr, *txd, *rxd);
+	device_printf(sc->dev, "txr %u, rxr %u, txd %u, rxd %u, rxbufsz %u\n",
+			info->num_tx_rings, info->num_rx_rings,
+			info->num_tx_descs, info->num_rx_descs,
+			info->rx_buf_maxsize);
 
 	return 0;
 }
@@ -1109,24 +1125,25 @@ ptnet_sync_from_csb(struct ptnet_softc *sc, struct netmap_adapter *na)
 	/* Sync krings from the host, reading from
 	 * CSB. */
 	for (i = 0; i < sc->num_rings; i++) {
-		struct ptnet_ring *ptring = sc->queues[i].ptring;
+		struct nm_csb_atok *atok = sc->queues[i].atok;
+		struct nm_csb_ktoa *ktoa = sc->queues[i].ktoa;
 		struct netmap_kring *kring;
 
 		if (i < na->num_tx_rings) {
-			kring = na->tx_rings + i;
+			kring = na->tx_rings[i];
 		} else {
-			kring = na->rx_rings + i - na->num_tx_rings;
+			kring = na->rx_rings[i - na->num_tx_rings];
 		}
-		kring->rhead = kring->ring->head = ptring->head;
-		kring->rcur = kring->ring->cur = ptring->cur;
-		kring->nr_hwcur = ptring->hwcur;
+		kring->rhead = kring->ring->head = atok->head;
+		kring->rcur = kring->ring->cur = atok->cur;
+		kring->nr_hwcur = ktoa->hwcur;
 		kring->nr_hwtail = kring->rtail =
-			kring->ring->tail = ptring->hwtail;
+			kring->ring->tail = ktoa->hwtail;
 
-		ND("%d,%d: csb {hc %u h %u c %u ht %u}", t, i,
-		   ptring->hwcur, ptring->head, ptring->cur,
-		   ptring->hwtail);
-		ND("%d,%d: kring {hc %u rh %u rc %u h %u c %u ht %u rt %u t %u}",
+		nm_prdis("%d,%d: csb {hc %u h %u c %u ht %u}", t, i,
+		   ktoa->hwcur, atok->head, atok->cur,
+		   ktoa->hwtail);
+		nm_prdis("%d,%d: kring {hc %u rh %u rc %u h %u c %u ht %u rt %u t %u}",
 		   t, i, kring->nr_hwcur, kring->rhead, kring->rcur,
 		   kring->ring->head, kring->ring->cur, kring->nr_hwtail,
 		   kring->rtail, kring->ring->tail);
@@ -1151,12 +1168,11 @@ ptnet_nm_register(struct netmap_adapter *na, int onoff)
 	struct ptnet_softc *sc = if_getsoftc(ifp);
 	int native = (na == &sc->ptna->hwup.up);
 	struct ptnet_queue *pq;
-	enum txrx t;
 	int ret = 0;
 	int i;
 
 	if (!onoff) {
-		sc->ptna->backend_regifs--;
+		sc->ptna->backend_users--;
 	}
 
 	/* If this is the last netmap client, guest interrupt enable flags may
@@ -1166,21 +1182,21 @@ ptnet_nm_register(struct netmap_adapter *na, int onoff)
 	 * in the RX rings, since we will not receive further interrupts
 	 * until these will be processed. */
 	if (native && !onoff && na->active_fds == 0) {
-		D("Exit netmap mode, re-enable interrupts");
+		nm_prinf("Exit netmap mode, re-enable interrupts");
 		for (i = 0; i < sc->num_rings; i++) {
 			pq = sc->queues + i;
-			pq->ptring->guest_need_kick = 1;
+			pq->atok->appl_need_kick = 1;
 		}
 	}
 
 	if (onoff) {
-		if (sc->ptna->backend_regifs == 0) {
+		if (sc->ptna->backend_users == 0) {
 			/* Initialize notification enable fields in the CSB. */
 			for (i = 0; i < sc->num_rings; i++) {
 				pq = sc->queues + i;
-				pq->ptring->host_need_kick = 1;
-				pq->ptring->guest_need_kick =
-					(!(ifp->if_capenable & IFCAP_POLLING)
+				pq->ktoa->kern_need_kick = 1;
+				pq->atok->appl_need_kick =
+					(!(if_getcapenable(ifp) & IFCAP_POLLING)
 						&& i >= sc->num_tx_rings);
 			}
 
@@ -1189,62 +1205,36 @@ ptnet_nm_register(struct netmap_adapter *na, int onoff)
 
 			/* Make sure the host adapter passed through is ready
 			 * for txsync/rxsync. */
-			ret = ptnet_nm_ptctl(ifp, PTNETMAP_PTCTL_CREATE);
+			ret = ptnet_nm_ptctl(sc, PTNETMAP_PTCTL_CREATE);
 			if (ret) {
 				return ret;
 			}
-		}
 
-		/* Sync from CSB must be done after REGIF PTCTL. Skip this
-		 * step only if this is a netmap client and it is not the
-		 * first one. */
-		if ((!native && sc->ptna->backend_regifs == 0) ||
-				(native && na->active_fds == 0)) {
+			/* Align the guest krings and rings to the state stored
+			 * in the CSB. */
 			ptnet_sync_from_csb(sc, na);
 		}
 
 		/* If not native, don't call nm_set_native_flags, since we don't want
 		 * to replace if_transmit method, nor set NAF_NETMAP_ON */
 		if (native) {
-			for_rx_tx(t) {
-				for (i = 0; i <= nma_get_nrings(na, t); i++) {
-					struct netmap_kring *kring = &NMR(na, t)[i];
-
-					if (nm_kring_pending_on(kring)) {
-						kring->nr_mode = NKR_NETMAP_ON;
-					}
-				}
-			}
+			netmap_krings_mode_commit(na, onoff);
 			nm_set_native_flags(na);
 		}
 
 	} else {
 		if (native) {
 			nm_clear_native_flags(na);
-			for_rx_tx(t) {
-				for (i = 0; i <= nma_get_nrings(na, t); i++) {
-					struct netmap_kring *kring = &NMR(na, t)[i];
-
-					if (nm_kring_pending_off(kring)) {
-						kring->nr_mode = NKR_NETMAP_OFF;
-					}
-				}
-			}
-		}
-
-		/* Sync from CSB must be done before UNREGIF PTCTL, on the last
-		 * netmap client. */
-		if (native && na->active_fds == 0) {
-			ptnet_sync_from_csb(sc, na);
+			netmap_krings_mode_commit(na, onoff);
 		}
 
-		if (sc->ptna->backend_regifs == 0) {
-			ret = ptnet_nm_ptctl(ifp, PTNETMAP_PTCTL_DELETE);
+		if (sc->ptna->backend_users == 0) {
+			ret = ptnet_nm_ptctl(sc, PTNETMAP_PTCTL_DELETE);
 		}
 	}
 
 	if (onoff) {
-		sc->ptna->backend_regifs++;
+		sc->ptna->backend_users++;
 	}
 
 	return ret;
@@ -1257,7 +1247,7 @@ ptnet_nm_txsync(struct netmap_kring *kring, int flags)
 	struct ptnet_queue *pq = sc->queues + kring->ring_id;
 	bool notify;
 
-	notify = netmap_pt_guest_txsync(pq->ptring, kring, flags);
+	notify = netmap_pt_guest_txsync(pq->atok, pq->ktoa, kring, flags);
 	if (notify) {
 		ptnet_kick(pq);
 	}
@@ -1272,7 +1262,7 @@ ptnet_nm_rxsync(struct netmap_kring *kring, int flags)
 	struct ptnet_queue *pq = sc->rxqueues + kring->ring_id;
 	bool notify;
 
-	notify = netmap_pt_guest_rxsync(pq->ptring, kring, flags);
+	notify = netmap_pt_guest_rxsync(pq->atok, pq->ktoa, kring, flags);
 	if (notify) {
 		ptnet_kick(pq);
 	}
@@ -1280,6 +1270,18 @@ ptnet_nm_rxsync(struct netmap_kring *kring, int flags)
 	return 0;
 }
 
+static void
+ptnet_nm_intr(struct netmap_adapter *na, int onoff)
+{
+	struct ptnet_softc *sc = if_getsoftc(na->ifp);
+	int i;
+
+	for (i = 0; i < sc->num_rings; i++) {
+		struct ptnet_queue *pq = sc->queues + i;
+		pq->atok->appl_need_kick = onoff;
+	}
+}
+
 static void
 ptnet_tx_intr(void *opaque)
 {
@@ -1324,150 +1326,6 @@ ptnet_rx_intr(void *opaque)
 	ptnet_rx_eof(pq, PTNET_RX_BUDGET, true);
 }
 
-/* The following offloadings-related functions are taken from the vtnet
- * driver, but the same functionality is required for the ptnet driver.
- * As a temporary solution, I copied this code from vtnet and I started
- * to generalize it (taking away driver-specific statistic accounting),
- * making as little modifications as possible.
- * In the future we need to share these functions between vtnet and ptnet.
- */
-static int
-ptnet_tx_offload_ctx(struct mbuf *m, int *etype, int *proto, int *start)
-{
-	struct ether_vlan_header *evh;
-	int offset;
-
-	evh = mtod(m, struct ether_vlan_header *);
-	if (evh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
-		/* BMV: We should handle nested VLAN tags too. */
-		*etype = ntohs(evh->evl_proto);
-		offset = sizeof(struct ether_vlan_header);
-	} else {
-		*etype = ntohs(evh->evl_encap_proto);
-		offset = sizeof(struct ether_header);
-	}
-
-	switch (*etype) {
-#if defined(INET)
-	case ETHERTYPE_IP: {
-		struct ip *ip, iphdr;
-		if (__predict_false(m->m_len < offset + sizeof(struct ip))) {
-			m_copydata(m, offset, sizeof(struct ip),
-			    (caddr_t) &iphdr);
-			ip = &iphdr;
-		} else
-			ip = (struct ip *)(m->m_data + offset);
-		*proto = ip->ip_p;
-		*start = offset + (ip->ip_hl << 2);
-		break;
-	}
-#endif
-#if defined(INET6)
-	case ETHERTYPE_IPV6:
-		*proto = -1;
-		*start = ip6_lasthdr(m, offset, IPPROTO_IPV6, proto);
-		/* Assert the network stack sent us a valid packet. */
-		KASSERT(*start > offset,
-		    ("%s: mbuf %p start %d offset %d proto %d", __func__, m,
-		    *start, offset, *proto));
-		break;
-#endif
-	default:
-		/* Here we should increment the tx_csum_bad_ethtype counter. */
-		return (EINVAL);
-	}
-
-	return (0);
-}
-
-static int
-ptnet_tx_offload_tso(if_t ifp, struct mbuf *m, int eth_type,
-		     int offset, bool allow_ecn, struct virtio_net_hdr *hdr)
-{
-	static struct timeval lastecn;
-	static int curecn;
-	struct tcphdr *tcp, tcphdr;
-
-	if (__predict_false(m->m_len < offset + sizeof(struct tcphdr))) {
-		m_copydata(m, offset, sizeof(struct tcphdr), (caddr_t) &tcphdr);
-		tcp = &tcphdr;
-	} else
-		tcp = (struct tcphdr *)(m->m_data + offset);
-
-	hdr->hdr_len = offset + (tcp->th_off << 2);
-	hdr->gso_size = m->m_pkthdr.tso_segsz;
-	hdr->gso_type = eth_type == ETHERTYPE_IP ? VIRTIO_NET_HDR_GSO_TCPV4 :
-	    VIRTIO_NET_HDR_GSO_TCPV6;
-
-	if (tcp->th_flags & TH_CWR) {
-		/*
-		 * Drop if VIRTIO_NET_F_HOST_ECN was not negotiated. In FreeBSD,
-		 * ECN support is not on a per-interface basis, but globally via
-		 * the net.inet.tcp.ecn.enable sysctl knob. The default is off.
-		 */
-		if (!allow_ecn) {
-			if (ppsratecheck(&lastecn, &curecn, 1))
-				if_printf(ifp,
-				    "TSO with ECN not negotiated with host\n");
-			return (ENOTSUP);
-		}
-		hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN;
-	}
-
-	/* Here we should increment tx_tso counter. */
-
-	return (0);
-}
-
-static struct mbuf *
-ptnet_tx_offload(if_t ifp, struct mbuf *m, bool allow_ecn,
-		 struct virtio_net_hdr *hdr)
-{
-	int flags, etype, csum_start, proto, error;
-
-	flags = m->m_pkthdr.csum_flags;
-
-	error = ptnet_tx_offload_ctx(m, &etype, &proto, &csum_start);
-	if (error)
-		goto drop;
-
-	if ((etype == ETHERTYPE_IP && flags & PTNET_CSUM_OFFLOAD) ||
-	    (etype == ETHERTYPE_IPV6 && flags & PTNET_CSUM_OFFLOAD_IPV6)) {
-		/*
-		 * We could compare the IP protocol vs the CSUM_ flag too,
-		 * but that really should not be necessary.
-		 */
-		hdr->flags |= VIRTIO_NET_HDR_F_NEEDS_CSUM;
-		hdr->csum_start = csum_start;
-		hdr->csum_offset = m->m_pkthdr.csum_data;
-		/* Here we should increment the tx_csum counter. */
-	}
-
-	if (flags & CSUM_TSO) {
-		if (__predict_false(proto != IPPROTO_TCP)) {
-			/* Likely failed to correctly parse the mbuf.
-			 * Here we should increment the tx_tso_not_tcp
-			 * counter. */
-			goto drop;
-		}
-
-		KASSERT(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM,
-		    ("%s: mbuf %p TSO without checksum offload %#x",
-		    __func__, m, flags));
-
-		error = ptnet_tx_offload_tso(ifp, m, etype, csum_start,
-					     allow_ecn, hdr);
-		if (error)
-			goto drop;
-	}
-
-	return (m);
-
-drop:
-	m_freem(m);
-	return (NULL);
-}
-
 static void
 ptnet_vlan_tag_remove(struct mbuf *m)
 {
@@ -1483,183 +1341,13 @@ ptnet_vlan_tag_remove(struct mbuf *m)
 	m_adj(m, ETHER_VLAN_ENCAP_LEN);
 }
 
-/*
- * Use the checksum offset in the VirtIO header to set the
- * correct CSUM_* flags.
- */
-static int
-ptnet_rx_csum_by_offset(struct mbuf *m, uint16_t eth_type, int ip_start,
-			struct virtio_net_hdr *hdr)
-{
-#if defined(INET) || defined(INET6)
-	int offset = hdr->csum_start + hdr->csum_offset;
-#endif
-
-	/* Only do a basic sanity check on the offset. */
-	switch (eth_type) {
-#if defined(INET)
-	case ETHERTYPE_IP:
-		if (__predict_false(offset < ip_start + sizeof(struct ip)))
-			return (1);
-		break;
-#endif
-#if defined(INET6)
-	case ETHERTYPE_IPV6:
-		if (__predict_false(offset < ip_start + sizeof(struct ip6_hdr)))
-			return (1);
-		break;
-#endif
-	default:
-		/* Here we should increment the rx_csum_bad_ethtype counter. */
-		return (1);
-	}
-
-	/*
-	 * Use the offset to determine the appropriate CSUM_* flags. This is
-	 * a bit dirty, but we can get by with it since the checksum offsets
-	 * happen to be different. We assume the host host does not do IPv4
-	 * header checksum offloading.
-	 */
-	switch (hdr->csum_offset) {
-	case offsetof(struct udphdr, uh_sum):
-	case offsetof(struct tcphdr, th_sum):
-		m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
-		m->m_pkthdr.csum_data = 0xFFFF;
-		break;
-	case offsetof(struct sctphdr, checksum):
-		m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
-		break;
-	default:
-		/* Here we should increment the rx_csum_bad_offset counter. */
-		return (1);
-	}
-
-	return (0);
-}
-
-static int
-ptnet_rx_csum_by_parse(struct mbuf *m, uint16_t eth_type, int ip_start,
-		       struct virtio_net_hdr *hdr)
-{
-	int offset, proto;
-
-	switch (eth_type) {
-#if defined(INET)
-	case ETHERTYPE_IP: {
-		struct ip *ip;
-		if (__predict_false(m->m_len < ip_start + sizeof(struct ip)))
-			return (1);
-		ip = (struct ip *)(m->m_data + ip_start);
-		proto = ip->ip_p;
-		offset = ip_start + (ip->ip_hl << 2);
-		break;
-	}
-#endif
-#if defined(INET6)
-	case ETHERTYPE_IPV6:
-		if (__predict_false(m->m_len < ip_start +
-		    sizeof(struct ip6_hdr)))
-			return (1);
-		offset = ip6_lasthdr(m, ip_start, IPPROTO_IPV6, &proto);
-		if (__predict_false(offset < 0))
-			return (1);
-		break;
-#endif
-	default:
-		/* Here we should increment the rx_csum_bad_ethtype counter. */
-		return (1);
-	}
-
-	switch (proto) {
-	case IPPROTO_TCP:
-		if (__predict_false(m->m_len < offset + sizeof(struct tcphdr)))
-			return (1);
-		m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
-		m->m_pkthdr.csum_data = 0xFFFF;
-		break;
-	case IPPROTO_UDP:
-		if (__predict_false(m->m_len < offset + sizeof(struct udphdr)))
-			return (1);
-		m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
-		m->m_pkthdr.csum_data = 0xFFFF;
-		break;
-	case IPPROTO_SCTP:
-		if (__predict_false(m->m_len < offset + sizeof(struct sctphdr)))
-			return (1);
-		m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
-		break;
-	default:
-		/*
-		 * For the remaining protocols, FreeBSD does not support
-		 * checksum offloading, so the checksum will be recomputed.
-		 */
-#if 0
-		if_printf(ifp, "cksum offload of unsupported "
-		    "protocol eth_type=%#x proto=%d csum_start=%d "
-		    "csum_offset=%d\n", __func__, eth_type, proto,
-		    hdr->csum_start, hdr->csum_offset);
-#endif
-		break;
-	}
-
-	return (0);
-}
-
-/*
- * Set the appropriate CSUM_* flags. Unfortunately, the information
- * provided is not directly useful to us. The VirtIO header gives the
- * offset of the checksum, which is all Linux needs, but this is not
- * how FreeBSD does things. We are forced to peek inside the packet
- * a bit.
- *
- * It would be nice if VirtIO gave us the L4 protocol or if FreeBSD
- * could accept the offsets and let the stack figure it out.
- */
-static int
-ptnet_rx_csum(struct mbuf *m, struct virtio_net_hdr *hdr)
-{
-	struct ether_header *eh;
-	struct ether_vlan_header *evh;
-	uint16_t eth_type;
-	int offset, error;
-
-	eh = mtod(m, struct ether_header *);
-	eth_type = ntohs(eh->ether_type);
-	if (eth_type == ETHERTYPE_VLAN) {
-		/* BMV: We should handle nested VLAN tags too. */
-		evh = mtod(m, struct ether_vlan_header *);
-		eth_type = ntohs(evh->evl_proto);
-		offset = sizeof(struct ether_vlan_header);
-	} else
-		offset = sizeof(struct ether_header);
-
-	if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)
-		error = ptnet_rx_csum_by_offset(m, eth_type, offset, hdr);
-	else
-		error = ptnet_rx_csum_by_parse(m, eth_type, offset, hdr);
-
-	return (error);
-}
-/* End of offloading-related functions to be shared with vtnet. */
-
-static inline void
-ptnet_sync_tail(struct ptnet_ring *ptring, struct netmap_kring *kring)
-{
-	struct netmap_ring *ring = kring->ring;
-
-	/* Update hwcur and hwtail as known by the host. */
-        ptnetmap_guest_read_kring_csb(ptring, kring);
-
-	/* nm_sync_finalize */
-	ring->tail = kring->rtail = kring->nr_hwtail;
-}
-
 static void
 ptnet_ring_update(struct ptnet_queue *pq, struct netmap_kring *kring,
 		  unsigned int head, unsigned int sync_flags)
 {
 	struct netmap_ring *ring = kring->ring;
-	struct ptnet_ring *ptring = pq->ptring;
+	struct nm_csb_atok *atok = pq->atok;
+	struct nm_csb_ktoa *ktoa = pq->ktoa;
 
 	/* Some packets have been pushed to the netmap ring. We have
 	 * to tell the host to process the new packets, updating cur
@@ -1669,11 +1357,11 @@ ptnet_ring_update(struct ptnet_queue *pq, struct netmap_kring *kring,
 	/* Mimic nm_txsync_prologue/nm_rxsync_prologue. */
 	kring->rcur = kring->rhead = head;
 
-	ptnetmap_guest_write_kring_csb(ptring, kring->rcur, kring->rhead);
+	nm_sync_kloop_appl_write(atok, kring->rcur, kring->rhead);
 
 	/* Kick the host if needed. */
-	if (NM_ACCESS_ONCE(ptring->host_need_kick)) {
-		ptring->sync_flags = sync_flags;
+	if (NM_ACCESS_ONCE(ktoa->kern_need_kick)) {
+		atok->sync_flags = sync_flags;
 		ptnet_kick(pq);
 	}
 }
@@ -1693,7 +1381,8 @@ ptnet_drain_transmit_queue(struct ptnet_queue *pq, unsigned int budget,
 	struct netmap_adapter *na = &sc->ptna->dr.up;
 	if_t ifp = sc->ifp;
 	unsigned int batch_count = 0;
-	struct ptnet_ring *ptring;
+	struct nm_csb_atok *atok;
+	struct nm_csb_ktoa *ktoa;
 	struct netmap_kring *kring;
 	struct netmap_ring *ring;
 	struct netmap_slot *slot;
@@ -1708,7 +1397,7 @@ ptnet_drain_transmit_queue(struct ptnet_queue *pq, unsigned int budget,
 
 	if (!PTNET_Q_TRYLOCK(pq)) {
 		/* We failed to acquire the lock, schedule the taskqueue. */
-		RD(1, "Deferring TX work");
+		nm_prlim(1, "Deferring TX work");
 		if (may_resched) {
 			taskqueue_enqueue(pq->taskq, &pq->task);
 		}
@@ -1716,14 +1405,15 @@ ptnet_drain_transmit_queue(struct ptnet_queue *pq, unsigned int budget,
 		return 0;
 	}
 
-	if (unlikely(!(ifp->if_drv_flags & IFF_DRV_RUNNING))) {
+	if (unlikely(!(if_getdrvflags(ifp) & IFF_DRV_RUNNING))) {
 		PTNET_Q_UNLOCK(pq);
-		RD(1, "Interface is down");
+		nm_prlim(1, "Interface is down");
 		return ENETDOWN;
 	}
 
-	ptring = pq->ptring;
-	kring = na->tx_rings + pq->kring_id;
+	atok = pq->atok;
+	ktoa = pq->ktoa;
+	kring = na->tx_rings[pq->kring_id];
 	ring = kring->ring;
 	lim = kring->nkr_num_slots - 1;
 	head = ring->head;
@@ -1734,26 +1424,31 @@ ptnet_drain_transmit_queue(struct ptnet_queue *pq, unsigned int budget,
 			/* We ran out of slot, let's see if the host has
 			 * freed up some, by reading hwcur and hwtail from
 			 * the CSB. */
-			ptnet_sync_tail(ptring, kring);
+			ptnet_sync_tail(ktoa, kring);
 
 			if (PTNET_TX_NOSPACE(head, kring, minspace)) {
 				/* Still no slots available. Reactivate the
 				 * interrupts so that we can be notified
 				 * when some free slots are made available by
 				 * the host. */
-				ptring->guest_need_kick = 1;
-
-				/* Double-check. */
-				ptnet_sync_tail(ptring, kring);
+				atok->appl_need_kick = 1;
+
+				/* Double check. We need a full barrier to
+				 * prevent the store to atok->appl_need_kick
+				 * to be reordered with the load from
+				 * ktoa->hwcur and ktoa->hwtail (store-load
+				 * barrier). */
+				nm_stld_barrier();
+				ptnet_sync_tail(ktoa, kring);
 				if (likely(PTNET_TX_NOSPACE(head, kring,
 							    minspace))) {
 					break;
 				}
 
-				RD(1, "Found more slots by doublecheck");
+				nm_prlim(1, "Found more slots by doublecheck");
 				/* More slots were freed before reactivating
 				 * the interrupts. */
-				ptring->guest_need_kick = 0;
+				atok->appl_need_kick = 0;
 			}
 		}
 
@@ -1777,7 +1472,7 @@ ptnet_drain_transmit_queue(struct ptnet_queue *pq, unsigned int budget,
 			 * two 8-bytes-wide writes. */
 			memset(nmbuf, 0, PTNET_HDR_SIZE);
 			if (mhead->m_pkthdr.csum_flags & PTNET_ALL_OFFLOAD) {
-				mhead = ptnet_tx_offload(ifp, mhead, false,
+				mhead = virtio_net_tx_offload(ifp, mhead, false,
 							 vh);
 				if (unlikely(!mhead)) {
 					/* Packet dropped because errors
@@ -1789,7 +1484,7 @@ ptnet_drain_transmit_queue(struct ptnet_queue *pq, unsigned int budget,
 					continue;
 				}
 			}
-			ND(1, "%s: [csum_flags %lX] vnet hdr: flags %x "
+			nm_prdis(1, "%s: [csum_flags %lX] vnet hdr: flags %x "
 			      "csum_start %u csum_ofs %u hdr_len = %u "
 			      "gso_size %u gso_type %x", __func__,
 			      mhead->m_pkthdr.csum_flags, vh->flags,
@@ -1864,7 +1559,7 @@ ptnet_drain_transmit_queue(struct ptnet_queue *pq, unsigned int budget,
 	}
 
 	if (count >= budget && may_resched) {
-		DBG(RD(1, "out of budget: resched, %d mbufs pending\n",
+		DBG(nm_prlim(1, "out of budget: resched, %d mbufs pending\n",
 					drbr_inuse(ifp, pq->bufring)));
 		taskqueue_enqueue(pq->taskq, &pq->task);
 	}
@@ -1906,13 +1601,13 @@ ptnet_transmit(if_t ifp, struct mbuf *m)
 	err = drbr_enqueue(ifp, pq->bufring, m);
 	if (err) {
 		/* ENOBUFS when the bufring is full */
-		RD(1, "%s: drbr_enqueue() failed %d\n",
+		nm_prlim(1, "%s: drbr_enqueue() failed %d\n",
 			__func__, err);
 		pq->stats.errors ++;
 		return err;
 	}
 
-	if (ifp->if_capenable & IFCAP_POLLING) {
+	if (if_getcapenable(ifp) & IFCAP_POLLING) {
 		/* If polling is on, the transmit queues will be
 		 * drained by the poller. */
 		return 0;
@@ -1983,51 +1678,59 @@ ptnet_rx_eof(struct ptnet_queue *pq, unsigned int budget, bool may_resched)
 {
 	struct ptnet_softc *sc = pq->sc;
 	bool have_vnet_hdr = sc->vnet_hdr_len;
-	struct ptnet_ring *ptring = pq->ptring;
+	struct nm_csb_atok *atok = pq->atok;
+	struct nm_csb_ktoa *ktoa = pq->ktoa;
 	struct netmap_adapter *na = &sc->ptna->dr.up;
-	struct netmap_kring *kring = na->rx_rings + pq->kring_id;
+	struct netmap_kring *kring = na->rx_rings[pq->kring_id];
 	struct netmap_ring *ring = kring->ring;
 	unsigned int const lim = kring->nkr_num_slots - 1;
-	unsigned int head = ring->head;
 	unsigned int batch_count = 0;
 	if_t ifp = sc->ifp;
 	unsigned int count = 0;
+	uint32_t head;
 
 	PTNET_Q_LOCK(pq);
 
-	if (unlikely(!(ifp->if_drv_flags & IFF_DRV_RUNNING))) {
+	if (unlikely(!(if_getdrvflags(ifp) & IFF_DRV_RUNNING))) {
 		goto unlock;
 	}
 
 	kring->nr_kflags &= ~NKR_PENDINTR;
 
+	head = ring->head;
 	while (count < budget) {
-		unsigned int prev_head = head;
+		uint32_t prev_head = head;
 		struct mbuf *mhead, *mtail;
 		struct virtio_net_hdr *vh;
 		struct netmap_slot *slot;
 		unsigned int nmbuf_len;
 		uint8_t *nmbuf;
+		int deliver = 1; /* the mbuf to the network stack. */
 host_sync:
 		if (head == ring->tail) {
 			/* We ran out of slot, let's see if the host has
 			 * added some, by reading hwcur and hwtail from
 			 * the CSB. */
-			ptnet_sync_tail(ptring, kring);
+			ptnet_sync_tail(ktoa, kring);
 
 			if (head == ring->tail) {
 				/* Still no slots available. Reactivate
 				 * interrupts as they were disabled by the
 				 * host thread right before issuing the
 				 * last interrupt. */
-				ptring->guest_need_kick = 1;
-
-				/* Double-check. */
-				ptnet_sync_tail(ptring, kring);
+				atok->appl_need_kick = 1;
+
+				/* Double check for more completed RX slots.
+				 * We need a full barrier to prevent the store
+				 * to atok->appl_need_kick to be reordered with
+				 * the load from ktoa->hwcur and ktoa->hwtail
+				 * (store-load barrier). */
+				nm_stld_barrier();
+				ptnet_sync_tail(ktoa, kring);
 				if (likely(head == ring->tail)) {
 					break;
 				}
-				ptring->guest_need_kick = 0;
+				atok->appl_need_kick = 0;
 			}
 		}
 
@@ -2043,12 +1746,13 @@ ptnet_rx_eof(struct ptnet_queue *pq, unsigned int budget, bool may_resched)
 				/* There is no good reason why host should
 				 * put the header in multiple netmap slots.
 				 * If this is the case, discard. */
-				RD(1, "Fragmented vnet-hdr: dropping");
+				nm_prlim(1, "Fragmented vnet-hdr: dropping");
 				head = ptnet_rx_discard(kring, head);
 				pq->stats.iqdrops ++;
+				deliver = 0;
 				goto skip;
 			}
-			ND(1, "%s: vnet hdr: flags %x csum_start %u "
+			nm_prdis(1, "%s: vnet hdr: flags %x csum_start %u "
 			      "csum_ofs %u hdr_len = %u gso_size %u "
 			      "gso_type %x", __func__, vh->flags,
 			      vh->csum_start, vh->csum_offset, vh->hdr_len,
@@ -2112,7 +1816,7 @@ ptnet_rx_eof(struct ptnet_queue *pq, unsigned int budget, bool may_resched)
 				/* The very last slot prepared by the host has
 				 * the NS_MOREFRAG set. Drop it and continue
 				 * the outer cycle (to do the double-check). */
-				RD(1, "Incomplete packet: dropping");
+				nm_prlim(1, "Incomplete packet: dropping");
 				m_freem(mhead);
 				pq->stats.iqdrops ++;
 				goto host_sync;
@@ -2131,7 +1835,7 @@ ptnet_rx_eof(struct ptnet_queue *pq, unsigned int budget, bool may_resched)
 		mhead->m_pkthdr.flowid = pq->kring_id;
 		M_HASHTYPE_SET(mhead, M_HASHTYPE_OPAQUE);
 
-		if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) {
+		if (if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING) {
 			struct ether_header *eh;
 
 			eh = mtod(mhead, struct ether_header *);
@@ -2146,37 +1850,43 @@ ptnet_rx_eof(struct ptnet_queue *pq, unsigned int budget, bool may_resched)
 			}
 		}
 
-		if (have_vnet_hdr && (vh->flags & (VIRTIO_NET_HDR_F_NEEDS_CSUM
-					| VIRTIO_NET_HDR_F_DATA_VALID))) {
-			if (unlikely(ptnet_rx_csum(mhead, vh))) {
-				m_freem(mhead);
-				RD(1, "Csum offload error: dropping");
-				pq->stats.iqdrops ++;
-				goto skip;
-			}
+		if (unlikely(have_vnet_hdr && virtio_net_rx_csum(mhead, vh))) {
+			m_freem(mhead);
+			nm_prlim(1, "Csum offload error: dropping");
+			pq->stats.iqdrops ++;
+			deliver = 0;
 		}
 
-		pq->stats.packets ++;
-		pq->stats.bytes += mhead->m_pkthdr.len;
-
-		PTNET_Q_UNLOCK(pq);
-		(*ifp->if_input)(ifp, mhead);
-		PTNET_Q_LOCK(pq);
-
-		if (unlikely(!(ifp->if_drv_flags & IFF_DRV_RUNNING))) {
-			/* The interface has gone down while we didn't
-			 * have the lock. Stop any processing and exit. */
-			goto unlock;
-		}
 skip:
 		count ++;
-		if (++batch_count == PTNET_RX_BATCH) {
-			/* Some packets have been pushed to the network stack.
-			 * We need to update the CSB to tell the host about the new
-			 * ring->cur and ring->head (RX buffer refill). */
+		if (++batch_count >= PTNET_RX_BATCH) {
+			/* Some packets have been (or will be) pushed to the network
+			 * stack. We need to update the CSB to tell the host about
+			 * the new ring->cur and ring->head (RX buffer refill). */
 			ptnet_ring_update(pq, kring, head, NAF_FORCE_READ);
 			batch_count = 0;
 		}
+
+		if (likely(deliver))  {
+			pq->stats.packets ++;
+			pq->stats.bytes += mhead->m_pkthdr.len;
+
+			PTNET_Q_UNLOCK(pq);
+			if_input(ifp, mhead);
+			PTNET_Q_LOCK(pq);
+			/* The ring->head index (and related indices) are
+			 * updated under pq lock by ptnet_ring_update().
+			 * Since we dropped the lock to call if_input(), we
+			 * must reload ring->head and restart processing the
+			 * ring from there. */
+			head = ring->head;
+
+			if (unlikely(!(if_getdrvflags(ifp) & IFF_DRV_RUNNING))) {
+				/* The interface has gone down while we didn't
+				 * have the lock. Stop any processing and exit. */
+				goto unlock;
+			}
+		}
 	}
 escape:
 	if (batch_count) {
@@ -2187,7 +1897,7 @@ ptnet_rx_eof(struct ptnet_queue *pq, unsigned int budget, bool may_resched)
 	if (count >= budget && may_resched) {
 		/* If we ran out of budget or the double-check found new
 		 * slots to process, schedule the taskqueue. */
-		DBG(RD(1, "out of budget: resched h %u t %u\n",
+		DBG(nm_prlim(1, "out of budget: resched h %u t %u\n",
 					head, ring->tail));
 		taskqueue_enqueue(pq->taskq, &pq->task);
 	}
@@ -2202,7 +1912,7 @@ ptnet_rx_task(void *context, int pending)
 {
 	struct ptnet_queue *pq = context;
 
-	DBG(RD(1, "%s: pq #%u\n", __func__, pq->kring_id));
+	DBG(nm_prlim(1, "%s: pq #%u\n", __func__, pq->kring_id));
 	ptnet_rx_eof(pq, PTNET_RX_BUDGET, true);
 }
 
@@ -2211,7 +1921,7 @@ ptnet_tx_task(void *context, int pending)
 {
 	struct ptnet_queue *pq = context;
 
-	DBG(RD(1, "%s: pq #%u\n", __func__, pq->kring_id));
+	DBG(nm_prlim(1, "%s: pq #%u\n", __func__, pq->kring_id));
 	ptnet_drain_transmit_queue(pq, PTNET_TX_BUDGET, true);
 }
 
@@ -2229,7 +1939,7 @@ ptnet_poll(if_t ifp, enum poll_cmd cmd, int budget)
 
 	KASSERT(sc->num_rings > 0, ("Found no queues in while polling ptnet"));
 	queue_budget = MAX(budget / sc->num_rings, 1);
-	RD(1, "Per-queue budget is %d", queue_budget);
+	nm_prlim(1, "Per-queue budget is %d", queue_budget);
 
 	while (budget) {
 		unsigned int rcnt = 0;
@@ -2274,3 +1984,4 @@ ptnet_poll(if_t ifp, enum poll_cmd cmd, int budget)
 	return count;
 }
 #endif /* DEVICE_POLLING */
+#endif /* WITH_PTNETMAP */
diff --git a/sys/dev/netmap/if_re_netmap.h b/sys/dev/netmap/if_re_netmap.h
index 28971cb7d..4864f3b50 100644
--- a/sys/dev/netmap/if_re_netmap.h
+++ b/sys/dev/netmap/if_re_netmap.h
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2011-2014 Luigi Rizzo. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,8 +26,6 @@
  */
 
 /*
- * $FreeBSD: head/sys/dev/netmap/if_re_netmap.h 234225 2012-04-13 15:33:12Z luigi $
- *
  * netmap support for: re
  *
  * For more details on netmap support please see ixgbe_netmap.h
@@ -45,8 +45,8 @@
 static int
 re_netmap_reg(struct netmap_adapter *na, int onoff)
 {
-	struct ifnet *ifp = na->ifp;
-	struct rl_softc *adapter = ifp->if_softc;
+	if_t ifp = na->ifp;
+	struct rl_softc *adapter = if_getsoftc(ifp);
 
 	RL_LOCK(adapter);
 	re_stop(adapter); /* also clears IFF_DRV_RUNNING */
@@ -57,7 +57,7 @@ re_netmap_reg(struct netmap_adapter *na, int onoff)
 	}
 	re_init_locked(adapter);	/* also enables intr */
 	RL_UNLOCK(adapter);
-	return (ifp->if_drv_flags & IFF_DRV_RUNNING ? 0 : 1);
+	return (if_getdrvflags(ifp) & IFF_DRV_RUNNING ? 0 : 1);
 }
 
 
@@ -68,7 +68,7 @@ static int
 re_netmap_txsync(struct netmap_kring *kring, int flags)
 {
 	struct netmap_adapter *na = kring->na;
-	struct ifnet *ifp = na->ifp;
+	if_t ifp = na->ifp;
 	struct netmap_ring *ring = kring->ring;
 	u_int nm_i;	/* index into the netmap ring */
 	u_int nic_i;	/* index into the NIC ring */
@@ -77,7 +77,7 @@ re_netmap_txsync(struct netmap_kring *kring, int flags)
 	u_int const head = kring->rhead;
 
 	/* device-specific */
-	struct rl_softc *sc = ifp->if_softc;
+	struct rl_softc *sc = if_getsoftc(ifp);
 	struct rl_txdesc *txd = sc->rl_ldata.rl_tx_desc;
 
 	bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag,
@@ -170,17 +170,16 @@ static int
 re_netmap_rxsync(struct netmap_kring *kring, int flags)
 {
 	struct netmap_adapter *na = kring->na;
-	struct ifnet *ifp = na->ifp;
+	if_t ifp = na->ifp;
 	struct netmap_ring *ring = kring->ring;
 	u_int nm_i;	/* index into the netmap ring */
 	u_int nic_i;	/* index into the NIC ring */
-	u_int n;
 	u_int const lim = kring->nkr_num_slots - 1;
 	u_int const head = kring->rhead;
 	int force_update = (flags & NAF_FORCE_READ) || kring->nr_kflags & NKR_PENDINTR;
 
 	/* device-specific */
-	struct rl_softc *sc = ifp->if_softc;
+	struct rl_softc *sc = if_getsoftc(ifp);
 	struct rl_rxdesc *rxd = sc->rl_ldata.rl_rx_desc;
 
 	if (head > lim)
@@ -199,7 +198,6 @@ re_netmap_rxsync(struct netmap_kring *kring, int flags)
 	 * is to stop right before nm_hwcur.
 	 */
 	if (netmap_no_pendintr || force_update) {
-		uint16_t slot_flags = kring->nkr_slot_flags;
 		uint32_t stop_i = nm_prev(kring->nr_hwcur, lim);
 
 		nic_i = sc->rl_ldata.rl_rx_prodidx; /* next pkt to check */
@@ -216,7 +214,7 @@ re_netmap_rxsync(struct netmap_kring *kring, int flags)
 			/* XXX subtract crc */
 			total_len = (total_len < 4) ? 0 : total_len - 4;
 			ring->slot[nm_i].len = total_len;
-			ring->slot[nm_i].flags = slot_flags;
+			ring->slot[nm_i].flags = 0;
 			/*  sync was in re_newbuf() */
 			bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag,
 			    rxd[nic_i].rx_dmamap, BUS_DMASYNC_POSTREAD);
@@ -235,7 +233,7 @@ re_netmap_rxsync(struct netmap_kring *kring, int flags)
 	nm_i = kring->nr_hwcur;
 	if (nm_i != head) {
 		nic_i = netmap_idx_k2n(kring, nm_i);
-		for (n = 0; nm_i != head; n++) {
+		while (nm_i != head) {
 			struct netmap_slot *slot = &ring->slot[nm_i];
 			uint64_t paddr;
 			void *addr = PNMB(na, slot, &paddr);
@@ -303,7 +301,7 @@ re_netmap_tx_init(struct rl_softc *sc)
 	/* l points in the netmap ring, i points in the NIC ring */
 	for (i = 0; i < n; i++) {
 		uint64_t paddr;
-		int l = netmap_idx_n2k(&na->tx_rings[0], i);
+		int l = netmap_idx_n2k(na->tx_rings[0], i);
 		void *addr = PNMB(na, slot + l, &paddr);
 
 		desc[i].rl_bufaddr_lo = htole32(RL_ADDR_LO(paddr));
@@ -329,11 +327,11 @@ re_netmap_rx_init(struct rl_softc *sc)
 	 * Do not release the slots owned by userspace,
 	 * and also keep one empty.
 	 */
-	max_avail = n - 1 - nm_kr_rxspace(&na->rx_rings[0]);
+	max_avail = n - 1 - nm_kr_rxspace(na->rx_rings[0]);
 	for (nic_i = 0; nic_i < n; nic_i++) {
 		void *addr;
 		uint64_t paddr;
-		uint32_t nm_i = netmap_idx_n2k(&na->rx_rings[0], nic_i);
+		uint32_t nm_i = netmap_idx_n2k(na->rx_rings[0], nic_i);
 
 		addr = PNMB(na, slot + nm_i, &paddr);
 
diff --git a/sys/dev/netmap/if_vtnet_netmap.h b/sys/dev/netmap/if_vtnet_netmap.h
index 4d8d9e367..4eafff5ed 100644
--- a/sys/dev/netmap/if_vtnet_netmap.h
+++ b/sys/dev/netmap/if_vtnet_netmap.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Vincenzo Maffione, Luigi Rizzo. All rights reserved.
+ * Copyright (C) 2014-2018 Vincenzo Maffione, Luigi Rizzo.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -23,84 +23,30 @@
  * SUCH DAMAGE.
  */
 
-/*
- * $FreeBSD: head/sys/dev/netmap/if_vtnet_netmap.h 270097 2014-08-17 10:25:27Z luigi $
- */
-
 #include 
 #include 
 #include 
 #include     /* vtophys ? */
 #include 
 
-
-#define SOFTC_T	vtnet_softc
-
-/* Free all the unused buffer in all the RX virtqueues.
- * This function is called when entering and exiting netmap mode.
- * - buffers queued by the virtio driver return skbuf/mbuf pointer
- *   and need to be freed;
- * - buffers queued by netmap return the txq/rxq, and do not need work
- */
-static void
-vtnet_netmap_free_bufs(struct SOFTC_T* sc)
-{
-	int i, nmb = 0, n = 0, last;
-
-	for (i = 0; i < sc->vtnet_max_vq_pairs; i++) {
-		struct vtnet_rxq *rxq = &sc->vtnet_rxqs[i];
-		struct virtqueue *vq;
-		struct mbuf *m;
-		struct vtnet_txq *txq = &sc->vtnet_txqs[i];
-                struct vtnet_tx_header *txhdr;
-
-		last = 0;
-		vq = rxq->vtnrx_vq;
-		while ((m = virtqueue_drain(vq, &last)) != NULL) {
-			n++;
-			if (m != (void *)rxq)
-				m_freem(m);
-			else
-				nmb++;
-		}
-
-		last = 0;
-		vq = txq->vtntx_vq;
-		while ((txhdr = virtqueue_drain(vq, &last)) != NULL) {
-			n++;
-			if (txhdr != (void *)txq) {
-				m_freem(txhdr->vth_mbuf);
-				uma_zfree(vtnet_tx_header_zone, txhdr);
-			} else
-				nmb++;
-		}
-	}
-	D("freed %d mbufs, %d netmap bufs on %d queues",
-		n - nmb, nmb, i);
-}
-
 /* Register and unregister. */
 static int
-vtnet_netmap_reg(struct netmap_adapter *na, int onoff)
+vtnet_netmap_reg(struct netmap_adapter *na, int state)
 {
-        struct ifnet *ifp = na->ifp;
-	struct SOFTC_T *sc = ifp->if_softc;
+	if_t ifp = na->ifp;
+	struct vtnet_softc *sc = if_getsoftc(ifp);
 
-	VTNET_CORE_LOCK(sc);
-	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
-	/* enable or disable flags and callbacks in na and ifp */
-	if (onoff) {
-		nm_set_native_flags(na);
-	} else {
-		nm_clear_native_flags(na);
-	}
-	/* drain queues so netmap and native drivers
-	 * do not interfere with each other
+	/*
+	 * Trigger a device reinit, asking vtnet_init_locked() to
+	 * also enter or exit netmap mode.
 	 */
-	vtnet_netmap_free_bufs(sc);
-        vtnet_init_locked(sc);       /* also enable intr */
-        VTNET_CORE_UNLOCK(sc);
-        return (ifp->if_drv_flags & IFF_DRV_RUNNING ? 0 : 1);
+	VTNET_CORE_LOCK(sc);
+	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
+	vtnet_init_locked(sc, state ? VTNET_INIT_NETMAP_ENTER
+	    : VTNET_INIT_NETMAP_EXIT);
+	VTNET_CORE_UNLOCK(sc);
+
+	return (0);
 }
 
 
@@ -109,126 +55,109 @@ static int
 vtnet_netmap_txsync(struct netmap_kring *kring, int flags)
 {
 	struct netmap_adapter *na = kring->na;
-        struct ifnet *ifp = na->ifp;
+	if_t ifp = na->ifp;
 	struct netmap_ring *ring = kring->ring;
 	u_int ring_nr = kring->ring_id;
 	u_int nm_i;	/* index into the netmap ring */
-	u_int nic_i;	/* index into the NIC ring */
-	u_int n;
 	u_int const lim = kring->nkr_num_slots - 1;
 	u_int const head = kring->rhead;
 
 	/* device-specific */
-	struct SOFTC_T *sc = ifp->if_softc;
+	struct vtnet_softc *sc = if_getsoftc(ifp);
 	struct vtnet_txq *txq = &sc->vtnet_txqs[ring_nr];
 	struct virtqueue *vq = txq->vtntx_vq;
+	int interrupts = !(kring->nr_kflags & NKR_NOINTR);
+	u_int n;
 
 	/*
 	 * First part: process new packets to send.
 	 */
-	rmb();
 
 	nm_i = kring->nr_hwcur;
 	if (nm_i != head) {	/* we have new packets to send */
 		struct sglist *sg = txq->vtntx_sg;
 
-		nic_i = netmap_idx_k2n(kring, nm_i);
-		for (n = 0; nm_i != head; n++) {
+		for (; nm_i != head; nm_i = nm_next(nm_i, lim)) {
 			/* we use an empty header here */
-			static struct virtio_net_hdr_mrg_rxbuf hdr;
 			struct netmap_slot *slot = &ring->slot[nm_i];
+			uint64_t offset = nm_get_offset(kring, slot);
 			u_int len = slot->len;
 			uint64_t paddr;
-			void *addr = PNMB(na, slot, &paddr);
-                        int err;
+			int err;
 
-			NM_CHECK_ADDR_LEN(na, addr, len);
+			(void)PNMB(na, slot, &paddr);
+			NM_CHECK_ADDR_LEN_OFF(na, len, offset);
 
 			slot->flags &= ~(NS_REPORT | NS_BUF_CHANGED);
 			/* Initialize the scatterlist, expose it to the hypervisor,
 			 * and kick the hypervisor (if necessary).
 			 */
 			sglist_reset(sg); // cheap
-			// if vtnet_hdr_size > 0 ...
-			err = sglist_append(sg, &hdr, sc->vtnet_hdr_size);
-			// XXX later, support multi segment
-			err = sglist_append_phys(sg, paddr, len);
-			/* use na as the cookie */
-                        err = virtqueue_enqueue(vq, txq, sg, sg->sg_nseg, 0);
-                        if (unlikely(err < 0)) {
-                                D("virtqueue_enqueue failed");
-                                break;
-                        }
-
-			nm_i = nm_next(nm_i, lim);
-			nic_i = nm_next(nic_i, lim);
+			err = sglist_append(sg, &txq->vtntx_shrhdr, sc->vtnet_hdr_size);
+			err |= sglist_append_phys(sg, paddr + offset, len);
+			KASSERT(err == 0, ("%s: cannot append to sglist %d",
+						__func__, err));
+			err = virtqueue_enqueue(vq, /*cookie=*/txq, sg,
+						/*readable=*/sg->sg_nseg,
+						/*writeable=*/0);
+			if (unlikely(err)) {
+				if (err != ENOSPC)
+					nm_prerr("virtqueue_enqueue(%s) failed: %d",
+							kring->name, err);
+				break;
+			}
 		}
-		/* Update hwcur depending on where we stopped. */
-		kring->nr_hwcur = nm_i; /* note we migth break early */
-
-		/* No more free TX slots? Ask the hypervisor for notifications,
-		 * possibly only when a considerable amount of work has been
-		 * done.
-		 */
-		ND(3,"sent %d packets, hwcur %d", n, nm_i);
-		virtqueue_disable_intr(vq);
-		virtqueue_notify(vq);
-	} else {
-		if (ring->head != ring->tail)
-		    ND(5, "pure notify ? head %d tail %d nused %d %d",
-			ring->head, ring->tail, virtqueue_nused(vq),
-			(virtqueue_dump(vq), 1));
+
 		virtqueue_notify(vq);
-		virtqueue_enable_intr(vq); // like postpone with 0
-	}
 
+		/* Update hwcur depending on where we stopped. */
+		kring->nr_hwcur = nm_i; /* note we might break early */
+	}
 
-        /* Free used slots. We only consider our own used buffers, recognized
-	 * by the token we passed to virtqueue_add_outbuf.
+	/* Free used slots. We only consider our own used buffers, recognized
+	 * by the token we passed to virtqueue_enqueue.
 	 */
-        n = 0;
-        for (;;) {
-                struct vtnet_tx_header *txhdr = virtqueue_dequeue(vq, NULL);
-                if (txhdr == NULL)
-                        break;
-                if (likely(txhdr == (void *)txq)) {
-                        n++;
-			if (virtqueue_nused(vq) < 32) { // XXX slow release
-				break;
-			}
-		} else { /* leftover from previous transmission */
-			m_freem(txhdr->vth_mbuf);
-			uma_zfree(vtnet_tx_header_zone, txhdr);
-		}
-        }
-	if (n) {
+	n = 0;
+	for (;;) {
+		void *token = virtqueue_dequeue(vq, NULL);
+		if (token == NULL)
+			break;
+		if (unlikely(token != (void *)txq))
+			nm_prerr("BUG: TX token mismatch");
+		else
+			n++;
+	}
+	if (n > 0) {
 		kring->nr_hwtail += n;
 		if (kring->nr_hwtail > lim)
 			kring->nr_hwtail -= lim + 1;
 	}
-	if (nm_i != kring->nr_hwtail /* && vtnet_txq_below_threshold(txq) == 0*/) {
-		ND(3, "disable intr, hwcur %d", nm_i);
-		virtqueue_disable_intr(vq);
-	} else {
-		ND(3, "enable intr, hwcur %d", nm_i);
-		virtqueue_postpone_intr(vq, VQ_POSTPONE_SHORT);
-	}
 
-        return 0;
+	if (interrupts && virtqueue_nfree(vq) < 32)
+		virtqueue_postpone_intr(vq, VQ_POSTPONE_LONG);
+
+	return 0;
 }
 
+/*
+ * Publish 'num 'netmap receive buffers to the host, starting
+ * from the next available one (rx->vtnrx_nm_refill).
+ * Return a positive error code on error, and 0 on success.
+ * If we could not publish all of the buffers that's an error,
+ * since the netmap ring and the virtqueue would go out of sync.
+ */
 static int
-vtnet_refill_rxq(struct netmap_kring *kring, u_int nm_i, u_int head)
+vtnet_netmap_kring_refill(struct netmap_kring *kring, u_int num)
 {
 	struct netmap_adapter *na = kring->na;
-        struct ifnet *ifp = na->ifp;
+	if_t ifp = na->ifp;
 	struct netmap_ring *ring = kring->ring;
 	u_int ring_nr = kring->ring_id;
 	u_int const lim = kring->nkr_num_slots - 1;
-	u_int n;
+	u_int nm_i;
 
 	/* device-specific */
-	struct SOFTC_T *sc = ifp->if_softc;
+	struct vtnet_softc *sc = if_getsoftc(ifp);
 	struct vtnet_rxq *rxq = &sc->vtnet_rxqs[ring_nr];
 	struct virtqueue *vq = rxq->vtnrx_vq;
 
@@ -236,31 +165,75 @@ vtnet_refill_rxq(struct netmap_kring *kring, u_int nm_i, u_int head)
 	struct sglist_seg ss[2];
 	struct sglist sg = { ss, 0, 0, 2 };
 
-	for (n = 0; nm_i != head; n++) {
-		static struct virtio_net_hdr_mrg_rxbuf hdr;
+	for (nm_i = rxq->vtnrx_nm_refill; num > 0;
+	    nm_i = nm_next(nm_i, lim), num--) {
 		struct netmap_slot *slot = &ring->slot[nm_i];
+		uint64_t offset = nm_get_offset(kring, slot);
 		uint64_t paddr;
 		void *addr = PNMB(na, slot, &paddr);
-		int err = 0;
+		int err;
 
 		if (addr == NETMAP_BUF_BASE(na)) { /* bad buf */
-			if (netmap_ring_reinit(kring))
-				return -1;
+			netmap_ring_reinit(kring);
+			return EFAULT;
 		}
 
 		slot->flags &= ~NS_BUF_CHANGED;
-		sglist_reset(&sg); // cheap
-		err = sglist_append(&sg, &hdr, sc->vtnet_hdr_size);
-		err = sglist_append_phys(&sg, paddr, NETMAP_BUF_SIZE(na));
+		sglist_reset(&sg);
+		err = sglist_append(&sg, &rxq->vtnrx_shrhdr, sc->vtnet_hdr_size);
+		err |= sglist_append_phys(&sg, paddr + offset,
+		    NETMAP_BUF_SIZE(na) - offset);
+		KASSERT(err == 0, ("%s: cannot append to sglist %d",
+					__func__, err));
 		/* writable for the host */
-		err = virtqueue_enqueue(vq, rxq, &sg, 0, sg.sg_nseg);
-		if (err < 0) {
-			D("virtqueue_enqueue failed");
+		err = virtqueue_enqueue(vq, /*cookie=*/rxq, &sg,
+				/*readable=*/0, /*writeable=*/sg.sg_nseg);
+		if (unlikely(err)) {
+			nm_prerr("virtqueue_enqueue(%s) failed: %d",
+				kring->name, err);
 			break;
 		}
-		nm_i = nm_next(nm_i, lim);
 	}
-	return nm_i;
+	rxq->vtnrx_nm_refill = nm_i;
+
+	return num == 0 ? 0 : ENOSPC;
+}
+
+/*
+ * Publish netmap buffers on a RX virtqueue.
+ * Returns -1 if this virtqueue is not being opened in netmap mode.
+ * If the virtqueue is being opened in netmap mode, return 0 on success and
+ * a positive error code on failure.
+ */
+static int
+vtnet_netmap_rxq_populate(struct vtnet_rxq *rxq)
+{
+	struct netmap_adapter *na = NA(rxq->vtnrx_sc->vtnet_ifp);
+	struct netmap_kring *kring;
+	struct netmap_slot *slot;
+	int error;
+	int num;
+
+	slot = netmap_reset(na, NR_RX, rxq->vtnrx_id, 0);
+	if (slot == NULL)
+		return -1;
+	kring = na->rx_rings[rxq->vtnrx_id];
+
+	/*
+	 * Expose all the RX netmap buffers we can. In case of no indirect
+	 * buffers, the number of netmap slots in the RX ring matches the
+	 * maximum number of 2-elements sglist that the RX virtqueue can
+	 * accommodate. We need to start from kring->nr_hwtail, which is 0
+	 * on the first netmap register and may be different from 0 if a
+	 * virtio re-init (caused by a netma register or i.e., ifconfig)
+	 * happens while the device is in use by netmap.
+	 */
+	rxq->vtnrx_nm_refill = kring->nr_hwtail;
+	num = na->num_rx_desc - 1 - nm_kr_rxspace(kring);
+	error = vtnet_netmap_kring_refill(kring, num);
+	virtqueue_notify(rxq->vtnrx_vq);
+
+	return error;
 }
 
 /* Reconcile kernel and user view of the receive ring. */
@@ -268,160 +241,210 @@ static int
 vtnet_netmap_rxsync(struct netmap_kring *kring, int flags)
 {
 	struct netmap_adapter *na = kring->na;
-        struct ifnet *ifp = na->ifp;
+	if_t ifp = na->ifp;
 	struct netmap_ring *ring = kring->ring;
 	u_int ring_nr = kring->ring_id;
 	u_int nm_i;	/* index into the netmap ring */
-	// u_int nic_i;	/* index into the NIC ring */
-	u_int n;
 	u_int const lim = kring->nkr_num_slots - 1;
 	u_int const head = kring->rhead;
-	int force_update = (flags & NAF_FORCE_READ) || kring->nr_kflags & NKR_PENDINTR;
+	int force_update = (flags & NAF_FORCE_READ) ||
+				(kring->nr_kflags & NKR_PENDINTR);
+	int interrupts = !(kring->nr_kflags & NKR_NOINTR);
 
 	/* device-specific */
-	struct SOFTC_T *sc = ifp->if_softc;
+	struct vtnet_softc *sc = if_getsoftc(ifp);
 	struct vtnet_rxq *rxq = &sc->vtnet_rxqs[ring_nr];
 	struct virtqueue *vq = rxq->vtnrx_vq;
 
-	/* XXX netif_carrier_ok ? */
-
-	if (head > lim)
-		return netmap_ring_reinit(kring);
-
-	rmb();
 	/*
 	 * First part: import newly received packets.
-	 * Only accept our
-	 * own buffers (matching the token). We should only get
-	 * matching buffers, because of vtnet_netmap_free_rx_unused_bufs()
-	 * and vtnet_netmap_init_buffers().
+	 * Only accept our own buffers (matching the token). We should only get
+	 * matching buffers. The hwtail should never overrun hwcur, because
+	 * we publish only N-1 receive buffers (and not N).
+	 * In any case we must not leave this routine with the interrupts
+	 * disabled, pending packets in the VQ and hwtail == (hwcur - 1),
+	 * otherwise the pending packets could stall.
 	 */
 	if (netmap_no_pendintr || force_update) {
-		uint16_t slot_flags = kring->nkr_slot_flags;
-                struct netmap_adapter *token;
+		uint32_t hwtail_lim = nm_prev(kring->nr_hwcur, lim);
+		void *token;
 
-                nm_i = kring->nr_hwtail;
-                n = 0;
+		vtnet_rxq_disable_intr(rxq);
+
+		nm_i = kring->nr_hwtail;
 		for (;;) {
 			int len;
-                        token = virtqueue_dequeue(vq, &len);
-                        if (token == NULL)
-                                break;
-                        if (likely(token == (void *)rxq)) {
-                            ring->slot[nm_i].len = len;
-                            ring->slot[nm_i].flags = slot_flags;
-                            nm_i = nm_next(nm_i, lim);
-                            n++;
-                        } else {
-			    D("This should not happen");
-                        }
+			token = virtqueue_dequeue(vq, &len);
+			if (token == NULL) {
+				/*
+				 * Enable the interrupts again and double-check
+				 * for more work. We can go on until we win the
+				 * race condition, since we are not replenishing
+				 * in the meanwhile, and thus we will process at
+				 * most N-1 slots.
+				 */
+				if (interrupts && vtnet_rxq_enable_intr(rxq)) {
+					vtnet_rxq_disable_intr(rxq);
+					continue;
+				}
+				break;
+			}
+			if (unlikely(token != (void *)rxq)) {
+				nm_prerr("BUG: RX token mismatch");
+			} else {
+				if (nm_i == hwtail_lim) {
+					KASSERT(false, ("hwtail would "
+					    "overrun hwcur"));
+				}
+
+				/* Skip the virtio-net header. */
+				len -= sc->vtnet_hdr_size;
+				if (unlikely(len < 0)) {
+					nm_prlim(1, "Truncated virtio-net-header, "
+						"missing %d bytes", -len);
+					len = 0;
+				}
+				ring->slot[nm_i].len = len;
+				ring->slot[nm_i].flags = 0;
+				nm_i = nm_next(nm_i, lim);
+			}
 		}
 		kring->nr_hwtail = nm_i;
 		kring->nr_kflags &= ~NKR_PENDINTR;
 	}
-        ND("[B] h %d c %d hwcur %d hwtail %d",
-		ring->head, ring->cur, kring->nr_hwcur,
-			      kring->nr_hwtail);
 
 	/*
 	 * Second part: skip past packets that userspace has released.
 	 */
 	nm_i = kring->nr_hwcur; /* netmap ring index */
 	if (nm_i != head) {
-		int err = vtnet_refill_rxq(kring, nm_i, head);
-		if (err < 0)
-			return 1;
-		kring->nr_hwcur = err;
+		int released;
+		int error;
+
+		released = head - nm_i;
+		if (released < 0)
+			released += kring->nkr_num_slots;
+		error = vtnet_netmap_kring_refill(kring, released);
+		if (error) {
+			nm_prerr("Failed to replenish RX VQ with %u sgs",
+			    released);
+			return error;
+		}
+		kring->nr_hwcur = head;
 		virtqueue_notify(vq);
-		/* After draining the queue may need an intr from the hypervisor */
-        	vtnet_rxq_enable_intr(rxq);
 	}
 
-        ND("[C] h %d c %d t %d hwcur %d hwtail %d",
-		ring->head, ring->cur, ring->tail,
-		kring->nr_hwcur, kring->nr_hwtail);
+	nm_prdis("h %d c %d t %d hwcur %d hwtail %d", kring->rhead,
+	    kring->rcur, kring->rtail, kring->nr_hwcur, kring->nr_hwtail);
 
 	return 0;
 }
 
 
-/* Make RX virtqueues buffers pointing to netmap buffers. */
-static int
-vtnet_netmap_init_rx_buffers(struct SOFTC_T *sc)
+/* Enable/disable interrupts on all virtqueues. */
+static void
+vtnet_netmap_intr(struct netmap_adapter *na, int state)
 {
-	struct ifnet *ifp = sc->vtnet_ifp;
-	struct netmap_adapter* na = NA(ifp);
-	unsigned int r;
-
-	if (!nm_native_on(na))
-		return 0;
-	for (r = 0; r < na->num_rx_rings; r++) {
-                struct netmap_kring *kring = &na->rx_rings[r];
-		struct vtnet_rxq *rxq = &sc->vtnet_rxqs[r];
-		struct virtqueue *vq = rxq->vtnrx_vq;
-	        struct netmap_slot* slot;
-		int err = 0;
-
-		slot = netmap_reset(na, NR_RX, r, 0);
-		if (!slot) {
-			D("strange, null netmap ring %d", r);
-			return 0;
+	struct vtnet_softc *sc = if_getsoftc(na->ifp);
+	int i;
+
+	for (i = 0; i < sc->vtnet_max_vq_pairs; i++) {
+		struct vtnet_rxq *rxq = &sc->vtnet_rxqs[i];
+		struct vtnet_txq *txq = &sc->vtnet_txqs[i];
+		struct virtqueue *txvq = txq->vtntx_vq;
+
+		if (state) {
+			vtnet_rxq_enable_intr(rxq);
+			virtqueue_enable_intr(txvq);
+		} else {
+			vtnet_rxq_disable_intr(rxq);
+			virtqueue_disable_intr(txvq);
 		}
-		/* Add up to na>-num_rx_desc-1 buffers to this RX virtqueue.
-		 * It's important to leave one virtqueue slot free, otherwise
-		 * we can run into ring->cur/ring->tail wraparounds.
-		 */
-		err = vtnet_refill_rxq(kring, 0, na->num_rx_desc-1);
-		if (err < 0)
-			return 0;
-		virtqueue_notify(vq);
 	}
+}
+
+static int
+vtnet_netmap_tx_slots(struct vtnet_softc *sc)
+{
+	int div;
+
+	/* We need to prepend a virtio-net header to each netmap buffer to be
+	 * transmitted, therefore calling virtqueue_enqueue() passing sglist
+	 * with 2 elements.
+	 * TX virtqueues use indirect descriptors if the feature was negotiated
+	 * with the host, and if sc->vtnet_tx_nsegs > 1. With indirect
+	 * descriptors, a single virtio descriptor is sufficient to reference
+	 * each TX sglist. Without them, we need two separate virtio descriptors
+	 * for each TX sglist. We therefore compute the number of netmap TX
+	 * slots according to these assumptions.
+	 */
+	if ((sc->vtnet_flags & VTNET_FLAG_INDIRECT) && sc->vtnet_tx_nsegs > 1)
+		div = 1;
+	else
+		div = 2;
 
-	return 1;
+	return virtqueue_size(sc->vtnet_txqs[0].vtntx_vq) / div;
 }
 
-/* Update the virtio-net device configurations. Number of queues can
- * change dinamically, by 'ethtool --set-channels $IFNAME combined $N'.
- * This is actually the only way virtio-net can currently enable
- * the multiqueue mode.
- * XXX note that we seem to lose packets if the netmap ring has more
- * slots than the queue
- */
 static int
-vtnet_netmap_config(struct netmap_adapter *na, u_int *txr, u_int *txd,
-						u_int *rxr, u_int *rxd)
+vtnet_netmap_rx_slots(struct vtnet_softc *sc)
 {
-	struct ifnet *ifp = na->ifp;
-	struct SOFTC_T *sc = ifp->if_softc;
+	int div;
+
+	/* We need to prepend a virtio-net header to each netmap buffer to be
+	 * received, therefore calling virtqueue_enqueue() passing sglist
+	 * with 2 elements.
+	 * RX virtqueues use indirect descriptors if the feature was negotiated
+	 * with the host, and if sc->vtnet_rx_nsegs > 1. With indirect
+	 * descriptors, a single virtio descriptor is sufficient to reference
+	 * each RX sglist. Without them, we need two separate virtio descriptors
+	 * for each RX sglist. We therefore compute the number of netmap RX
+	 * slots according to these assumptions.
+	 */
+	if ((sc->vtnet_flags & VTNET_FLAG_INDIRECT) && sc->vtnet_rx_nsegs > 1)
+		div = 1;
+	else
+		div = 2;
 
-	*txr = *rxr = sc->vtnet_max_vq_pairs;
-	*rxd = 512; // sc->vtnet_rx_nmbufs;
-	*txd = *rxd; // XXX
-        D("vtnet config txq=%d, txd=%d rxq=%d, rxd=%d",
-					*txr, *txd, *rxr, *rxd);
+	return virtqueue_size(sc->vtnet_rxqs[0].vtnrx_vq) / div;
+}
+
+static int
+vtnet_netmap_config(struct netmap_adapter *na, struct nm_config_info *info)
+{
+	struct vtnet_softc *sc = if_getsoftc(na->ifp);
+
+	info->num_tx_rings = sc->vtnet_act_vq_pairs;
+	info->num_rx_rings = sc->vtnet_act_vq_pairs;
+	info->num_tx_descs = vtnet_netmap_tx_slots(sc);
+	info->num_rx_descs = vtnet_netmap_rx_slots(sc);
+	info->rx_buf_maxsize = NETMAP_BUF_SIZE(na);
 
 	return 0;
 }
 
 static void
-vtnet_netmap_attach(struct SOFTC_T *sc)
+vtnet_netmap_attach(struct vtnet_softc *sc)
 {
 	struct netmap_adapter na;
 
 	bzero(&na, sizeof(na));
 
 	na.ifp = sc->vtnet_ifp;
-	na.num_tx_desc =  1024;// sc->vtnet_rx_nmbufs;
-	na.num_rx_desc =  1024; // sc->vtnet_rx_nmbufs;
+	na.na_flags = NAF_OFFSETS;
+	na.num_tx_desc = vtnet_netmap_tx_slots(sc);
+	na.num_rx_desc = vtnet_netmap_rx_slots(sc);
+	na.num_tx_rings = na.num_rx_rings = sc->vtnet_max_vq_pairs;
+	na.rx_buf_maxsize = 0;
 	na.nm_register = vtnet_netmap_reg;
 	na.nm_txsync = vtnet_netmap_txsync;
 	na.nm_rxsync = vtnet_netmap_rxsync;
+	na.nm_intr = vtnet_netmap_intr;
 	na.nm_config = vtnet_netmap_config;
-	na.num_tx_rings = na.num_rx_rings = sc->vtnet_max_vq_pairs;
-	D("max rings %d", sc->vtnet_max_vq_pairs);
+
 	netmap_attach(&na);
 
-        D("virtio attached txq=%d, txd=%d rxq=%d, rxd=%d",
+	nm_prinf("vtnet attached txq=%d, txd=%d rxq=%d, rxd=%d",
 			na.num_tx_rings, na.num_tx_desc,
 			na.num_tx_rings, na.num_rx_desc);
 }
diff --git a/sys/dev/netmap/ixgbe_netmap.h b/sys/dev/netmap/ixgbe_netmap.h
index ddfed4a44..c7ff503ed 100644
--- a/sys/dev/netmap/ixgbe_netmap.h
+++ b/sys/dev/netmap/ixgbe_netmap.h
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,8 +26,6 @@
  */
 
 /*
- * $FreeBSD: head/sys/dev/netmap/ixgbe_netmap.h 244514 2012-12-20 22:26:03Z luigi $
- *
  * netmap support for: ixgbe (both ix and ixv)
  *
  * This file is meant to be a reference on how to implement
@@ -88,7 +88,7 @@ set_crcstrip(struct ixgbe_hw *hw, int onoff)
 	hl = IXGBE_READ_REG(hw, IXGBE_HLREG0);
 	rxc = IXGBE_READ_REG(hw, IXGBE_RDRXCTL);
 	if (netmap_verbose)
-		D("%s read  HLREG 0x%x rxc 0x%x",
+		nm_prinf("%s read  HLREG 0x%x rxc 0x%x",
 			onoff ? "enter" : "exit", hl, rxc);
 	/* hw requirements ... */
 	rxc &= ~IXGBE_RDRXCTL_RSCFRSTSIZE;
@@ -103,7 +103,7 @@ set_crcstrip(struct ixgbe_hw *hw, int onoff)
 		rxc |= IXGBE_RDRXCTL_CRCSTRIP;
 	}
 	if (netmap_verbose)
-		D("%s write HLREG 0x%x rxc 0x%x",
+		nm_prinf("%s write HLREG 0x%x rxc 0x%x",
 			onoff ? "enter" : "exit", hl, rxc);
 	IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hl);
 	IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, rxc);
@@ -326,8 +326,8 @@ ixgbe_netmap_txsync(struct netmap_kring *kring, int flags)
 		 */
 		nic_i = IXGBE_READ_REG(&adapter->hw, IXGBE_IS_VF(adapter) ?
 				IXGBE_VFTDH(kring->ring_id) : IXGBE_TDH(kring->ring_id));
-		if (nic_i >= kring->nkr_num_slots) { /* XXX can it happen ? */
-			D("TDH wrap %d", nic_i);
+		if (unlikely(nic_i >= kring->nkr_num_slots)) {
+			nm_prerr("TDH wrap at idx %d", nic_i);
 			nic_i -= kring->nkr_num_slots;
 		}
 		if (nic_i != txr->next_to_clean) {
@@ -395,7 +395,6 @@ ixgbe_netmap_rxsync(struct netmap_kring *kring, int flags)
 	 */
 	if (netmap_no_pendintr || force_update) {
 		int crclen = (ix_crcstrip || IXGBE_IS_VF(adapter) ) ? 0 : 4;
-		uint16_t slot_flags = kring->nkr_slot_flags;
 
 		nic_i = rxr->next_to_check; // or also k2n(kring->nr_hwtail)
 		nm_i = netmap_idx_n2k(kring, nic_i);
@@ -407,7 +406,7 @@ ixgbe_netmap_rxsync(struct netmap_kring *kring, int flags)
 			if ((staterr & IXGBE_RXD_STAT_DD) == 0)
 				break;
 			ring->slot[nm_i].len = le16toh(curr->wb.upper.length) - crclen;
-			ring->slot[nm_i].flags = slot_flags;
+			ring->slot[nm_i].flags = 0;
 			bus_dmamap_sync(rxr->ptag,
 			    rxr->rx_buffers[nic_i].pmap, BUS_DMASYNC_POSTREAD);
 			nm_i = nm_next(nm_i, lim);
diff --git a/sys/dev/netmap/netmap.c b/sys/dev/netmap/netmap.c
index 852152968..2c4e8ce28 100644
--- a/sys/dev/netmap/netmap.c
+++ b/sys/dev/netmap/netmap.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2011-2014 Matteo Landi
  * Copyright (C) 2011-2016 Luigi Rizzo
  * Copyright (C) 2011-2016 Giuseppe Lettieri
@@ -29,8 +31,6 @@
 
 
 /*
- * $FreeBSD$
- *
  * This module supports memory mapped access to network devices,
  * see netmap(4).
  *
@@ -191,7 +191,7 @@ ports attached to the switch)
  * 	      always attached to a bridge.
  * 	      Persistent VALE ports must must be created separately, and i
  * 	      then attached like normal NICs. The NIOCREGIF we are examining
- * 	      will find them only if they had previosly been created and
+ * 	      will find them only if they had previously been created and
  * 	      attached (see VALE_CTL below).
  *
  * 	- netmap_pipe_adapter 	      [netmap_pipe.c]
@@ -260,7 +260,7 @@ ports attached to the switch)
  *
  *  Any network interface known to the system (including a persistent VALE
  *  port) can be attached to a VALE switch by issuing the
- *  NETMAP_BDG_ATTACH subcommand. After the attachment, persistent VALE ports
+ *  NETMAP_REQ_VALE_ATTACH command. After the attachment, persistent VALE ports
  *  look exactly like ephemeral VALE ports (as created in step 2 above).  The
  *  attachment of other interfaces, instead, requires the creation of a
  *  netmap_bwrap_adapter.  Moreover, the attached interface must be put in
@@ -435,11 +435,13 @@ ports attached to the switch)
 #include 	/* struct socket */
 #include 
 #include 
+#include 
 #include 
 #include  /* sockaddrs */
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -447,6 +449,7 @@ ports attached to the switch)
 #include 	/* bus_dmamap_* */
 #include 
 #include 
+#include 	/* ETHER_BPF_MTAP */
 
 
 #elif defined(linux)
@@ -478,12 +481,13 @@ ports attached to the switch)
 
 /* user-controlled variables */
 int netmap_verbose;
+#ifdef CONFIG_NETMAP_DEBUG
+int netmap_debug;
+#endif /* CONFIG_NETMAP_DEBUG */
 
 static int netmap_no_timestamp; /* don't timestamp on rxsync */
-int netmap_mitigate = 1;
 int netmap_no_pendintr = 1;
 int netmap_txsync_retry = 2;
-int netmap_flags = 0;	/* debug flags */
 static int netmap_fwd = 0;	/* force transparent forwarding */
 
 /*
@@ -513,12 +517,17 @@ int netmap_generic_mit = 100*1000;
  * Anyway users looking for the best performance should
  * use native adapters.
  */
+#ifdef linux
 int netmap_generic_txqdisc = 1;
+#endif
 
 /* Default number of slots and queues for generic adapters. */
 int netmap_generic_ringsize = 1024;
 int netmap_generic_rings = 1;
 
+/* Non-zero to enable checksum offloading in NIC drivers */
+int netmap_generic_hwcsum = 0;
+
 /* Non-zero if ptnet devices are allowed to use virtio-net headers. */
 int ptnet_vnet_hdr = 1;
 
@@ -529,25 +538,43 @@ int ptnet_vnet_hdr = 1;
 SYSBEGIN(main_init);
 
 SYSCTL_DECL(_dev_netmap);
-SYSCTL_NODE(_dev, OID_AUTO, netmap, CTLFLAG_RW, 0, "Netmap args");
+SYSCTL_NODE(_dev, OID_AUTO, netmap, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+		"Netmap args");
 SYSCTL_INT(_dev_netmap, OID_AUTO, verbose,
-    CTLFLAG_RW, &netmap_verbose, 0, "Verbose mode");
+		CTLFLAG_RW, &netmap_verbose, 0, "Verbose mode");
+#ifdef CONFIG_NETMAP_DEBUG
+SYSCTL_INT(_dev_netmap, OID_AUTO, debug,
+		CTLFLAG_RW, &netmap_debug, 0, "Debug messages");
+#endif /* CONFIG_NETMAP_DEBUG */
 SYSCTL_INT(_dev_netmap, OID_AUTO, no_timestamp,
-    CTLFLAG_RW, &netmap_no_timestamp, 0, "no_timestamp");
-SYSCTL_INT(_dev_netmap, OID_AUTO, mitigate, CTLFLAG_RW, &netmap_mitigate, 0, "");
-SYSCTL_INT(_dev_netmap, OID_AUTO, no_pendintr,
-    CTLFLAG_RW, &netmap_no_pendintr, 0, "Always look for new received packets.");
+		CTLFLAG_RW, &netmap_no_timestamp, 0, "no_timestamp");
+SYSCTL_INT(_dev_netmap, OID_AUTO, no_pendintr, CTLFLAG_RW, &netmap_no_pendintr,
+		0, "Always look for new received packets.");
 SYSCTL_INT(_dev_netmap, OID_AUTO, txsync_retry, CTLFLAG_RW,
-    &netmap_txsync_retry, 0 , "Number of txsync loops in bridge's flush.");
-
-SYSCTL_INT(_dev_netmap, OID_AUTO, flags, CTLFLAG_RW, &netmap_flags, 0 , "");
-SYSCTL_INT(_dev_netmap, OID_AUTO, fwd, CTLFLAG_RW, &netmap_fwd, 0 , "");
-SYSCTL_INT(_dev_netmap, OID_AUTO, admode, CTLFLAG_RW, &netmap_admode, 0 , "");
-SYSCTL_INT(_dev_netmap, OID_AUTO, generic_mit, CTLFLAG_RW, &netmap_generic_mit, 0 , "");
-SYSCTL_INT(_dev_netmap, OID_AUTO, generic_ringsize, CTLFLAG_RW, &netmap_generic_ringsize, 0 , "");
-SYSCTL_INT(_dev_netmap, OID_AUTO, generic_rings, CTLFLAG_RW, &netmap_generic_rings, 0 , "");
-SYSCTL_INT(_dev_netmap, OID_AUTO, generic_txqdisc, CTLFLAG_RW, &netmap_generic_txqdisc, 0 , "");
-SYSCTL_INT(_dev_netmap, OID_AUTO, ptnet_vnet_hdr, CTLFLAG_RW, &ptnet_vnet_hdr, 0 , "");
+		&netmap_txsync_retry, 0, "Number of txsync loops in bridge's flush.");
+
+SYSCTL_INT(_dev_netmap, OID_AUTO, fwd, CTLFLAG_RW, &netmap_fwd, 0,
+		"Force NR_FORWARD mode");
+SYSCTL_INT(_dev_netmap, OID_AUTO, admode, CTLFLAG_RW, &netmap_admode, 0,
+		"Adapter mode. 0 selects the best option available,"
+		"1 forces native adapter, 2 forces emulated adapter");
+SYSCTL_INT(_dev_netmap, OID_AUTO, generic_hwcsum, CTLFLAG_RW, &netmap_generic_hwcsum,
+		0, "Hardware checksums. 0 to disable checksum generation by the NIC (default),"
+		"1 to enable checksum generation by the NIC");
+SYSCTL_INT(_dev_netmap, OID_AUTO, generic_mit, CTLFLAG_RW, &netmap_generic_mit,
+		0, "RX notification interval in nanoseconds");
+SYSCTL_INT(_dev_netmap, OID_AUTO, generic_ringsize, CTLFLAG_RW,
+		&netmap_generic_ringsize, 0,
+		"Number of per-ring slots for emulated netmap mode");
+SYSCTL_INT(_dev_netmap, OID_AUTO, generic_rings, CTLFLAG_RW,
+		&netmap_generic_rings, 0,
+		"Number of TX/RX queues for emulated netmap adapters");
+#ifdef linux
+SYSCTL_INT(_dev_netmap, OID_AUTO, generic_txqdisc, CTLFLAG_RW,
+		&netmap_generic_txqdisc, 0, "Use qdisc for generic adapters");
+#endif
+SYSCTL_INT(_dev_netmap, OID_AUTO, ptnet_vnet_hdr, CTLFLAG_RW, &ptnet_vnet_hdr,
+		0, "Allow ptnet devices to use virtio-net headers");
 
 SYSEND;
 
@@ -574,9 +601,9 @@ void
 netmap_set_ring(struct netmap_adapter *na, u_int ring_id, enum txrx t, int stopped)
 {
 	if (stopped)
-		netmap_disable_ring(NMR(na, t) + ring_id, stopped);
+		netmap_disable_ring(NMR(na, t)[ring_id], stopped);
 	else
-		NMR(na, t)[ring_id].nkr_stopped = 0;
+		NMR(na, t)[ring_id]->nkr_stopped = 0;
 }
 
 
@@ -590,6 +617,10 @@ netmap_set_all_rings(struct netmap_adapter *na, int stopped)
 	if (!nm_netmap_on(na))
 		return;
 
+	if (netmap_verbose) {
+		nm_prinf("%s: %sable all rings", na->name,
+		    (stopped ? "dis" : "en"));
+	}
 	for_rx_tx(t) {
 		for (i = 0; i < netmap_real_rings(na, t); i++) {
 			netmap_set_ring(na, i, t, stopped);
@@ -604,10 +635,10 @@ netmap_set_all_rings(struct netmap_adapter *na, int stopped)
  * onload).
  */
 void
-netmap_disable_all_rings(struct ifnet *ifp)
+netmap_disable_all_rings(if_t ifp)
 {
 	if (NM_NA_VALID(ifp)) {
-		netmap_set_all_rings(NA(ifp), NM_KR_STOPPED);
+		netmap_set_all_rings(NA(ifp), NM_KR_LOCKED);
 	}
 }
 
@@ -617,7 +648,7 @@ netmap_disable_all_rings(struct ifnet *ifp)
  * napi_enable().
  */
 void
-netmap_enable_all_rings(struct ifnet *ifp)
+netmap_enable_all_rings(if_t ifp)
 {
 	if (NM_NA_VALID(ifp)) {
 		netmap_set_all_rings(NA(ifp), 0 /* enabled */);
@@ -625,7 +656,7 @@ netmap_enable_all_rings(struct ifnet *ifp)
 }
 
 void
-netmap_make_zombie(struct ifnet *ifp)
+netmap_make_zombie(if_t ifp)
 {
 	if (NM_NA_VALID(ifp)) {
 		struct netmap_adapter *na = NA(ifp);
@@ -636,7 +667,7 @@ netmap_make_zombie(struct ifnet *ifp)
 }
 
 void
-netmap_undo_zombie(struct ifnet *ifp)
+netmap_undo_zombie(if_t ifp)
 {
 	if (NM_NA_VALID(ifp)) {
 		struct netmap_adapter *na = NA(ifp);
@@ -669,7 +700,7 @@ nm_bound_var(u_int *v, u_int dflt, u_int lo, u_int hi, const char *msg)
 		op = "Clamp";
 	}
 	if (op && msg)
-		printf("%s %s to %d (was %d)\n", op, msg, *v, oldv);
+		nm_prinf("%s %s to %d (was %d)", op, msg, *v, oldv);
 	return *v;
 }
 
@@ -728,39 +759,47 @@ nm_dump_buf(char *p, int len, int lim, char *dst)
 int
 netmap_update_config(struct netmap_adapter *na)
 {
-	u_int txr, txd, rxr, rxd;
+	struct nm_config_info info;
+
+	if (na->ifp && !nm_is_bwrap(na)) {
+		strlcpy(na->name, if_name(na->ifp), sizeof(na->name));
+	}
 
-	txr = txd = rxr = rxd = 0;
+	bzero(&info, sizeof(info));
 	if (na->nm_config == NULL ||
-	    na->nm_config(na, &txr, &txd, &rxr, &rxd))
-	{
+	    na->nm_config(na, &info)) {
 		/* take whatever we had at init time */
-		txr = na->num_tx_rings;
-		txd = na->num_tx_desc;
-		rxr = na->num_rx_rings;
-		rxd = na->num_rx_desc;
+		info.num_tx_rings = na->num_tx_rings;
+		info.num_tx_descs = na->num_tx_desc;
+		info.num_rx_rings = na->num_rx_rings;
+		info.num_rx_descs = na->num_rx_desc;
+		info.rx_buf_maxsize = na->rx_buf_maxsize;
 	}
 
-	if (na->num_tx_rings == txr && na->num_tx_desc == txd &&
-	    na->num_rx_rings == rxr && na->num_rx_desc == rxd)
+	if (na->num_tx_rings == info.num_tx_rings &&
+	    na->num_tx_desc == info.num_tx_descs &&
+	    na->num_rx_rings == info.num_rx_rings &&
+	    na->num_rx_desc == info.num_rx_descs &&
+	    na->rx_buf_maxsize == info.rx_buf_maxsize)
 		return 0; /* nothing changed */
-	if (netmap_verbose || na->active_fds > 0) {
-		D("stored config %s: txring %d x %d, rxring %d x %d",
-			na->name,
-			na->num_tx_rings, na->num_tx_desc,
-			na->num_rx_rings, na->num_rx_desc);
-		D("new config %s: txring %d x %d, rxring %d x %d",
-			na->name, txr, txd, rxr, rxd);
-	}
 	if (na->active_fds == 0) {
-		D("configuration changed (but fine)");
-		na->num_tx_rings = txr;
-		na->num_tx_desc = txd;
-		na->num_rx_rings = rxr;
-		na->num_rx_desc = rxd;
+		na->num_tx_rings = info.num_tx_rings;
+		na->num_tx_desc = info.num_tx_descs;
+		na->num_rx_rings = info.num_rx_rings;
+		na->num_rx_desc = info.num_rx_descs;
+		na->rx_buf_maxsize = info.rx_buf_maxsize;
+		if (netmap_verbose)
+			nm_prinf("configuration changed for %s: txring %d x %d, "
+				"rxring %d x %d, rxbufsz %d",
+				na->name, na->num_tx_rings, na->num_tx_desc,
+				na->num_rx_rings, na->num_rx_desc, na->rx_buf_maxsize);
 		return 0;
 	}
-	D("configuration changed while active, this is bad...");
+	nm_prerr("WARNING: configuration changed for %s while active: "
+		"txring %d x %d, rxring %d x %d, rxbufsz %d",
+		na->name, info.num_tx_rings, info.num_tx_descs,
+		info.num_rx_rings, info.num_rx_descs,
+		info.rx_buf_maxsize);
 	return 1;
 }
 
@@ -768,6 +807,14 @@ netmap_update_config(struct netmap_adapter *na)
 static int netmap_txsync_to_host(struct netmap_kring *kring, int flags);
 static int netmap_rxsync_from_host(struct netmap_kring *kring, int flags);
 
+static int
+netmap_default_bufcfg(struct netmap_kring *kring, uint64_t target)
+{
+	kring->hwbuf_len = target;
+	kring->buf_align = 0; /* no alignment */
+	return 0;
+}
+
 /* create the krings array and initialize the fields common to all adapters.
  * The array layout is this:
  *
@@ -788,6 +835,10 @@ static int netmap_rxsync_from_host(struct netmap_kring *kring, int flags);
  *                    |          |  } tailroom bytes
  *                    |          | /
  *                    +----------+
+ * netmap_kring       |          | (aligned to NM_KRING_ALIGNMENT)
+ * structs            ~          ~
+ *                    |          |
+ *                    +----------+
  *
  * Note: for compatibility, host krings are created even when not needed.
  * The tailroom space is currently used by vale ports for allocating leases.
@@ -800,24 +851,38 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
 	struct netmap_kring *kring;
 	u_int n[NR_TXRX];
 	enum txrx t;
+	int err = 0;
 
 	if (na->tx_rings != NULL) {
-		D("warning: krings were already created");
+		if (netmap_debug & NM_DEBUG_ON)
+			nm_prerr("warning: krings were already created");
 		return 0;
 	}
 
 	/* account for the (possibly fake) host rings */
-	n[NR_TX] = na->num_tx_rings + 1;
-	n[NR_RX] = na->num_rx_rings + 1;
+	n[NR_TX] = netmap_all_rings(na, NR_TX);
+	n[NR_RX] = netmap_all_rings(na, NR_RX);
 
-	len = (n[NR_TX] + n[NR_RX]) * sizeof(struct netmap_kring) + tailroom;
+	len = (n[NR_TX] + n[NR_RX]) * sizeof(struct netmap_kring *) +
+		tailroom +
+		(n[NR_TX] + n[NR_RX]) * sizeof(struct netmap_kring) +
+		NM_KRING_ALIGNMENT;
 
 	na->tx_rings = nm_os_malloc((size_t)len);
 	if (na->tx_rings == NULL) {
-		D("Cannot allocate krings");
+		nm_prerr("Cannot allocate krings");
 		return ENOMEM;
 	}
 	na->rx_rings = na->tx_rings + n[NR_TX];
+	na->tailroom = na->rx_rings + n[NR_RX];
+
+	/* link the krings in the krings array */
+	kring = (struct netmap_kring *)
+		nm_tailroom_align((uint64_t)((char *)na->tailroom + tailroom));
+	for (i = 0; i < n[NR_TX] + n[NR_RX]; i++) {
+		na->tx_rings[i] = kring;
+		kring++;
+	}
 
 	/*
 	 * All fields in krings are 0 except the one initialized below.
@@ -826,9 +891,9 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
 	for_rx_tx(t) {
 		ndesc = nma_get_ndesc(na, t);
 		for (i = 0; i < n[t]; i++) {
-			kring = &NMR(na, t)[i];
+			kring = NMR(na, t)[i];
 			bzero(kring, sizeof(*kring));
-			kring->na = na;
+			kring->notify_na = na;
 			kring->ring_id = i;
 			kring->tx = t;
 			kring->nkr_num_slots = ndesc;
@@ -836,10 +901,16 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
 			kring->nr_pending_mode = NKR_NETMAP_OFF;
 			if (i < nma_get_nrings(na, t)) {
 				kring->nm_sync = (t == NR_TX ? na->nm_txsync : na->nm_rxsync);
+				kring->nm_bufcfg = na->nm_bufcfg;
+				if (kring->nm_bufcfg == NULL)
+					kring->nm_bufcfg = netmap_default_bufcfg;
 			} else {
+				if (!(na->na_flags & NAF_HOST_RINGS))
+					kring->nr_kflags |= NKR_FAKERING;
 				kring->nm_sync = (t == NR_TX ?
 						netmap_txsync_to_host:
 						netmap_rxsync_from_host);
+				kring->nm_bufcfg = netmap_default_bufcfg;
 			}
 			kring->nm_notify = na->nm_notify;
 			kring->rhead = kring->rcur = kring->nr_hwcur = 0;
@@ -849,16 +920,23 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
 			kring->rtail = kring->nr_hwtail = (t == NR_TX ? ndesc - 1 : 0);
 			snprintf(kring->name, sizeof(kring->name) - 1, "%s %s%d", na->name,
 					nm_txrx2str(t), i);
-			ND("ktx %s h %d c %d t %d",
+			nm_prdis("ktx %s h %d c %d t %d",
 				kring->name, kring->rhead, kring->rcur, kring->rtail);
+			err = nm_os_selinfo_init(&kring->si, kring->name);
+			if (err) {
+				netmap_krings_delete(na);
+				return err;
+			}
 			mtx_init(&kring->q_lock, (t == NR_TX ? "nm_txq_lock" : "nm_rxq_lock"), NULL, MTX_DEF);
-			nm_os_selinfo_init(&kring->si);
+			kring->na = na;	/* setting this field marks the mutex as initialized */
+		}
+		err = nm_os_selinfo_init(&na->si[t], na->name);
+		if (err) {
+			netmap_krings_delete(na);
+			return err;
 		}
-		nm_os_selinfo_init(&na->si[t]);
 	}
 
-	na->tailroom = na->rx_rings + n[NR_RX];
-
 	return 0;
 }
 
@@ -868,11 +946,12 @@ netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
 void
 netmap_krings_delete(struct netmap_adapter *na)
 {
-	struct netmap_kring *kring = na->tx_rings;
+	struct netmap_kring **kring = na->tx_rings;
 	enum txrx t;
 
 	if (na->tx_rings == NULL) {
-		D("warning: krings were already deleted");
+		if (netmap_debug & NM_DEBUG_ON)
+			nm_prerr("warning: krings were already deleted");
 		return;
 	}
 
@@ -881,8 +960,9 @@ netmap_krings_delete(struct netmap_adapter *na)
 
 	/* we rely on the krings layout described above */
 	for ( ; kring != na->tailroom; kring++) {
-		mtx_destroy(&kring->q_lock);
-		nm_os_selinfo_uninit(&kring->si);
+		if ((*kring)->na != NULL)
+			mtx_destroy(&(*kring)->q_lock);
+		nm_os_selinfo_uninit(&(*kring)->si);
 	}
 	nm_os_free(na->tx_rings);
 	na->tx_rings = na->rx_rings = na->tailroom = NULL;
@@ -898,15 +978,55 @@ netmap_krings_delete(struct netmap_adapter *na)
 void
 netmap_hw_krings_delete(struct netmap_adapter *na)
 {
-	struct mbq *q = &na->rx_rings[na->num_rx_rings].rx_queue;
+	u_int lim = netmap_real_rings(na, NR_RX), i;
 
-	ND("destroy sw mbq with len %d", mbq_len(q));
-	mbq_purge(q);
-	mbq_safe_fini(q);
+	for (i = nma_get_nrings(na, NR_RX); i < lim; i++) {
+		struct mbq *q = &NMR(na, NR_RX)[i]->rx_queue;
+		nm_prdis("destroy sw mbq with len %d", mbq_len(q));
+		mbq_purge(q);
+		mbq_safe_fini(q);
+	}
 	netmap_krings_delete(na);
 }
 
+void
+netmap_mem_restore(struct netmap_adapter *na)
+{
+	if (na->nm_mem_prev) {
+		netmap_mem_put(na->nm_mem);
+		na->nm_mem = na->nm_mem_prev;
+		na->nm_mem_prev = NULL;
+	}
+}
+
+static void
+netmap_mem_drop(struct netmap_adapter *na)
+{
+	netmap_mem_deref(na->nm_mem, na);
+
+	if (na->active_fds <= 0) {
+		/* if the native allocator had been overridden on regif,
+		 * restore it now and drop the temporary one
+		 */
+		netmap_mem_restore(na);
+	}
+}
+
+static void
+netmap_update_hostrings_mode(struct netmap_adapter *na)
+{
+	enum txrx t;
+	struct netmap_kring *kring;
+	int i;
 
+	for_rx_tx(t) {
+		for (i = nma_get_nrings(na, t);
+		     i < netmap_real_rings(na, t); i++) {
+			kring = NMR(na, t)[i];
+			kring->nr_mode = kring->nr_pending_mode;
+		}
+	}
+}
 
 /*
  * Undo everything that was done in netmap_do_regif(). In particular,
@@ -937,8 +1057,12 @@ netmap_do_unregif(struct netmap_priv_d *priv)
 	}
 #endif
 
+	netmap_update_hostrings_mode(na);
+
 	if (na->active_fds <= 0 || nm_kring_pending(priv)) {
+		netmap_set_all_rings(na, NM_KR_LOCKED);
 		na->nm_register(na, 0);
+		netmap_set_all_rings(na, 0);
 	}
 
 	/* delete rings and buffers that are no longer needed */
@@ -959,35 +1083,36 @@ netmap_do_unregif(struct netmap_priv_d *priv)
 		 * happens if the close() occurs while a concurrent
 		 * syscall is running.
 		 */
-		if (netmap_verbose)
-			D("deleting last instance for %s", na->name);
+		if (netmap_debug & NM_DEBUG_ON)
+			nm_prinf("deleting last instance for %s", na->name);
 
-                if (nm_netmap_on(na)) {
-                    D("BUG: netmap on while going to delete the krings");
-                }
+		if (nm_netmap_on(na)) {
+			nm_prerr("BUG: netmap on while going to delete the krings");
+		}
 
 		na->nm_krings_delete(na);
+
+		/* restore the default number of host tx and rx rings */
+		if (na->na_flags & NAF_HOST_RINGS) {
+			na->num_host_tx_rings = 1;
+			na->num_host_rx_rings = 1;
+		} else {
+			na->num_host_tx_rings = 0;
+			na->num_host_rx_rings = 0;
+		}
 	}
 
-	/* possibily decrement counter of tx_si/rx_si users */
+	/* possibly decrement counter of tx_si/rx_si users */
 	netmap_unset_ringid(priv);
 	/* delete the nifp */
 	netmap_mem_if_delete(na, priv->np_nifp);
 	/* drop the allocator */
-	netmap_mem_deref(na->nm_mem, na);
+	netmap_mem_drop(na);
 	/* mark the priv as unregistered */
 	priv->np_na = NULL;
 	priv->np_nifp = NULL;
 }
 
-/* call with NMG_LOCK held */
-static __inline int
-nm_si_user(struct netmap_priv_d *priv, enum txrx t)
-{
-	return (priv->np_na != NULL &&
-		(priv->np_qlast[t] - priv->np_qfirst[t] > 1));
-}
-
 struct netmap_priv_d*
 netmap_priv_new(void)
 {
@@ -1059,7 +1184,7 @@ netmap_dtor(void *data)
  *   they will be forwarded to the hw TX rings, saving the application
  *   from doing the same task in user-space.
  *
- * Transparent fowarding can be enabled per-ring, by setting the NR_FORWARD
+ * Transparent forwarding can be enabled per-ring, by setting the NR_FORWARD
  * flag, or globally with the netmap_fwd sysctl.
  *
  * The transfer NIC --> host is relatively easy, just encapsulate
@@ -1075,22 +1200,29 @@ netmap_dtor(void *data)
  * After this call the queue is empty.
  */
 static void
-netmap_send_up(struct ifnet *dst, struct mbq *q)
+netmap_send_up(if_t dst, struct mbq *q)
 {
 	struct mbuf *m;
 	struct mbuf *head = NULL, *prev = NULL;
+#ifdef __FreeBSD__
+	struct epoch_tracker et;
 
+	NET_EPOCH_ENTER(et);
+#endif /* __FreeBSD__ */
 	/* Send packets up, outside the lock; head/prev machinery
 	 * is only useful for Windows. */
 	while ((m = mbq_dequeue(q)) != NULL) {
-		if (netmap_verbose & NM_VERB_HOST)
-			D("sending up pkt %p size %d", m, MBUF_LEN(m));
+		if (netmap_debug & NM_DEBUG_HOST)
+			nm_prinf("sending up pkt %p size %d", m, MBUF_LEN(m));
 		prev = nm_os_send_up(dst, m, prev);
 		if (head == NULL)
 			head = prev;
 	}
 	if (head)
 		nm_os_send_up(dst, NULL, head);
+#ifdef __FreeBSD__
+	NET_EPOCH_EXIT(et);
+#endif /* __FreeBSD__ */
 	mbq_fini(q);
 }
 
@@ -1112,16 +1244,18 @@ netmap_grab_packets(struct netmap_kring *kring, struct mbq *q, int force)
 	for (n = kring->nr_hwcur; n != head; n = nm_next(n, lim)) {
 		struct mbuf *m;
 		struct netmap_slot *slot = &kring->ring->slot[n];
+		uint16_t len = NM_ACCESS_ONCE(slot->len);
+		uint64_t offset = nm_get_offset(kring, slot);
 
 		if ((slot->flags & NS_FORWARD) == 0 && !force)
 			continue;
-		if (slot->len < 14 || slot->len > NETMAP_BUF_SIZE(na)) {
-			RD(5, "bad pkt at %d len %d", n, slot->len);
+		if (len < 14 || len > (NETMAP_BUF_SIZE(na) - offset)) {
+			nm_prlim(5, "bad pkt at %d len %d", n, len);
 			continue;
 		}
 		slot->flags &= ~NS_FORWARD; // XXX needed ?
 		/* XXX TODO: adapt to the case of a multisegment packet */
-		m = m_devget(NMB(na, slot), slot->len, 0, na->ifp, NULL);
+		m = m_devget(NMB_O(kring, slot), len, 0, na->ifp, NULL);
 
 		if (m == NULL)
 			break;
@@ -1167,7 +1301,7 @@ nm_may_forward_down(struct netmap_kring *kring, int sync_flags)
 static u_int
 netmap_sw_to_nic(struct netmap_adapter *na)
 {
-	struct netmap_kring *kring = &na->rx_rings[na->num_rx_rings];
+	struct netmap_kring *kring = na->rx_rings[na->num_rx_rings];
 	struct netmap_slot *rxslot = kring->ring->slot;
 	u_int i, rxcur = kring->nr_hwcur;
 	u_int const head = kring->rhead;
@@ -1176,7 +1310,7 @@ netmap_sw_to_nic(struct netmap_adapter *na)
 
 	/* scan rings to find space, then fill as much as possible */
 	for (i = 0; i < na->num_tx_rings; i++) {
-		struct netmap_kring *kdst = &na->tx_rings[i];
+		struct netmap_kring *kdst = na->tx_rings[i];
 		struct netmap_ring *rdst = kdst->ring;
 		u_int const dst_lim = kdst->nkr_num_slots - 1;
 
@@ -1230,7 +1364,7 @@ netmap_txsync_to_host(struct netmap_kring *kring, int flags)
 	 */
 	mbq_init(&q);
 	netmap_grab_packets(kring, &q, 1 /* force */);
-	ND("have %d pkts in queue", mbq_len(&q));
+	nm_prdis("have %d pkts in queue", mbq_len(&q));
 	kring->nr_hwcur = head;
 	kring->nr_hwtail = head + lim;
 	if (kring->nr_hwtail > lim)
@@ -1273,17 +1407,17 @@ netmap_rxsync_from_host(struct netmap_kring *kring, int flags)
 
 		nm_i = kring->nr_hwtail;
 		stop_i = nm_prev(kring->nr_hwcur, lim);
-		while ( nm_i != stop_i && (m = mbq_dequeue(q)) != NULL ) {
+		while (nm_i != stop_i && (m = mbq_dequeue(q)) != NULL) {
 			int len = MBUF_LEN(m);
 			struct netmap_slot *slot = &ring->slot[nm_i];
 
-			m_copydata(m, 0, len, NMB(na, slot));
-			ND("nm %d len %d", nm_i, len);
-			if (netmap_verbose)
-                                D("%s", nm_dump_buf(NMB(na, slot),len, 128, NULL));
+			m_copydata(m, 0, len, NMB_O(kring, slot));
+			nm_prdis("nm %d len %d", nm_i, len);
+			if (netmap_debug & NM_DEBUG_HOST)
+				nm_prinf("%s", nm_dump_buf(NMB_O(kring, slot), len, 128, NULL));
 
 			slot->len = len;
-			slot->flags = kring->nkr_slot_flags;
+			slot->flags = 0;
 			nm_i = nm_next(nm_i, lim);
 			mbq_enqueue(&fq, m);
 		}
@@ -1339,7 +1473,7 @@ netmap_rxsync_from_host(struct netmap_kring *kring, int flags)
  */
 static void netmap_hw_dtor(struct netmap_adapter *); /* needed by NM_IS_NATIVE() */
 int
-netmap_get_hw_na(struct ifnet *ifp, struct netmap_mem_d *nmd, struct netmap_adapter **na)
+netmap_get_hw_na(if_t ifp, struct netmap_mem_d *nmd, struct netmap_adapter **na)
 {
 	/* generic support */
 	int i = netmap_admode;	/* Take a snapshot. */
@@ -1403,7 +1537,7 @@ netmap_get_hw_na(struct ifnet *ifp, struct netmap_mem_d *nmd, struct netmap_adap
 assign_mem:
 	if (nmd != NULL && !((*na)->na_flags & NAF_MEM_OWNER) &&
 	    (*na)->active_fds == 0 && ((*na)->nm_mem != nmd)) {
-		netmap_mem_put((*na)->nm_mem);
+		(*na)->nm_mem_prev = (*na)->nm_mem;
 		(*na)->nm_mem = netmap_mem_get(nmd);
 	}
 
@@ -1414,7 +1548,7 @@ netmap_get_hw_na(struct ifnet *ifp, struct netmap_mem_d *nmd, struct netmap_adap
  * MUST BE CALLED UNDER NMG_LOCK()
  *
  * Get a refcounted reference to a netmap adapter attached
- * to the interface specified by nmr.
+ * to the interface specified by req.
  * This is always called in the execution of an ioctl().
  *
  * Return ENXIO if the interface specified by the request does
@@ -1424,13 +1558,15 @@ netmap_get_hw_na(struct ifnet *ifp, struct netmap_mem_d *nmd, struct netmap_adap
  * could not be allocated.
  * If successful, hold a reference to the netmap adapter.
  *
- * If the interface specified by nmr is a system one, also keep
+ * If the interface specified by req is a system one, also keep
  * a reference to it and return a valid *ifp.
  */
 int
-netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na,
-	      struct ifnet **ifp, struct netmap_mem_d *nmd, int create)
+netmap_get_na(struct nmreq_header *hdr,
+	      struct netmap_adapter **na, if_t *ifp,
+	      struct netmap_mem_d *nmd, int create)
 {
+	struct nmreq_register *req = (struct nmreq_register *)(uintptr_t)hdr->nr_body;
 	int error = 0;
 	struct netmap_adapter *ret = NULL;
 	int nmd_ref = 0;
@@ -1438,13 +1574,24 @@ netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na,
 	*na = NULL;     /* default return value */
 	*ifp = NULL;
 
+	if (hdr->nr_reqtype != NETMAP_REQ_REGISTER) {
+		return EINVAL;
+	}
+
+	if (req->nr_mode == NR_REG_PIPE_MASTER ||
+			req->nr_mode == NR_REG_PIPE_SLAVE) {
+		/* Do not accept deprecated pipe modes. */
+		nm_prerr("Deprecated pipe nr_mode, use xx{yy or xx}yy syntax");
+		return EINVAL;
+	}
+
 	NMG_LOCK_ASSERT();
 
 	/* if the request contain a memid, try to find the
 	 * corresponding memory region
 	 */
-	if (nmd == NULL && nmr->nr_arg2) {
-		nmd = netmap_mem_find(nmr->nr_arg2);
+	if (nmd == NULL && req->nr_mem_id) {
+		nmd = netmap_mem_find(req->nr_mem_id);
 		if (nmd == NULL)
 			return EINVAL;
 		/* keep the rereference */
@@ -1461,24 +1608,22 @@ netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na,
 	 *   0	  !NULL		type matches and na created/found
 	 *  !0    !NULL		impossible
 	 */
-
-	/* try to see if this is a ptnetmap port */
-	error = netmap_get_pt_host_na(nmr, na, nmd, create);
+	error = netmap_get_null_na(hdr, na, nmd, create);
 	if (error || *na != NULL)
 		goto out;
 
 	/* try to see if this is a monitor port */
-	error = netmap_get_monitor_na(nmr, na, nmd, create);
+	error = netmap_get_monitor_na(hdr, na, nmd, create);
 	if (error || *na != NULL)
 		goto out;
 
 	/* try to see if this is a pipe port */
-	error = netmap_get_pipe_na(nmr, na, nmd, create);
+	error = netmap_get_pipe_na(hdr, na, nmd, create);
 	if (error || *na != NULL)
 		goto out;
 
-	/* try to see if this is a bridge port */
-	error = netmap_get_bdg_na(nmr, na, nmd, create);
+	/* try to see if this is a vale port */
+	error = netmap_get_vale_na(hdr, na, nmd, create);
 	if (error)
 		goto out;
 
@@ -1491,7 +1636,7 @@ netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na,
 	 * This may still be a tap, a veth/epair, or even a
 	 * persistent VALE port.
 	 */
-	*ifp = ifunit_ref(nmr->nr_name);
+	*ifp = ifunit_ref(hdr->nr_name);
 	if (*ifp == NULL) {
 		error = ENXIO;
 		goto out;
@@ -1504,6 +1649,19 @@ netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na,
 	*na = ret;
 	netmap_adapter_get(ret);
 
+	/*
+	 * if the adapter supports the host rings and it is not already open,
+	 * try to set the number of host rings as requested by the user
+	 */
+	if (((*na)->na_flags & NAF_HOST_RINGS) && (*na)->active_fds == 0) {
+		if (req->nr_host_tx_rings)
+			(*na)->num_host_tx_rings = req->nr_host_tx_rings;
+		if (req->nr_host_rx_rings)
+			(*na)->num_host_rx_rings = req->nr_host_rx_rings;
+	}
+	nm_prdis("%s: host tx %d rx %u", (*na)->name, (*na)->num_host_tx_rings,
+			(*na)->num_host_rx_rings);
+
 out:
 	if (error) {
 		if (ret)
@@ -1521,7 +1679,7 @@ netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na,
 
 /* undo netmap_get_na() */
 void
-netmap_unget_na(struct netmap_adapter *na, struct ifnet *ifp)
+netmap_unget_na(struct netmap_adapter *na, if_t ifp)
 {
 	if (ifp)
 		if_rele(ifp);
@@ -1532,7 +1690,7 @@ netmap_unget_na(struct netmap_adapter *na, struct ifnet *ifp)
 
 #define NM_FAIL_ON(t) do {						\
 	if (unlikely(t)) {						\
-		RD(5, "%s: fail '" #t "' "				\
+		nm_prlim(5, "%s: fail '" #t "' "				\
 			"h %d c %d t %d "				\
 			"rh %d rc %d rt %d "				\
 			"hc %d ht %d",					\
@@ -1560,11 +1718,11 @@ netmap_unget_na(struct netmap_adapter *na, struct ifnet *ifp)
 u_int
 nm_txsync_prologue(struct netmap_kring *kring, struct netmap_ring *ring)
 {
-	u_int head = ring->head; /* read only once */
-	u_int cur = ring->cur; /* read only once */
+	u_int head = NM_ACCESS_ONCE(ring->head);
+	u_int cur = NM_ACCESS_ONCE(ring->cur);
 	u_int n = kring->nkr_num_slots;
 
-	ND(5, "%s kcur %d ktail %d head %d cur %d tail %d",
+	nm_prdis(5, "%s kcur %d ktail %d head %d cur %d tail %d",
 		kring->name,
 		kring->nr_hwcur, kring->nr_hwtail,
 		ring->head, ring->cur, ring->tail);
@@ -1600,7 +1758,7 @@ nm_txsync_prologue(struct netmap_kring *kring, struct netmap_ring *ring)
 		}
 	}
 	if (ring->tail != kring->rtail) {
-		RD(5, "%s tail overwritten was %d need %d", kring->name,
+		nm_prlim(5, "%s tail overwritten was %d need %d", kring->name,
 			ring->tail, kring->rtail);
 		ring->tail = kring->rtail;
 	}
@@ -1627,7 +1785,7 @@ nm_rxsync_prologue(struct netmap_kring *kring, struct netmap_ring *ring)
 	uint32_t const n = kring->nkr_num_slots;
 	uint32_t head, cur;
 
-	ND(5,"%s kc %d kt %d h %d c %d t %d",
+	nm_prdis(5,"%s kc %d kt %d h %d c %d t %d",
 		kring->name,
 		kring->nr_hwcur, kring->nr_hwtail,
 		ring->head, ring->cur, ring->tail);
@@ -1638,8 +1796,8 @@ nm_rxsync_prologue(struct netmap_kring *kring, struct netmap_ring *ring)
 	 * - cur could in principle go back, however it does not matter
 	 *   because we are processing a brand new rxsync()
 	 */
-	cur = kring->rcur = ring->cur;	/* read only once */
-	head = kring->rhead = ring->head;	/* read only once */
+	cur = kring->rcur = NM_ACCESS_ONCE(ring->cur);
+	head = kring->rhead = NM_ACCESS_ONCE(ring->head);
 #if 1 /* kernel sanity checks */
 	NM_FAIL_ON(kring->nr_hwcur >= n || kring->nr_hwtail >= n);
 #endif /* kernel sanity checks */
@@ -1662,7 +1820,7 @@ nm_rxsync_prologue(struct netmap_kring *kring, struct netmap_ring *ring)
 		}
 	}
 	if (ring->tail != kring->rtail) {
-		RD(5, "%s tail overwritten was %d need %d",
+		nm_prlim(5, "%s tail overwritten was %d need %d",
 			kring->name,
 			ring->tail, kring->rtail);
 		ring->tail = kring->rtail;
@@ -1673,7 +1831,7 @@ nm_rxsync_prologue(struct netmap_kring *kring, struct netmap_ring *ring)
 
 /*
  * Error routine called when txsync/rxsync detects an error.
- * Can't do much more than resetting head =cur = hwcur, tail = hwtail
+ * Can't do much more than resetting head = cur = hwcur, tail = hwtail
  * Return 1 on reinit.
  *
  * This routine is only called by the upper half of the kernel.
@@ -1691,7 +1849,7 @@ netmap_ring_reinit(struct netmap_kring *kring)
 	int errors = 0;
 
 	// XXX KASSERT nm_kr_tryget
-	RD(10, "called for %s", kring->name);
+	nm_prlim(10, "called for %s", kring->name);
 	// XXX probably wrong to trust userspace
 	kring->rhead = ring->head;
 	kring->rcur  = ring->cur;
@@ -1707,17 +1865,17 @@ netmap_ring_reinit(struct netmap_kring *kring)
 		u_int idx = ring->slot[i].buf_idx;
 		u_int len = ring->slot[i].len;
 		if (idx < 2 || idx >= kring->na->na_lut.objtotal) {
-			RD(5, "bad index at slot %d idx %d len %d ", i, idx, len);
+			nm_prlim(5, "bad index at slot %d idx %d len %d ", i, idx, len);
 			ring->slot[i].buf_idx = 0;
 			ring->slot[i].len = 0;
 		} else if (len > NETMAP_BUF_SIZE(kring->na)) {
 			ring->slot[i].len = 0;
-			RD(5, "bad len at slot %d idx %d len %d", i, idx, len);
+			nm_prlim(5, "bad len at slot %d idx %d len %d", i, idx, len);
 		}
 	}
 	if (errors) {
-		RD(10, "total %d errors", errors);
-		RD(10, "%s reinit, cur %d -> %d tail %d -> %d",
+		nm_prlim(10, "total %d errors", errors);
+		nm_prlim(10, "%s reinit, cur %d -> %d tail %d -> %d",
 			kring->name,
 			ring->cur, kring->nr_hwcur,
 			ring->tail, kring->nr_hwtail);
@@ -1736,79 +1894,82 @@ netmap_ring_reinit(struct netmap_kring *kring)
  *
  */
 int
-netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags)
+netmap_interp_ringid(struct netmap_priv_d *priv, struct nmreq_header *hdr)
 {
 	struct netmap_adapter *na = priv->np_na;
-	u_int j, i = ringid & NETMAP_RING_MASK;
-	u_int reg = flags & NR_REG_MASK;
+	struct nmreq_register *reg = (struct nmreq_register *)hdr->nr_body;
 	int excluded_direction[] = { NR_TX_RINGS_ONLY, NR_RX_RINGS_ONLY };
 	enum txrx t;
-
-	if (reg == NR_REG_DEFAULT) {
-		/* convert from old ringid to flags */
-		if (ringid & NETMAP_SW_RING) {
-			reg = NR_REG_SW;
-		} else if (ringid & NETMAP_HW_RING) {
-			reg = NR_REG_ONE_NIC;
-		} else {
-			reg = NR_REG_ALL_NIC;
-		}
-		D("deprecated API, old ringid 0x%x -> ringid %x reg %d", ringid, i, reg);
-	}
-
-	if ((flags & NR_PTNETMAP_HOST) && (reg != NR_REG_ALL_NIC ||
-			flags & (NR_RX_RINGS_ONLY|NR_TX_RINGS_ONLY))) {
-		D("Error: only NR_REG_ALL_NIC supported with netmap passthrough");
-		return EINVAL;
-	}
+	u_int j;
+	u_int nr_flags = reg->nr_flags, nr_mode = reg->nr_mode,
+	      nr_ringid = reg->nr_ringid;
 
 	for_rx_tx(t) {
-		if (flags & excluded_direction[t]) {
+		if (nr_flags & excluded_direction[t]) {
 			priv->np_qfirst[t] = priv->np_qlast[t] = 0;
 			continue;
 		}
-		switch (reg) {
+		switch (nr_mode) {
 		case NR_REG_ALL_NIC:
-		case NR_REG_PIPE_MASTER:
-		case NR_REG_PIPE_SLAVE:
+		case NR_REG_NULL:
 			priv->np_qfirst[t] = 0;
 			priv->np_qlast[t] = nma_get_nrings(na, t);
-			ND("ALL/PIPE: %s %d %d", nm_txrx2str(t),
+			nm_prdis("ALL/PIPE: %s %d %d", nm_txrx2str(t),
 				priv->np_qfirst[t], priv->np_qlast[t]);
 			break;
 		case NR_REG_SW:
 		case NR_REG_NIC_SW:
 			if (!(na->na_flags & NAF_HOST_RINGS)) {
-				D("host rings not supported");
+				nm_prerr("host rings not supported");
 				return EINVAL;
 			}
-			priv->np_qfirst[t] = (reg == NR_REG_SW ?
+			priv->np_qfirst[t] = (nr_mode == NR_REG_SW ?
 				nma_get_nrings(na, t) : 0);
-			priv->np_qlast[t] = nma_get_nrings(na, t) + 1;
-			ND("%s: %s %d %d", reg == NR_REG_SW ? "SW" : "NIC+SW",
+			priv->np_qlast[t] = netmap_all_rings(na, t);
+			nm_prdis("%s: %s %d %d", nr_mode == NR_REG_SW ? "SW" : "NIC+SW",
 				nm_txrx2str(t),
 				priv->np_qfirst[t], priv->np_qlast[t]);
 			break;
 		case NR_REG_ONE_NIC:
-			if (i >= na->num_tx_rings && i >= na->num_rx_rings) {
-				D("invalid ring id %d", i);
+			if (nr_ringid >= na->num_tx_rings &&
+					nr_ringid >= na->num_rx_rings) {
+				nm_prerr("invalid ring id %d", nr_ringid);
 				return EINVAL;
 			}
 			/* if not enough rings, use the first one */
-			j = i;
+			j = nr_ringid;
 			if (j >= nma_get_nrings(na, t))
 				j = 0;
 			priv->np_qfirst[t] = j;
 			priv->np_qlast[t] = j + 1;
-			ND("ONE_NIC: %s %d %d", nm_txrx2str(t),
+			nm_prdis("ONE_NIC: %s %d %d", nm_txrx2str(t),
+				priv->np_qfirst[t], priv->np_qlast[t]);
+			break;
+		case NR_REG_ONE_SW:
+			if (!(na->na_flags & NAF_HOST_RINGS)) {
+				nm_prerr("host rings not supported");
+				return EINVAL;
+			}
+			if (nr_ringid >= na->num_host_tx_rings &&
+					nr_ringid >= na->num_host_rx_rings) {
+				nm_prerr("invalid ring id %d", nr_ringid);
+				return EINVAL;
+			}
+			/* if not enough rings, use the first one */
+			j = nr_ringid;
+			if (j >= nma_get_host_nrings(na, t))
+				j = 0;
+			priv->np_qfirst[t] = nma_get_nrings(na, t) + j;
+			priv->np_qlast[t] = nma_get_nrings(na, t) + j + 1;
+			nm_prdis("ONE_SW: %s %d %d", nm_txrx2str(t),
 				priv->np_qfirst[t], priv->np_qlast[t]);
 			break;
 		default:
-			D("invalid regif type %d", reg);
+			nm_prerr("invalid regif type %d", nr_mode);
 			return EINVAL;
 		}
 	}
-	priv->np_flags = (flags & ~NR_REG_MASK) | reg;
+	priv->np_flags = nr_flags;
 
 	/* Allow transparent forwarding mode in the host --> nic
 	 * direction only if all the TX hw rings have been opened. */
@@ -1818,13 +1979,13 @@ netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags
 	}
 
 	if (netmap_verbose) {
-		D("%s: tx [%d,%d) rx [%d,%d) id %d",
+		nm_prinf("%s: tx [%d,%d) rx [%d,%d) id %d",
 			na->name,
 			priv->np_qfirst[NR_TX],
 			priv->np_qlast[NR_TX],
 			priv->np_qfirst[NR_RX],
 			priv->np_qlast[NR_RX],
-			i);
+			nr_ringid);
 	}
 	return 0;
 }
@@ -1835,18 +1996,19 @@ netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags
  * for all rings is the same as a single ring.
  */
 static int
-netmap_set_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags)
+netmap_set_ringid(struct netmap_priv_d *priv, struct nmreq_header *hdr)
 {
 	struct netmap_adapter *na = priv->np_na;
+	struct nmreq_register *reg = (struct nmreq_register *)hdr->nr_body;
 	int error;
 	enum txrx t;
 
-	error = netmap_interp_ringid(priv, ringid, flags);
+	error = netmap_interp_ringid(priv, hdr);
 	if (error) {
 		return error;
 	}
 
-	priv->np_txpoll = (ringid & NETMAP_NO_TX_POLL) ? 0 : 1;
+	priv->np_txpoll = (reg->nr_flags & NR_NO_TX_POLL) ? 0 : 1;
 
 	/* optimization: count the users registered for more than
 	 * one ring, which are the ones sleeping on the global queue.
@@ -1873,8 +2035,22 @@ netmap_unset_ringid(struct netmap_priv_d *priv)
 	}
 	priv->np_flags = 0;
 	priv->np_txpoll = 0;
+	priv->np_kloop_state = 0;
 }
 
+#define within_sel(p_, t_, i_)					  	  \
+	((i_) < (p_)->np_qlast[(t_)])
+#define nonempty_sel(p_, t_)						  \
+	(within_sel((p_), (t_), (p_)->np_qfirst[(t_)]))
+#define foreach_selected_ring(p_, t_, i_, kring_)			  \
+	for ((t_) = nonempty_sel((p_), NR_RX) ? NR_RX : NR_TX,		  \
+	     (i_) = (p_)->np_qfirst[(t_)];				  \
+	     (t_ == NR_RX ||						  \
+	      (t == NR_TX && within_sel((p_), (t_), (i_)))) &&     	  \
+	      ((kring_) = NMR((p_)->np_na, (t_))[(i_)]); 		  \
+	     (i_) = within_sel((p_), (t_), (i_) + 1) ? (i_) + 1 :         \
+		(++(t_) < NR_TXRX ? (p_)->np_qfirst[(t_)] : (i_)))
+
 
 /* Set the nr_pending_mode for the requested rings.
  * If requested, also try to get exclusive access to the rings, provided
@@ -1889,7 +2065,8 @@ netmap_krings_get(struct netmap_priv_d *priv)
 	int excl = (priv->np_flags & NR_EXCLUSIVE);
 	enum txrx t;
 
-	ND("%s: grabbing tx [%d, %d) rx [%d, %d)",
+	if (netmap_debug & NM_DEBUG_ON)
+		nm_prinf("%s: grabbing tx [%d, %d) rx [%d, %d)",
 			na->name,
 			priv->np_qfirst[NR_TX],
 			priv->np_qlast[NR_TX],
@@ -1897,32 +2074,26 @@ netmap_krings_get(struct netmap_priv_d *priv)
 			priv->np_qlast[NR_RX]);
 
 	/* first round: check that all the requested rings
-	 * are neither alread exclusively owned, nor we
+	 * are neither already exclusively owned, nor we
 	 * want exclusive ownership when they are already in use
 	 */
-	for_rx_tx(t) {
-		for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
-			kring = &NMR(na, t)[i];
-			if ((kring->nr_kflags & NKR_EXCLUSIVE) ||
-			    (kring->users && excl))
-			{
-				ND("ring %s busy", kring->name);
-				return EBUSY;
-			}
+	foreach_selected_ring(priv, t, i, kring) {
+		if ((kring->nr_kflags & NKR_EXCLUSIVE) ||
+		    (kring->users && excl))
+		{
+			nm_prdis("ring %s busy", kring->name);
+			return EBUSY;
 		}
 	}
 
 	/* second round: increment usage count (possibly marking them
 	 * as exclusive) and set the nr_pending_mode
 	 */
-	for_rx_tx(t) {
-		for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
-			kring = &NMR(na, t)[i];
-			kring->users++;
-			if (excl)
-				kring->nr_kflags |= NKR_EXCLUSIVE;
-	                kring->nr_pending_mode = NKR_NETMAP_ON;
-		}
+	foreach_selected_ring(priv, t, i, kring) {
+		kring->users++;
+		if (excl)
+			kring->nr_kflags |= NKR_EXCLUSIVE;
+		kring->nr_pending_mode = NKR_NETMAP_ON;
 	}
 
 	return 0;
@@ -1935,30 +2106,392 @@ netmap_krings_get(struct netmap_priv_d *priv)
 static void
 netmap_krings_put(struct netmap_priv_d *priv)
 {
-	struct netmap_adapter *na = priv->np_na;
 	u_int i;
 	struct netmap_kring *kring;
 	int excl = (priv->np_flags & NR_EXCLUSIVE);
 	enum txrx t;
 
-	ND("%s: releasing tx [%d, %d) rx [%d, %d)",
+	nm_prdis("%s: releasing tx [%d, %d) rx [%d, %d)",
 			na->name,
 			priv->np_qfirst[NR_TX],
 			priv->np_qlast[NR_TX],
 			priv->np_qfirst[NR_RX],
 			priv->np_qlast[MR_RX]);
 
+	foreach_selected_ring(priv, t, i, kring) {
+		if (excl)
+			kring->nr_kflags &= ~NKR_EXCLUSIVE;
+		kring->users--;
+		if (kring->users == 0)
+			kring->nr_pending_mode = NKR_NETMAP_OFF;
+	}
+}
+
+static int
+nm_priv_rx_enabled(struct netmap_priv_d *priv)
+{
+	return (priv->np_qfirst[NR_RX] != priv->np_qlast[NR_RX]);
+}
+
+/* Validate the CSB entries for both directions (atok and ktoa).
+ * To be called under NMG_LOCK(). */
+static int
+netmap_csb_validate(struct netmap_priv_d *priv, struct nmreq_opt_csb *csbo)
+{
+	struct nm_csb_atok *csb_atok_base =
+		(struct nm_csb_atok *)(uintptr_t)csbo->csb_atok;
+	struct nm_csb_ktoa *csb_ktoa_base =
+		(struct nm_csb_ktoa *)(uintptr_t)csbo->csb_ktoa;
+	enum txrx t;
+	int num_rings[NR_TXRX], tot_rings;
+	size_t entry_size[2];
+	void *csb_start[2];
+	int i;
+
+	if (priv->np_kloop_state & NM_SYNC_KLOOP_RUNNING) {
+		nm_prerr("Cannot update CSB while kloop is running");
+		return EBUSY;
+	}
 
+	tot_rings = 0;
 	for_rx_tx(t) {
-		for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
-			kring = &NMR(na, t)[i];
-			if (excl)
-				kring->nr_kflags &= ~NKR_EXCLUSIVE;
-			kring->users--;
-			if (kring->users == 0)
-				kring->nr_pending_mode = NKR_NETMAP_OFF;
+		num_rings[t] = priv->np_qlast[t] - priv->np_qfirst[t];
+		tot_rings += num_rings[t];
+	}
+	if (tot_rings <= 0)
+		return 0;
+
+	if (!(priv->np_flags & NR_EXCLUSIVE)) {
+		nm_prerr("CSB mode requires NR_EXCLUSIVE");
+		return EINVAL;
+	}
+
+	entry_size[0] = sizeof(*csb_atok_base);
+	entry_size[1] = sizeof(*csb_ktoa_base);
+	csb_start[0] = (void *)csb_atok_base;
+	csb_start[1] = (void *)csb_ktoa_base;
+
+	for (i = 0; i < 2; i++) {
+		/* On Linux we could use access_ok() to simplify
+		 * the validation. However, the advantage of
+		 * this approach is that it works also on
+		 * FreeBSD. */
+		size_t csb_size = tot_rings * entry_size[i];
+		void *tmp;
+		int err;
+
+		if ((uintptr_t)csb_start[i] & (entry_size[i]-1)) {
+			nm_prerr("Unaligned CSB address");
+			return EINVAL;
+		}
+
+		tmp = nm_os_malloc(csb_size);
+		if (!tmp)
+			return ENOMEM;
+		if (i == 0) {
+			/* Application --> kernel direction. */
+			err = copyin(csb_start[i], tmp, csb_size);
+		} else {
+			/* Kernel --> application direction. */
+			memset(tmp, 0, csb_size);
+			err = copyout(tmp, csb_start[i], csb_size);
+		}
+		nm_os_free(tmp);
+		if (err) {
+			nm_prerr("Invalid CSB address");
+			return err;
+		}
+	}
+
+	priv->np_csb_atok_base = csb_atok_base;
+	priv->np_csb_ktoa_base = csb_ktoa_base;
+
+	/* Initialize the CSB. */
+	for_rx_tx(t) {
+		for (i = 0; i < num_rings[t]; i++) {
+			struct netmap_kring *kring =
+				NMR(priv->np_na, t)[i + priv->np_qfirst[t]];
+			struct nm_csb_atok *csb_atok = csb_atok_base + i;
+			struct nm_csb_ktoa *csb_ktoa = csb_ktoa_base + i;
+
+			if (t == NR_RX) {
+				csb_atok += num_rings[NR_TX];
+				csb_ktoa += num_rings[NR_TX];
+			}
+
+			CSB_WRITE(csb_atok, head, kring->rhead);
+			CSB_WRITE(csb_atok, cur, kring->rcur);
+			CSB_WRITE(csb_atok, appl_need_kick, 1);
+			CSB_WRITE(csb_atok, sync_flags, 1);
+			CSB_WRITE(csb_ktoa, hwcur, kring->nr_hwcur);
+			CSB_WRITE(csb_ktoa, hwtail, kring->nr_hwtail);
+			CSB_WRITE(csb_ktoa, kern_need_kick, 1);
+
+			nm_prinf("csb_init for kring %s: head %u, cur %u, "
+				"hwcur %u, hwtail %u", kring->name,
+				kring->rhead, kring->rcur, kring->nr_hwcur,
+				kring->nr_hwtail);
+		}
+	}
+
+	return 0;
+}
+
+/* Ensure that the netmap adapter can support the given MTU.
+ * @return EINVAL if the na cannot be set to mtu, 0 otherwise.
+ */
+int
+netmap_buf_size_validate(const struct netmap_adapter *na, unsigned mtu) {
+	unsigned nbs = NETMAP_BUF_SIZE(na);
+
+	if (mtu <= na->rx_buf_maxsize) {
+		/* The MTU fits a single NIC slot. We only
+		 * Need to check that netmap buffers are
+		 * large enough to hold an MTU. NS_MOREFRAG
+		 * cannot be used in this case. */
+		if (nbs < mtu) {
+			nm_prerr("error: netmap buf size (%u) "
+				 "< device MTU (%u)", nbs, mtu);
+			return EINVAL;
+		}
+	} else {
+		/* More NIC slots may be needed to receive
+		 * or transmit a single packet. Check that
+		 * the adapter supports NS_MOREFRAG and that
+		 * netmap buffers are large enough to hold
+		 * the maximum per-slot size. */
+		if (!(na->na_flags & NAF_MOREFRAG)) {
+			nm_prerr("error: large MTU (%d) needed "
+				 "but %s does not support "
+				 "NS_MOREFRAG", mtu,
+				 if_name(na->ifp));
+			return EINVAL;
+		} else if (nbs < na->rx_buf_maxsize) {
+			nm_prerr("error: using NS_MOREFRAG on "
+				 "%s requires netmap buf size "
+				 ">= %u", if_name(na->ifp),
+				 na->rx_buf_maxsize);
+			return EINVAL;
+		} else {
+			nm_prinf("info: netmap application on "
+				 "%s needs to support "
+				 "NS_MOREFRAG "
+				 "(MTU=%u,netmap_buf_size=%u)",
+				 if_name(na->ifp), mtu, nbs);
+		}
+	}
+	return 0;
+}
+
+/* Handle the offset option, if present in the hdr.
+ * Returns 0 on success, or an error.
+ */
+static int
+netmap_offsets_init(struct netmap_priv_d *priv, struct nmreq_header *hdr)
+{
+	struct nmreq_opt_offsets *opt;
+	struct netmap_adapter *na = priv->np_na;
+	struct netmap_kring *kring;
+	uint64_t mask = 0, bits = 0, maxbits = sizeof(uint64_t) * 8,
+		 max_offset = 0, initial_offset = 0, min_gap = 0;
+	u_int i;
+	enum txrx t;
+	int error = 0;
+
+	opt = (struct nmreq_opt_offsets *)
+		nmreq_getoption(hdr, NETMAP_REQ_OPT_OFFSETS);
+	if (opt == NULL)
+		return 0;
+
+	if (!(na->na_flags & NAF_OFFSETS)) {
+		if (netmap_verbose)
+			nm_prerr("%s does not support offsets",
+				na->name);
+		error = EOPNOTSUPP;
+		goto out;
+	}
+
+	/* check sanity of the opt values */
+	max_offset = opt->nro_max_offset;
+	min_gap = opt->nro_min_gap;
+	initial_offset = opt->nro_initial_offset;
+	bits = opt->nro_offset_bits;
+
+	if (bits > maxbits) {
+		if (netmap_verbose)
+			nm_prerr("bits: %llu too large (max %llu)",
+				(unsigned long long)bits,
+				(unsigned long long)maxbits);
+		error = EINVAL;
+		goto out;
+	}
+	/* we take bits == 0 as a request to use the entire field */
+	if (bits == 0 || bits == maxbits) {
+		/* shifting a type by sizeof(type) is undefined */
+		bits = maxbits;
+		mask = 0xffffffffffffffff;
+	} else {
+		mask = (1ULL << bits) - 1;
+	}
+	if (max_offset > NETMAP_BUF_SIZE(na)) {
+		if (netmap_verbose)
+			nm_prerr("max offset %llu > buf size %u",
+				(unsigned long long)max_offset, NETMAP_BUF_SIZE(na));
+		error = EINVAL;
+		goto out;
+	}
+	if ((max_offset & mask) != max_offset) {
+		if (netmap_verbose)
+			nm_prerr("max offset %llu to large for %llu bits",
+				(unsigned long long)max_offset,
+				(unsigned long long)bits);
+		error = EINVAL;
+		goto out;
+	}
+	if (initial_offset > max_offset) {
+		if (netmap_verbose)
+			nm_prerr("initial offset %llu > max offset %llu",
+				(unsigned long long)initial_offset,
+				(unsigned long long)max_offset);
+		error = EINVAL;
+		goto out;
+	}
+
+	/* initialize the kring and ring fields. */
+	foreach_selected_ring(priv, t, i, kring) {
+		struct netmap_kring *kring = NMR(na, t)[i];
+		struct netmap_ring *ring = kring->ring;
+		u_int j;
+
+		/* it the ring is already in use we check that the
+		 * new request is compatible with the existing one
+		 */
+		if (kring->offset_mask) {
+			if ((kring->offset_mask & mask) != mask ||
+			     kring->offset_max < max_offset) {
+				if (netmap_verbose)
+					nm_prinf("%s: cannot increase"
+						 "offset mask and/or max"
+						 "(current: mask=%llx,max=%llu",
+							kring->name,
+							(unsigned long long)kring->offset_mask,
+							(unsigned long long)kring->offset_max);
+				error = EBUSY;
+				goto out;
+			}
+			mask = kring->offset_mask;
+			max_offset = kring->offset_max;
+		} else {
+			kring->offset_mask = mask;
+			*(uint64_t *)(uintptr_t)&ring->offset_mask = mask;
+			kring->offset_max = max_offset;
+			kring->offset_gap = min_gap;
+		}
+
+		/* if there is an initial offset, put it into
+		 * all the slots
+		 *
+		 * Note: we cannot change the offsets if the
+		 * ring is already in use.
+		 */
+		if (!initial_offset || kring->users > 1)
+			continue;
+
+		for (j = 0; j < kring->nkr_num_slots; j++) {
+			struct netmap_slot *slot = ring->slot + j;
+
+			nm_write_offset(kring, slot, initial_offset);
+		}
+	}
+
+out:
+	opt->nro_opt.nro_status = error;
+	if (!error) {
+		opt->nro_max_offset = max_offset;
+	}
+	return error;
+
+}
+
+
+/* set the hardware buffer length in each one of the newly opened rings
+ * (hwbuf_len field in the kring struct). The purpose it to select
+ * the maximum supported input buffer lenght that will not cause writes
+ * outside of the available space, even when offsets are in use.
+ */
+static int
+netmap_compute_buf_len(struct netmap_priv_d *priv)
+{
+	enum txrx t;
+	u_int i;
+	struct netmap_kring *kring;
+	int error = 0;
+	unsigned mtu = 0;
+	struct netmap_adapter *na = priv->np_na;
+	uint64_t target;
+
+	foreach_selected_ring(priv, t, i, kring) {
+		/* rings that are already active have their hwbuf_len
+		 * already set and we cannot change it.
+		 */
+		if (kring->users > 1)
+			continue;
+
+		/* For netmap buffers which are not shared among several ring
+		 * slots (the normal case), the available space is the buf size
+		 * minus the max offset declared by the user at open time.  If
+		 * the user plans to have several slots pointing to different
+		 * offsets into the same large buffer, she must also declare a
+		 * "minimum gap" between two such consecutive offsets. In this
+		 * case the user-declared 'offset_gap' is taken as the
+		 * available space and offset_max is ignored.
+		 */
+
+		/* start with the normal case (unshared buffers) */
+		target = NETMAP_BUF_SIZE(kring->na) -
+			kring->offset_max;
+		/* if offset_gap is zero, the user does not intend to use
+		 * shared buffers. In this case the minimum gap between
+		 * two consective offsets into the same buffer can be
+		 * assumed to be equal to the buffer size. In this way
+		 * offset_gap always contains the available space ignoring
+		 * offset_max. This may be used by drivers of NICs that
+		 * are guaranteed to never write more than MTU bytes, even
+		 * if the input buffer is larger: if the MTU is less
+		 * than the target they can set hwbuf_len to offset_gap.
+		 */
+		if (!kring->offset_gap)
+			kring->offset_gap =
+				NETMAP_BUF_SIZE(kring->na);
+
+		if (kring->offset_gap < target)
+			target = kring->offset_gap;
+		error = kring->nm_bufcfg(kring, target);
+		if (error)
+			goto out;
+
+		*(uint64_t *)(uintptr_t)&kring->ring->buf_align = kring->buf_align;
+
+		if (mtu && t == NR_RX && kring->hwbuf_len < mtu) {
+			if (!(na->na_flags & NAF_MOREFRAG)) {
+				nm_prerr("error: large MTU (%d) needed "
+					 "but %s does not support "
+					 "NS_MOREFRAG", mtu,
+					 na->name);
+				error = EINVAL;
+				goto out;
+			} else {
+				nm_prinf("info: netmap application on "
+					 "%s needs to support "
+					 "NS_MOREFRAG "
+					 "(MTU=%u,buf_size=%llu)",
+					 kring->name, mtu,
+					 (unsigned long long)kring->hwbuf_len);
+			}
 		}
 	}
+out:
+	return error;
 }
 
 /*
@@ -2033,28 +2566,58 @@ netmap_krings_put(struct netmap_priv_d *priv)
  */
 int
 netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
-	uint16_t ringid, uint32_t flags)
+	struct nmreq_header *hdr)
 {
 	struct netmap_if *nifp = NULL;
 	int error;
 
 	NMG_LOCK_ASSERT();
-	/* ring configuration may have changed, fetch from the card */
-	netmap_update_config(na);
 	priv->np_na = na;     /* store the reference */
-	error = netmap_set_ringid(priv, ringid, flags);
-	if (error)
-		goto err;
 	error = netmap_mem_finalize(na->nm_mem, na);
 	if (error)
 		goto err;
 
+	if (na->active_fds == 0) {
+
+		/* cache the allocator info in the na */
+		error = netmap_mem_get_lut(na->nm_mem, &na->na_lut);
+		if (error)
+			goto err_drop_mem;
+		nm_prdis("lut %p bufs %u size %u", na->na_lut.lut, na->na_lut.objtotal,
+					    na->na_lut.objsize);
+
+		/* ring configuration may have changed, fetch from the card */
+		netmap_update_config(na);
+	}
+
+	/* compute the range of tx and rx rings to monitor */
+	error = netmap_set_ringid(priv, hdr);
+	if (error)
+		goto err_put_lut;
+
 	if (na->active_fds == 0) {
 		/*
 		 * If this is the first registration of the adapter,
-		 * create the  in-kernel view of the netmap rings,
-		 * the netmap krings.
+		 * perform sanity checks and create the in-kernel view
+		 * of the netmap rings (the netmap krings).
 		 */
+		if (na->ifp && nm_priv_rx_enabled(priv)) {
+			/* This netmap adapter is attached to an ifnet. */
+			unsigned mtu = nm_os_ifnet_mtu(na->ifp);
+
+			nm_prdis("%s: mtu %d rx_buf_maxsize %d netmap_buf_size %d",
+				na->name, mtu, na->rx_buf_maxsize, NETMAP_BUF_SIZE(na));
+
+			if (na->rx_buf_maxsize == 0) {
+				nm_prerr("%s: error: rx_buf_maxsize == 0", na->name);
+				error = EIO;
+				goto err_drop_mem;
+			}
+
+			error = netmap_buf_size_validate(na, mtu);
+			if (error)
+				goto err_drop_mem;
+		}
 
 		/*
 		 * Depending on the adapter, this may also create
@@ -2062,7 +2625,7 @@ netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
 		 */
 		error = na->nm_krings_create(na);
 		if (error)
-			goto err_drop_mem;
+			goto err_put_lut;
 
 	}
 
@@ -2079,28 +2642,36 @@ netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
 	if (error)
 		goto err_rel_excl;
 
+	/* initialize offsets if requested */
+	error = netmap_offsets_init(priv, hdr);
+	if (error)
+		goto err_rel_excl;
+
+	/* compute and validate the buf lengths */
+	error = netmap_compute_buf_len(priv);
+	if (error)
+		goto err_rel_excl;
+
 	/* in all cases, create a new netmap if */
 	nifp = netmap_mem_if_new(na, priv);
 	if (nifp == NULL) {
 		error = ENOMEM;
-		goto err_del_rings;
+		goto err_rel_excl;
 	}
 
-	if (na->active_fds == 0) {
-		/* cache the allocator info in the na */
-		error = netmap_mem_get_lut(na->nm_mem, &na->na_lut);
-		if (error)
-			goto err_del_if;
-		ND("lut %p bufs %u size %u", na->na_lut.lut, na->na_lut.objtotal,
-					    na->na_lut.objsize);
-	}
+	/* make sure we don't call na->nm_register() when only
+	 * host rings are changing mode
+	 */
+	netmap_update_hostrings_mode(na);
 
-	if (nm_kring_pending(priv)) {
+	if (na->active_fds == 0 || nm_kring_pending(priv)) {
 		/* Some kring is switching mode, tell the adapter to
 		 * react on this. */
+		netmap_set_all_rings(na, NM_KR_LOCKED);
 		error = na->nm_register(na, 1);
+		netmap_set_all_rings(na, 0);
 		if (error)
-			goto err_put_lut;
+			goto err_del_if;
 	}
 
 	/* Commit the reference. */
@@ -2116,20 +2687,20 @@ netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
 
 	return 0;
 
-err_put_lut:
-	if (na->active_fds == 0)
-		memset(&na->na_lut, 0, sizeof(na->na_lut));
 err_del_if:
 	netmap_mem_if_delete(na, nifp);
 err_rel_excl:
 	netmap_krings_put(priv);
-err_del_rings:
+	netmap_update_hostrings_mode(na);
 	netmap_mem_rings_delete(na);
 err_del_krings:
 	if (na->active_fds == 0)
 		na->nm_krings_delete(na);
+err_put_lut:
+	if (na->active_fds == 0)
+		memset(&na->na_lut, 0, sizeof(na->na_lut));
 err_drop_mem:
-	netmap_mem_deref(na->nm_mem, na);
+	netmap_mem_drop(na);
 err:
 	priv->np_na = NULL;
 	return error;
@@ -2149,7 +2720,7 @@ nm_sync_finalize(struct netmap_kring *kring)
 	 */
 	kring->ring->tail = kring->rtail = kring->nr_hwtail;
 
-	ND(5, "%s now hwcur %d hwtail %d head %d cur %d tail %d",
+	nm_prdis(5, "%s now hwcur %d hwtail %d head %d cur %d tail %d",
 		kring->name, kring->nr_hwcur, kring->nr_hwtail,
 		kring->rhead, kring->rcur, kring->rtail);
 }
@@ -2163,237 +2734,462 @@ ring_timestamp_set(struct netmap_ring *ring)
 	}
 }
 
+static int nmreq_copyin(struct nmreq_header *, int);
+static int nmreq_copyout(struct nmreq_header *, int);
+static int nmreq_checkoptions(struct nmreq_header *);
 
 /*
  * ioctl(2) support for the "netmap" device.
  *
  * Following a list of accepted commands:
- * - NIOCGINFO
+ * - NIOCCTRL		device control API
+ * - NIOCTXSYNC		sync TX rings
+ * - NIOCRXSYNC		sync RX rings
  * - SIOCGIFADDR	just for convenience
- * - NIOCREGIF
- * - NIOCTXSYNC
- * - NIOCRXSYNC
+ * - NIOCGINFO		deprecated (legacy API)
+ * - NIOCREGIF		deprecated (legacy API)
  *
  * Return 0 on success, errno otherwise.
  */
 int
-netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread *td)
+netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
+		struct thread *td, int nr_body_is_user)
 {
 	struct mbq q;	/* packets from RX hw queues to host stack */
-	struct nmreq *nmr = (struct nmreq *) data;
 	struct netmap_adapter *na = NULL;
 	struct netmap_mem_d *nmd = NULL;
-	struct ifnet *ifp = NULL;
+	if_t ifp = NULL;
 	int error = 0;
 	u_int i, qfirst, qlast;
-	struct netmap_if *nifp;
-	struct netmap_kring *krings;
+	struct netmap_kring **krings;
 	int sync_flags;
 	enum txrx t;
 
-	if (cmd == NIOCGINFO || cmd == NIOCREGIF) {
-		/* truncate name */
-		nmr->nr_name[sizeof(nmr->nr_name) - 1] = '\0';
-		if (nmr->nr_version != NETMAP_API) {
-			D("API mismatch for %s got %d need %d",
-				nmr->nr_name,
-				nmr->nr_version, NETMAP_API);
-			nmr->nr_version = NETMAP_API;
-		}
-		if (nmr->nr_version < NETMAP_MIN_API ||
-		    nmr->nr_version > NETMAP_MAX_API) {
+	switch (cmd) {
+	case NIOCCTRL: {
+		struct nmreq_header *hdr = (struct nmreq_header *)data;
+
+		if (hdr->nr_version < NETMAP_MIN_API ||
+		    hdr->nr_version > NETMAP_MAX_API) {
+			nm_prerr("API mismatch: got %d need %d",
+				hdr->nr_version, NETMAP_API);
 			return EINVAL;
 		}
-	}
 
-	switch (cmd) {
-	case NIOCGINFO:		/* return capabilities etc */
-		if (nmr->nr_cmd == NETMAP_BDG_LIST) {
-			error = netmap_bdg_ctl(nmr, NULL);
-			break;
+		/* Make a kernel-space copy of the user-space nr_body.
+		 * For convenience, the nr_body pointer and the pointers
+		 * in the options list will be replaced with their
+		 * kernel-space counterparts. The original pointers are
+		 * saved internally and later restored by nmreq_copyout
+		 */
+		error = nmreq_copyin(hdr, nr_body_is_user);
+		if (error) {
+			return error;
 		}
 
-		NMG_LOCK();
-		do {
-			/* memsize is always valid */
-			u_int memflags;
+		/* Sanitize hdr->nr_name. */
+		hdr->nr_name[sizeof(hdr->nr_name) - 1] = '\0';
+
+		switch (hdr->nr_reqtype) {
+		case NETMAP_REQ_REGISTER: {
+			struct nmreq_register *req =
+				(struct nmreq_register *)(uintptr_t)hdr->nr_body;
+			struct netmap_if *nifp;
+
+			/* Protect access to priv from concurrent requests. */
+			NMG_LOCK();
+			do {
+				struct nmreq_option *opt;
+				u_int memflags;
+
+				if (priv->np_nifp != NULL) {	/* thread already registered */
+					error = EBUSY;
+					break;
+				}
+
+#ifdef WITH_EXTMEM
+				opt = nmreq_getoption(hdr, NETMAP_REQ_OPT_EXTMEM);
+				if (opt != NULL) {
+					struct nmreq_opt_extmem *e =
+						(struct nmreq_opt_extmem *)opt;
+
+					nmd = netmap_mem_ext_create(e->nro_usrptr,
+							&e->nro_info, &error);
+					opt->nro_status = error;
+					if (nmd == NULL)
+						break;
+				}
+#endif /* WITH_EXTMEM */
+
+				if (nmd == NULL && req->nr_mem_id) {
+					/* find the allocator and get a reference */
+					nmd = netmap_mem_find(req->nr_mem_id);
+					if (nmd == NULL) {
+						if (netmap_verbose) {
+							nm_prerr("%s: failed to find mem_id %u",
+									hdr->nr_name, req->nr_mem_id);
+						}
+						error = EINVAL;
+						break;
+					}
+				}
+				/* find the interface and a reference */
+				error = netmap_get_na(hdr, &na, &ifp, nmd,
+						      1 /* create */); /* keep reference */
+				if (error)
+					break;
+				if (NETMAP_OWNED_BY_KERN(na)) {
+					error = EBUSY;
+					break;
+				}
+
+				if (na->virt_hdr_len && !(req->nr_flags & NR_ACCEPT_VNET_HDR)) {
+					nm_prerr("virt_hdr_len=%d, but application does "
+						"not accept it", na->virt_hdr_len);
+					error = EIO;
+					break;
+				}
+
+				error = netmap_do_regif(priv, na, hdr);
+				if (error) {    /* reg. failed, release priv and ref */
+					break;
+				}
 
-			if (nmr->nr_name[0] != '\0') {
+				opt = nmreq_getoption(hdr, NETMAP_REQ_OPT_CSB);
+				if (opt != NULL) {
+					struct nmreq_opt_csb *csbo =
+						(struct nmreq_opt_csb *)opt;
+					error = netmap_csb_validate(priv, csbo);
+					opt->nro_status = error;
+					if (error) {
+						netmap_do_unregif(priv);
+						break;
+					}
+				}
 
-				/* get a refcount */
-				error = netmap_get_na(nmr, &na, &ifp, NULL, 1 /* create */);
+				nifp = priv->np_nifp;
+
+				/* return the offset of the netmap_if object */
+				req->nr_rx_rings = na->num_rx_rings;
+				req->nr_tx_rings = na->num_tx_rings;
+				req->nr_rx_slots = na->num_rx_desc;
+				req->nr_tx_slots = na->num_tx_desc;
+				req->nr_host_tx_rings = na->num_host_tx_rings;
+				req->nr_host_rx_rings = na->num_host_rx_rings;
+				error = netmap_mem_get_info(na->nm_mem, &req->nr_memsize, &memflags,
+					&req->nr_mem_id);
 				if (error) {
-					na = NULL;
-					ifp = NULL;
+					netmap_do_unregif(priv);
 					break;
 				}
-				nmd = na->nm_mem; /* get memory allocator */
-			} else {
-				nmd = netmap_mem_find(nmr->nr_arg2 ? nmr->nr_arg2 : 1);
-				if (nmd == NULL) {
-					error = EINVAL;
+				if (memflags & NETMAP_MEM_PRIVATE) {
+					*(uint32_t *)(uintptr_t)&nifp->ni_flags |= NI_PRIV_MEM;
+				}
+				for_rx_tx(t) {
+					priv->np_si[t] = nm_si_user(priv, t) ?
+						&na->si[t] : &NMR(na, t)[priv->np_qfirst[t]]->si;
+				}
+
+				if (req->nr_extra_bufs) {
+					if (netmap_verbose)
+						nm_prinf("requested %d extra buffers",
+							req->nr_extra_bufs);
+					req->nr_extra_bufs = netmap_extra_alloc(na,
+						&nifp->ni_bufs_head, req->nr_extra_bufs);
+					if (netmap_verbose)
+						nm_prinf("got %d extra buffers", req->nr_extra_bufs);
+				} else {
+					nifp->ni_bufs_head = 0;
+				}
+				req->nr_offset = netmap_mem_if_offset(na->nm_mem, nifp);
+
+				error = nmreq_checkoptions(hdr);
+				if (error) {
+					netmap_do_unregif(priv);
 					break;
 				}
+
+				/* store ifp reference so that priv destructor may release it */
+				priv->np_ifp = ifp;
+			} while (0);
+			if (error) {
+				netmap_unget_na(na, ifp);
 			}
+			/* release the reference from netmap_mem_find() or
+			 * netmap_mem_ext_create()
+			 */
+			if (nmd)
+				netmap_mem_put(nmd);
+			NMG_UNLOCK();
+			break;
+		}
 
-			error = netmap_mem_get_info(nmd, &nmr->nr_memsize, &memflags,
-				&nmr->nr_arg2);
-			if (error)
-				break;
-			if (na == NULL) /* only memory info */
-				break;
-			nmr->nr_offset = 0;
-			nmr->nr_rx_slots = nmr->nr_tx_slots = 0;
-			netmap_update_config(na);
-			nmr->nr_rx_rings = na->num_rx_rings;
-			nmr->nr_tx_rings = na->num_tx_rings;
-			nmr->nr_rx_slots = na->num_rx_desc;
-			nmr->nr_tx_slots = na->num_tx_desc;
-		} while (0);
-		netmap_unget_na(na, ifp);
-		NMG_UNLOCK();
-		break;
+		case NETMAP_REQ_PORT_INFO_GET: {
+			struct nmreq_port_info_get *req =
+				(struct nmreq_port_info_get *)(uintptr_t)hdr->nr_body;
+			int nmd_ref = 0;
 
-	case NIOCREGIF:
-		/*
-		 * If nmr->nr_cmd is not zero, this NIOCREGIF is not really
-		 * a regif operation, but a different one, specified by the
-		 * value of nmr->nr_cmd.
-		 */
-		i = nmr->nr_cmd;
-		if (i == NETMAP_BDG_ATTACH || i == NETMAP_BDG_DETACH
-				|| i == NETMAP_BDG_VNET_HDR
-				|| i == NETMAP_BDG_NEWIF
-				|| i == NETMAP_BDG_DELIF
-				|| i == NETMAP_BDG_POLLING_ON
-				|| i == NETMAP_BDG_POLLING_OFF) {
-			/* possibly attach/detach NIC and VALE switch */
-			error = netmap_bdg_ctl(nmr, NULL);
+			NMG_LOCK();
+			do {
+				u_int memflags;
+
+				if (hdr->nr_name[0] != '\0') {
+					/* Build a nmreq_register out of the nmreq_port_info_get,
+					 * so that we can call netmap_get_na(). */
+					struct nmreq_register regreq;
+					bzero(®req, sizeof(regreq));
+					regreq.nr_mode = NR_REG_ALL_NIC;
+					regreq.nr_tx_slots = req->nr_tx_slots;
+					regreq.nr_rx_slots = req->nr_rx_slots;
+					regreq.nr_tx_rings = req->nr_tx_rings;
+					regreq.nr_rx_rings = req->nr_rx_rings;
+					regreq.nr_host_tx_rings = req->nr_host_tx_rings;
+					regreq.nr_host_rx_rings = req->nr_host_rx_rings;
+					regreq.nr_mem_id = req->nr_mem_id;
+
+					/* get a refcount */
+					hdr->nr_reqtype = NETMAP_REQ_REGISTER;
+					hdr->nr_body = (uintptr_t)®req;
+					error = netmap_get_na(hdr, &na, &ifp, NULL, 1 /* create */);
+					hdr->nr_reqtype = NETMAP_REQ_PORT_INFO_GET; /* reset type */
+					hdr->nr_body = (uintptr_t)req; /* reset nr_body */
+					if (error) {
+						na = NULL;
+						ifp = NULL;
+						break;
+					}
+					nmd = na->nm_mem; /* get memory allocator */
+				} else {
+					nmd = netmap_mem_find(req->nr_mem_id ? req->nr_mem_id : 1);
+					if (nmd == NULL) {
+						if (netmap_verbose)
+							nm_prerr("%s: failed to find mem_id %u",
+									hdr->nr_name,
+									req->nr_mem_id ? req->nr_mem_id : 1);
+						error = EINVAL;
+						break;
+					}
+					nmd_ref = 1;
+				}
+
+				error = netmap_mem_get_info(nmd, &req->nr_memsize, &memflags,
+					&req->nr_mem_id);
+				if (error)
+					break;
+				if (na == NULL) /* only memory info */
+					break;
+				netmap_update_config(na);
+				req->nr_rx_rings = na->num_rx_rings;
+				req->nr_tx_rings = na->num_tx_rings;
+				req->nr_rx_slots = na->num_rx_desc;
+				req->nr_tx_slots = na->num_tx_desc;
+				req->nr_host_tx_rings = na->num_host_tx_rings;
+				req->nr_host_rx_rings = na->num_host_rx_rings;
+			} while (0);
+			netmap_unget_na(na, ifp);
+			if (nmd_ref)
+				netmap_mem_put(nmd);
+			NMG_UNLOCK();
 			break;
-		} else if (i == NETMAP_PT_HOST_CREATE || i == NETMAP_PT_HOST_DELETE) {
-			/* forward the command to the ptnetmap subsystem */
-			error = ptnetmap_ctl(nmr, priv->np_na);
+		}
+#ifdef WITH_VALE
+		case NETMAP_REQ_VALE_ATTACH: {
+			error = netmap_bdg_attach(hdr, NULL /* userspace request */);
 			break;
-		} else if (i == NETMAP_VNET_HDR_GET) {
-			/* get vnet-header length for this netmap port */
-			struct ifnet *ifp;
+		}
 
+		case NETMAP_REQ_VALE_DETACH: {
+			error = netmap_bdg_detach(hdr, NULL /* userspace request */);
+			break;
+		}
+
+		case NETMAP_REQ_PORT_HDR_SET: {
+			struct nmreq_port_hdr *req =
+				(struct nmreq_port_hdr *)(uintptr_t)hdr->nr_body;
+			/* Build a nmreq_register out of the nmreq_port_hdr,
+			 * so that we can call netmap_get_bdg_na(). */
+			struct nmreq_register regreq;
+			bzero(®req, sizeof(regreq));
+			regreq.nr_mode = NR_REG_ALL_NIC;
+
+			/* For now we only support virtio-net headers, and only for
+			 * VALE ports, but this may change in future. Valid lengths
+			 * for the virtio-net header are 0 (no header), 10 and 12. */
+			if (req->nr_hdr_len != 0 &&
+				req->nr_hdr_len != sizeof(struct nm_vnet_hdr) &&
+					req->nr_hdr_len != 12) {
+				if (netmap_verbose)
+					nm_prerr("invalid hdr_len %u", req->nr_hdr_len);
+				error = EINVAL;
+				break;
+			}
 			NMG_LOCK();
-			error = netmap_get_na(nmr, &na, &ifp, NULL, 0);
+			hdr->nr_reqtype = NETMAP_REQ_REGISTER;
+			hdr->nr_body = (uintptr_t)®req;
+			error = netmap_get_vale_na(hdr, &na, NULL, 0);
+			hdr->nr_reqtype = NETMAP_REQ_PORT_HDR_SET;
+			hdr->nr_body = (uintptr_t)req;
 			if (na && !error) {
-				nmr->nr_arg1 = na->virt_hdr_len;
+				struct netmap_vp_adapter *vpna =
+					(struct netmap_vp_adapter *)na;
+				na->virt_hdr_len = req->nr_hdr_len;
+				if (na->virt_hdr_len) {
+					vpna->mfs = NETMAP_BUF_SIZE(na);
+				}
+				if (netmap_verbose)
+					nm_prinf("Using vnet_hdr_len %d for %p", na->virt_hdr_len, na);
+				netmap_adapter_put(na);
+			} else if (!na) {
+				error = ENXIO;
 			}
-			netmap_unget_na(na, ifp);
 			NMG_UNLOCK();
 			break;
-		} else if (i == NETMAP_POOLS_INFO_GET) {
-			/* get information from the memory allocator */
-			error = netmap_mem_pools_info_get(nmr, priv->np_na);
+		}
+
+		case NETMAP_REQ_PORT_HDR_GET: {
+			/* Get vnet-header length for this netmap port */
+			struct nmreq_port_hdr *req =
+				(struct nmreq_port_hdr *)(uintptr_t)hdr->nr_body;
+			/* Build a nmreq_register out of the nmreq_port_hdr,
+			 * so that we can call netmap_get_bdg_na(). */
+			struct nmreq_register regreq;
+			if_t ifp;
+
+			bzero(®req, sizeof(regreq));
+			regreq.nr_mode = NR_REG_ALL_NIC;
+			NMG_LOCK();
+			hdr->nr_reqtype = NETMAP_REQ_REGISTER;
+			hdr->nr_body = (uintptr_t)®req;
+			error = netmap_get_na(hdr, &na, &ifp, NULL, 0);
+			hdr->nr_reqtype = NETMAP_REQ_PORT_HDR_GET;
+			hdr->nr_body = (uintptr_t)req;
+			if (na && !error) {
+				req->nr_hdr_len = na->virt_hdr_len;
+			}
+			netmap_unget_na(na, ifp);
+			NMG_UNLOCK();
 			break;
-		} else if (i != 0) {
-			D("nr_cmd must be 0 not %d", i);
-			error = EINVAL;
+		}
+
+		case NETMAP_REQ_VALE_LIST: {
+			error = netmap_vale_list(hdr);
 			break;
 		}
 
-		/* protect access to priv from concurrent NIOCREGIF */
-		NMG_LOCK();
-		do {
-			u_int memflags;
-			struct ifnet *ifp;
+		case NETMAP_REQ_VALE_NEWIF: {
+			error = nm_vi_create(hdr);
+			break;
+		}
 
-			if (priv->np_nifp != NULL) {	/* thread already registered */
-				error = EBUSY;
-				break;
-			}
+		case NETMAP_REQ_VALE_DELIF: {
+			error = nm_vi_destroy(hdr->nr_name);
+			break;
+		}
+#endif  /* WITH_VALE */
 
-			if (nmr->nr_arg2) {
-				nmd = netmap_mem_find(nmr->nr_arg2);
+		case NETMAP_REQ_VALE_POLLING_ENABLE:
+		case NETMAP_REQ_VALE_POLLING_DISABLE: {
+			error = nm_bdg_polling(hdr);
+			break;
+		}
+		case NETMAP_REQ_POOLS_INFO_GET: {
+			/* Get information from the memory allocator used for
+			 * hdr->nr_name. */
+			struct nmreq_pools_info *req =
+				(struct nmreq_pools_info *)(uintptr_t)hdr->nr_body;
+			NMG_LOCK();
+			do {
+				/* Build a nmreq_register out of the nmreq_pools_info,
+				 * so that we can call netmap_get_na(). */
+				struct nmreq_register regreq;
+				bzero(®req, sizeof(regreq));
+				regreq.nr_mem_id = req->nr_mem_id;
+				regreq.nr_mode = NR_REG_ALL_NIC;
+
+				hdr->nr_reqtype = NETMAP_REQ_REGISTER;
+				hdr->nr_body = (uintptr_t)®req;
+				error = netmap_get_na(hdr, &na, &ifp, NULL, 1 /* create */);
+				hdr->nr_reqtype = NETMAP_REQ_POOLS_INFO_GET; /* reset type */
+				hdr->nr_body = (uintptr_t)req; /* reset nr_body */
+				if (error) {
+					na = NULL;
+					ifp = NULL;
+					break;
+				}
+				nmd = na->nm_mem; /* grab the memory allocator */
 				if (nmd == NULL) {
 					error = EINVAL;
 					break;
 				}
-			}
-			/* find the interface and a reference */
-			error = netmap_get_na(nmr, &na, &ifp, nmd,
-					      1 /* create */); /* keep reference */
-			if (error)
-				break;
-			if (NETMAP_OWNED_BY_KERN(na)) {
-				error = EBUSY;
-				break;
-			}
 
-			if (na->virt_hdr_len && !(nmr->nr_flags & NR_ACCEPT_VNET_HDR)) {
-				error = EIO;
-				break;
-			}
+				/* Finalize the memory allocator, get the pools
+				 * information and release the allocator. */
+				error = netmap_mem_finalize(nmd, na);
+				if (error) {
+					break;
+				}
+				error = netmap_mem_pools_info_get(req, nmd);
+				netmap_mem_drop(na);
+			} while (0);
+			netmap_unget_na(na, ifp);
+			NMG_UNLOCK();
+			break;
+		}
 
-			error = netmap_do_regif(priv, na, nmr->nr_ringid, nmr->nr_flags);
-			if (error) {    /* reg. failed, release priv and ref */
-				break;
-			}
-			nifp = priv->np_nifp;
-			priv->np_td = td; // XXX kqueue, debugging only
-
-			/* return the offset of the netmap_if object */
-			nmr->nr_rx_rings = na->num_rx_rings;
-			nmr->nr_tx_rings = na->num_tx_rings;
-			nmr->nr_rx_slots = na->num_rx_desc;
-			nmr->nr_tx_slots = na->num_tx_desc;
-			error = netmap_mem_get_info(na->nm_mem, &nmr->nr_memsize, &memflags,
-				&nmr->nr_arg2);
-			if (error) {
-				netmap_do_unregif(priv);
-				break;
-			}
-			if (memflags & NETMAP_MEM_PRIVATE) {
-				*(uint32_t *)(uintptr_t)&nifp->ni_flags |= NI_PRIV_MEM;
-			}
-			for_rx_tx(t) {
-				priv->np_si[t] = nm_si_user(priv, t) ?
-					&na->si[t] : &NMR(na, t)[priv->np_qfirst[t]].si;
-			}
+		case NETMAP_REQ_CSB_ENABLE: {
+			struct nmreq_option *opt;
 
-			if (nmr->nr_arg3) {
-				if (netmap_verbose)
-					D("requested %d extra buffers", nmr->nr_arg3);
-				nmr->nr_arg3 = netmap_extra_alloc(na,
-					&nifp->ni_bufs_head, nmr->nr_arg3);
-				if (netmap_verbose)
-					D("got %d extra buffers", nmr->nr_arg3);
+			opt = nmreq_getoption(hdr, NETMAP_REQ_OPT_CSB);
+			if (opt == NULL) {
+				error = EINVAL;
+			} else {
+				struct nmreq_opt_csb *csbo =
+					(struct nmreq_opt_csb *)opt;
+				NMG_LOCK();
+				error = netmap_csb_validate(priv, csbo);
+				NMG_UNLOCK();
+				opt->nro_status = error;
 			}
-			nmr->nr_offset = netmap_mem_if_offset(na->nm_mem, nifp);
+			break;
+		}
 
-			/* store ifp reference so that priv destructor may release it */
-			priv->np_ifp = ifp;
-		} while (0);
-		if (error) {
-			netmap_unget_na(na, ifp);
-			if (nmd)
-				netmap_mem_put(nmd);
+		case NETMAP_REQ_SYNC_KLOOP_START: {
+			error = netmap_sync_kloop(priv, hdr);
+			break;
 		}
-		NMG_UNLOCK();
+
+		case NETMAP_REQ_SYNC_KLOOP_STOP: {
+			error = netmap_sync_kloop_stop(priv);
+			break;
+		}
+
+		default: {
+			error = EINVAL;
+			break;
+		}
+		}
+		/* Write back request body to userspace and reset the
+		 * user-space pointer. */
+		error = nmreq_copyout(hdr, error);
 		break;
+	}
 
 	case NIOCTXSYNC:
-	case NIOCRXSYNC:
-		nifp = priv->np_nifp;
-
-		if (nifp == NULL) {
+	case NIOCRXSYNC: {
+		if (unlikely(priv->np_nifp == NULL)) {
 			error = ENXIO;
 			break;
 		}
 		mb(); /* make sure following reads are not from cache */
 
-		na = priv->np_na;      /* we have a reference */
-
-		if (na == NULL) {
-			D("Internal error: nifp != NULL && na == NULL");
-			error = ENXIO;
+		if (unlikely(priv->np_csb_atok_base)) {
+			nm_prerr("Invalid sync in CSB mode");
+			error = EBUSY;
 			break;
 		}
 
+		na = priv->np_na;      /* we have a reference */
+
 		mbq_init(&q);
 		t = (cmd == NIOCTXSYNC ? NR_TX : NR_RX);
 		krings = NMR(na, t);
@@ -2402,7 +3198,7 @@ netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread
 		sync_flags = priv->np_sync_flags;
 
 		for (i = qfirst; i < qlast; i++) {
-			struct netmap_kring *kring = krings + i;
+			struct netmap_kring *kring = krings[i];
 			struct netmap_ring *ring = kring->ring;
 
 			if (unlikely(nm_kr_tryget(kring, 1, &error))) {
@@ -2411,8 +3207,8 @@ netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread
 			}
 
 			if (cmd == NIOCTXSYNC) {
-				if (netmap_verbose & NM_VERB_TXSYNC)
-					D("pre txsync ring %d cur %d hwcur %d",
+				if (netmap_debug & NM_DEBUG_TXSYNC)
+					nm_prinf("pre txsync ring %d cur %d hwcur %d",
 					    i, ring->cur,
 					    kring->nr_hwcur);
 				if (nm_txsync_prologue(kring, ring) >= kring->nkr_num_slots) {
@@ -2420,8 +3216,8 @@ netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread
 				} else if (kring->nm_sync(kring, sync_flags | NAF_FORCE_RECLAIM) == 0) {
 					nm_sync_finalize(kring);
 				}
-				if (netmap_verbose & NM_VERB_TXSYNC)
-					D("post txsync ring %d cur %d hwcur %d",
+				if (netmap_debug & NM_DEBUG_TXSYNC)
+					nm_prinf("post txsync ring %d cur %d hwcur %d",
 					    i, ring->cur,
 					    kring->nr_hwcur);
 			} else {
@@ -2445,59 +3241,424 @@ netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread
 		}
 
 		break;
+	}
 
-#ifdef WITH_VALE
-	case NIOCCONFIG:
-		error = netmap_bdg_config(nmr);
-		break;
-#endif
-#ifdef __FreeBSD__
-	case FIONBIO:
-	case FIOASYNC:
-		ND("FIONBIO/FIOASYNC are no-ops");
+	default: {
+		return netmap_ioctl_legacy(priv, cmd, data, td);
 		break;
+	}
+	}
+
+	return (error);
+}
 
-	case BIOCIMMEDIATE:
-	case BIOCGHDRCMPLT:
-	case BIOCSHDRCMPLT:
-	case BIOCSSEESENT:
-		D("ignore BIOCIMMEDIATE/BIOCSHDRCMPLT/BIOCSHDRCMPLT/BIOCSSEESENT");
+size_t
+nmreq_size_by_type(uint16_t nr_reqtype)
+{
+	switch (nr_reqtype) {
+	case NETMAP_REQ_REGISTER:
+		return sizeof(struct nmreq_register);
+	case NETMAP_REQ_PORT_INFO_GET:
+		return sizeof(struct nmreq_port_info_get);
+	case NETMAP_REQ_VALE_ATTACH:
+		return sizeof(struct nmreq_vale_attach);
+	case NETMAP_REQ_VALE_DETACH:
+		return sizeof(struct nmreq_vale_detach);
+	case NETMAP_REQ_VALE_LIST:
+		return sizeof(struct nmreq_vale_list);
+	case NETMAP_REQ_PORT_HDR_SET:
+	case NETMAP_REQ_PORT_HDR_GET:
+		return sizeof(struct nmreq_port_hdr);
+	case NETMAP_REQ_VALE_NEWIF:
+		return sizeof(struct nmreq_vale_newif);
+	case NETMAP_REQ_VALE_DELIF:
+	case NETMAP_REQ_SYNC_KLOOP_STOP:
+	case NETMAP_REQ_CSB_ENABLE:
+		return 0;
+	case NETMAP_REQ_VALE_POLLING_ENABLE:
+	case NETMAP_REQ_VALE_POLLING_DISABLE:
+		return sizeof(struct nmreq_vale_polling);
+	case NETMAP_REQ_POOLS_INFO_GET:
+		return sizeof(struct nmreq_pools_info);
+	case NETMAP_REQ_SYNC_KLOOP_START:
+		return sizeof(struct nmreq_sync_kloop_start);
+	}
+	return 0;
+}
+
+static size_t
+nmreq_opt_size_by_type(uint32_t nro_reqtype, uint64_t nro_size)
+{
+	size_t rv = sizeof(struct nmreq_option);
+#ifdef NETMAP_REQ_OPT_DEBUG
+	if (nro_reqtype & NETMAP_REQ_OPT_DEBUG)
+		return (nro_reqtype & ~NETMAP_REQ_OPT_DEBUG);
+#endif /* NETMAP_REQ_OPT_DEBUG */
+	switch (nro_reqtype) {
+#ifdef WITH_EXTMEM
+	case NETMAP_REQ_OPT_EXTMEM:
+		rv = sizeof(struct nmreq_opt_extmem);
+		break;
+#endif /* WITH_EXTMEM */
+	case NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS:
+		if (nro_size >= rv)
+			rv = nro_size;
+		break;
+	case NETMAP_REQ_OPT_CSB:
+		rv = sizeof(struct nmreq_opt_csb);
+		break;
+	case NETMAP_REQ_OPT_SYNC_KLOOP_MODE:
+		rv = sizeof(struct nmreq_opt_sync_kloop_mode);
 		break;
+	case NETMAP_REQ_OPT_OFFSETS:
+		rv = sizeof(struct nmreq_opt_offsets);
+		break;
+	}
+	/* subtract the common header */
+	return rv - sizeof(struct nmreq_option);
+}
 
-	default:	/* allow device-specific ioctls */
-	    {
-		struct ifnet *ifp = ifunit_ref(nmr->nr_name);
-		if (ifp == NULL) {
-			error = ENXIO;
-		} else {
-			struct socket so;
+/*
+ * nmreq_copyin: create an in-kernel version of the request.
+ *
+ * We build the following data structure:
+ *
+ * hdr -> +-------+                buf
+ *        |       |          +---------------+
+ *        +-------+          |usr body ptr   |
+ *        |options|-.        +---------------+
+ *        +-------+ |        |usr options ptr|
+ *        |body   |--------->+---------------+
+ *        +-------+ |        |               |
+ *                  |        |  copy of body |
+ *                  |        |               |
+ *                  |        +---------------+
+ *                  |        |    NULL       |
+ *                  |        +---------------+
+ *                  |    .---|               |\
+ *                  |    |   +---------------+ |
+ *                  | .------|               | |
+ *                  | |  |   +---------------+  \ option table
+ *                  | |  |   |      ...      |  / indexed by option
+ *                  | |  |   +---------------+ |  type
+ *                  | |  |   |               | |
+ *                  | |  |   +---------------+/
+ *                  | |  |   |usr next ptr 1 |
+ *                  `-|----->+---------------+
+ *                    |  |   | copy of opt 1 |
+ *                    |  |   |               |
+ *                    |  | .-| nro_next      |
+ *                    |  | | +---------------+
+ *                    |  | | |usr next ptr 2 |
+ *                    |  `-`>+---------------+
+ *                    |      | copy of opt 2 |
+ *                    |      |               |
+ *                    |    .-| nro_next      |
+ *                    |    | +---------------+
+ *                    |    | |               |
+ *                    ~    ~ ~      ...      ~
+ *                    |    .-|               |
+ *                    `----->+---------------+
+ *                         | |usr next ptr n |
+ *                         `>+---------------+
+ *                           | copy of opt n |
+ *                           |               |
+ *                           | nro_next(NULL)|
+ *                           +---------------+
+ *
+ * The options and body fields of the hdr structure are overwritten
+ * with in-kernel valid pointers inside the buf. The original user
+ * pointers are saved in the buf and restored on copyout.
+ * The list of options is copied and the pointers adjusted. The
+ * original pointers are saved before the option they belonged.
+ *
+ * The option table has an entry for every available option.  Entries
+ * for options that have not been passed contain NULL.
+ *
+ */
 
-			bzero(&so, sizeof(so));
-			so.so_vnet = ifp->if_vnet;
-			// so->so_proto not null.
-			error = ifioctl(&so, cmd, data, td);
-			if_rele(ifp);
+int
+nmreq_copyin(struct nmreq_header *hdr, int nr_body_is_user)
+{
+	size_t rqsz, optsz, bufsz;
+	int error = 0;
+	char *ker = NULL, *p;
+	struct nmreq_option **next, *src, **opt_tab, *opt;
+	uint64_t *ptrs;
+
+	if (hdr->nr_reserved) {
+		if (netmap_verbose)
+			nm_prerr("nr_reserved must be zero");
+		return EINVAL;
+	}
+
+	if (!nr_body_is_user)
+		return 0;
+
+	hdr->nr_reserved = nr_body_is_user;
+
+	/* compute the total size of the buffer */
+	rqsz = nmreq_size_by_type(hdr->nr_reqtype);
+	if (rqsz > NETMAP_REQ_MAXSIZE) {
+		error = EMSGSIZE;
+		goto out_err;
+	}
+	if ((rqsz && hdr->nr_body == (uintptr_t)NULL) ||
+		(!rqsz && hdr->nr_body != (uintptr_t)NULL)) {
+		/* Request body expected, but not found; or
+		 * request body found but unexpected. */
+		if (netmap_verbose)
+			nm_prerr("nr_body expected but not found, or vice versa");
+		error = EINVAL;
+		goto out_err;
+	}
+
+	/*
+	 * The buffer size must be large enough to store the request body,
+	 * all the possible options and the additional user pointers
+	 * (2+NETMAP_REQ_OPT_MAX). Note that the maximum size of body plus
+	 * options can not exceed NETMAP_REQ_MAXSIZE;
+	 */
+	bufsz = (2 + NETMAP_REQ_OPT_MAX) * sizeof(void *) + NETMAP_REQ_MAXSIZE +
+		NETMAP_REQ_OPT_MAX * sizeof(opt_tab);
+
+	ker = nm_os_malloc(bufsz);
+	if (ker == NULL) {
+		error = ENOMEM;
+		goto out_err;
+	}
+	p = ker;	/* write pointer into the buffer */
+
+	/* make a copy of the user pointers */
+	ptrs = (uint64_t*)p;
+	*ptrs++ = hdr->nr_body;
+	*ptrs++ = hdr->nr_options;
+	p = (char *)ptrs;
+	/* overwrite the user pointer with the in-kernel one */
+	hdr->nr_body = (uintptr_t)p;
+	/* prepare the options-list pointers and temporarily terminate
+	 * the in-kernel list, in case we have to jump to out_restore
+	 */
+	next = (struct nmreq_option **)&hdr->nr_options;
+	src = *next;
+	hdr->nr_options = 0;
+
+	/* copy the body */
+	error = copyin(*(void **)ker, p, rqsz);
+	if (error)
+		goto out_restore;
+	p += rqsz;
+	/* start of the options table */
+	opt_tab = (struct nmreq_option **)p;
+	p += sizeof(opt_tab) * NETMAP_REQ_OPT_MAX;
+
+	/* copy the options */
+	while (src) {
+		struct nmreq_option *nsrc;
+
+		if (p - ker + sizeof(uint64_t*) + sizeof(*src) > bufsz) {
+			error = EMSGSIZE;
+			/* there might be a loop in the list: don't try to
+			 * copyout the options
+			 */
+			hdr->nr_options = 0;
+			goto out_restore;
 		}
-		break;
-	    }
+		/* copy the option header */
+		ptrs = (uint64_t *)p;
+		opt = (struct nmreq_option *)(ptrs + 1);
+		error = copyin(src, opt, sizeof(*src));
+		if (error)
+			goto out_restore;
+		rqsz += sizeof(*src);
+		p = (char *)(opt + 1);
+
+		/* make a copy of the user next pointer */
+		*ptrs = opt->nro_next;
+		/* append the option to the in-kernel list */
+		*next = opt;
+		/* temporarily teminate the in-kernel list, in case we have to
+		 * jump to out_restore
+		 */
+		nsrc = (struct nmreq_option *)opt->nro_next;
+		opt->nro_next = 0;
 
-#else /* linux */
-	default:
-		error = EOPNOTSUPP;
-#endif /* linux */
+		opt->nro_status = 0;
+
+		/* check for invalid types */
+		if (opt->nro_reqtype < 1) {
+			if (netmap_verbose)
+				nm_prinf("invalid option type: %u", opt->nro_reqtype);
+			opt->nro_status = EINVAL;
+			error = EINVAL;
+			goto out_restore;
+		}
+
+		if (opt->nro_reqtype >= NETMAP_REQ_OPT_MAX) {
+			/* opt->nro_status will be set to EOPNOTSUPP */
+			goto next;
+		}
+
+		/* if the type is valid, index the option in the table
+		 * unless it is a duplicate.
+		 */
+		if (opt_tab[opt->nro_reqtype] != NULL) {
+			if (netmap_verbose)
+				nm_prinf("duplicate option: %u", opt->nro_reqtype);
+			opt->nro_status = EINVAL;
+			opt_tab[opt->nro_reqtype]->nro_status = EINVAL;
+			error = EINVAL;
+			goto out_restore;
+		}
+		opt_tab[opt->nro_reqtype] = opt;
+
+		/* copy the option body */
+		optsz = nmreq_opt_size_by_type(opt->nro_reqtype,
+						opt->nro_size);
+		/* check optsz and nro_size to avoid for possible integer overflows of rqsz */
+		if ((optsz > NETMAP_REQ_MAXSIZE) || (opt->nro_size > NETMAP_REQ_MAXSIZE)
+				|| (rqsz + optsz > NETMAP_REQ_MAXSIZE)
+				|| (p - ker + optsz > bufsz)
+				|| (optsz > 0 && rqsz + optsz <= rqsz)) {
+			error = EMSGSIZE;
+			goto out_restore;
+		}
+		rqsz += optsz;
+		if (optsz) {
+			/* the option body follows the option header */
+			error = copyin(src + 1, p, optsz);
+			if (error)
+				goto out_restore;
+			p += optsz;
+		}
+
+	next:
+		/* move to next option */
+		next = (struct nmreq_option **)&opt->nro_next;
+		src = nsrc;
 	}
 
-	return (error);
+	/* initialize all the options as not supported.  Recognized options
+	 * will update their field.
+	 */
+	for (src = (struct nmreq_option *)hdr->nr_options; src;
+			src = (struct nmreq_option *)src->nro_next) {
+		src->nro_status = EOPNOTSUPP;
+	}
+	return 0;
+
+out_restore:
+	nmreq_copyout(hdr, error);
+out_err:
+	return error;
 }
 
+static int
+nmreq_copyout(struct nmreq_header *hdr, int rerror)
+{
+	struct nmreq_option *src, *dst;
+	void *ker = (void *)(uintptr_t)hdr->nr_body, *bufstart;
+	uint64_t *ptrs;
+	size_t bodysz;
+	int error;
+
+	if (!hdr->nr_reserved)
+		return rerror;
+
+	/* restore the user pointers in the header */
+	ptrs = (uint64_t *)ker - 2;
+	bufstart = ptrs;
+	hdr->nr_body = *ptrs++;
+	src = (struct nmreq_option *)(uintptr_t)hdr->nr_options;
+	hdr->nr_options = *ptrs;
+
+	if (!rerror) {
+		/* copy the body */
+		bodysz = nmreq_size_by_type(hdr->nr_reqtype);
+		error = copyout(ker, (void *)(uintptr_t)hdr->nr_body, bodysz);
+		if (error) {
+			rerror = error;
+			goto out;
+		}
+	}
+
+	/* copy the options */
+	dst = (struct nmreq_option *)(uintptr_t)hdr->nr_options;
+	while (src) {
+		size_t optsz;
+		uint64_t next;
+
+		/* restore the user pointer */
+		next = src->nro_next;
+		ptrs = (uint64_t *)src - 1;
+		src->nro_next = *ptrs;
+
+		/* always copy the option header */
+		error = copyout(src, dst, sizeof(*src));
+		if (error) {
+			rerror = error;
+			goto out;
+		}
+
+		/* copy the option body only if there was no error */
+		if (!rerror && !src->nro_status) {
+			optsz = nmreq_opt_size_by_type(src->nro_reqtype,
+							src->nro_size);
+			if (optsz) {
+				error = copyout(src + 1, dst + 1, optsz);
+				if (error) {
+					rerror = error;
+					goto out;
+				}
+			}
+		}
+		src = (struct nmreq_option *)(uintptr_t)next;
+		dst = (struct nmreq_option *)(uintptr_t)*ptrs;
+	}
+
+
+out:
+	hdr->nr_reserved = 0;
+	nm_os_free(bufstart);
+	return rerror;
+}
+
+struct nmreq_option *
+nmreq_getoption(struct nmreq_header *hdr, uint16_t reqtype)
+{
+	struct nmreq_option **opt_tab;
+
+	if (!hdr->nr_options)
+		return NULL;
+
+	opt_tab = (struct nmreq_option **)((uintptr_t)hdr->nr_options) -
+	    (NETMAP_REQ_OPT_MAX + 1);
+	return opt_tab[reqtype];
+}
+
+static int
+nmreq_checkoptions(struct nmreq_header *hdr)
+{
+	struct nmreq_option *opt;
+	/* return error if there is still any option
+	 * marked as not supported
+	 */
+
+	for (opt = (struct nmreq_option *)(uintptr_t)hdr->nr_options; opt;
+	     opt = (struct nmreq_option *)(uintptr_t)opt->nro_next)
+		if (opt->nro_status == EOPNOTSUPP)
+			return EOPNOTSUPP;
+
+	return 0;
+}
 
 /*
  * select(2) and poll(2) handlers for the "netmap" device.
  *
  * Can be called for one or more queues.
  * Return true the event mask corresponding to ready events.
- * If there are no ready events, do a selrecord on either individual
- * selinfo or on the global one.
+ * If there are no ready events (and 'sr' is not NULL), do a
+ * selrecord on either individual selinfo or on the global one.
  * Device-dependent parts (locking and sync of tx/rx rings)
  * are done through callbacks.
  *
@@ -2511,11 +3672,11 @@ netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
 	struct netmap_adapter *na;
 	struct netmap_kring *kring;
 	struct netmap_ring *ring;
-	u_int i, check_all_tx, check_all_rx, want[NR_TXRX], revents = 0;
+	u_int i, want[NR_TXRX], revents = 0;
+	NM_SELINFO_T *si[NR_TXRX];
 #define want_tx want[NR_TX]
 #define want_rx want[NR_RX]
 	struct mbq q;	/* packets from RX hw queues to host stack */
-	enum txrx t;
 
 	/*
 	 * In order to avoid nested locks, we need to "double check"
@@ -2535,27 +3696,31 @@ netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
 
 	mbq_init(&q);
 
-	if (priv->np_nifp == NULL) {
-		D("No if registered");
+	if (unlikely(priv->np_nifp == NULL)) {
 		return POLLERR;
 	}
 	mb(); /* make sure following reads are not from cache */
 
 	na = priv->np_na;
 
-	if (!nm_netmap_on(na))
+	if (unlikely(!nm_netmap_on(na)))
+		return POLLERR;
+
+	if (unlikely(priv->np_csb_atok_base)) {
+		nm_prerr("Invalid poll in CSB mode");
 		return POLLERR;
+	}
 
-	if (netmap_verbose & 0x8000)
-		D("device %s events 0x%x", na->name, events);
+	if (netmap_debug & NM_DEBUG_ON)
+		nm_prinf("device %s events 0x%x", na->name, events);
 	want_tx = events & (POLLOUT | POLLWRNORM);
 	want_rx = events & (POLLIN | POLLRDNORM);
 
 	/*
-	 * check_all_{tx|rx} are set if the card has more than one queue AND
-	 * the file descriptor is bound to all of them. If so, we sleep on
-	 * the "global" selinfo, otherwise we sleep on individual selinfo
-	 * (FreeBSD only allows two selinfo's per file descriptor).
+	 * If the card has more than one queue AND the file descriptor is
+	 * bound to all of them, we sleep on the "global" selinfo, otherwise
+	 * we sleep on individual selinfo (FreeBSD only allows two selinfo's
+	 * per file descriptor).
 	 * The interrupt routine in the driver wake one or the other
 	 * (or both) depending on which clients are active.
 	 *
@@ -2564,57 +3729,62 @@ netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
 	 * there are pending packets to send. The latter can be disabled
 	 * passing NETMAP_NO_TX_POLL in the NIOCREG call.
 	 */
-	check_all_tx = nm_si_user(priv, NR_TX);
-	check_all_rx = nm_si_user(priv, NR_RX);
+	si[NR_RX] = priv->np_si[NR_RX];
+	si[NR_TX] = priv->np_si[NR_TX];
 
+#ifdef __FreeBSD__
 	/*
 	 * We start with a lock free round which is cheap if we have
 	 * slots available. If this fails, then lock and call the sync
-	 * routines.
+	 * routines. We can't do this on Linux, as the contract says
+	 * that we must call nm_os_selrecord() unconditionally.
 	 */
-#if 1 /* new code- call rx if any of the ring needs to release or read buffers */
 	if (want_tx) {
-		t = NR_TX;
-		for (i = priv->np_qfirst[t]; want[t] && i < priv->np_qlast[t]; i++) {
-			kring = &NMR(na, t)[i];
-			/* XXX compare ring->cur and kring->tail */
-			if (!nm_ring_empty(kring->ring)) {
+		const enum txrx t = NR_TX;
+		for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
+			kring = NMR(na, t)[i];
+			if (kring->ring->cur != kring->ring->tail) {
+				/* Some unseen TX space is available, so what
+				 * we don't need to run txsync. */
 				revents |= want[t];
-				want[t] = 0;	/* also breaks the loop */
+				want[t] = 0;
+				break;
 			}
 		}
 	}
 	if (want_rx) {
-		want_rx = 0; /* look for a reason to run the handlers */
-		t = NR_RX;
+		const enum txrx t = NR_RX;
+		int rxsync_needed = 0;
+
 		for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
-			kring = &NMR(na, t)[i];
-			if (kring->ring->cur == kring->ring->tail /* try fetch new buffers */
-			    || kring->rhead != kring->ring->head /* release buffers */) {
-				want_rx = 1;
+			kring = NMR(na, t)[i];
+			if (kring->ring->cur == kring->ring->tail
+				|| kring->rhead != kring->ring->head) {
+				/* There are no unseen packets on this ring,
+				 * or there are some buffers to be returned
+				 * to the netmap port. We therefore go ahead
+				 * and run rxsync. */
+				rxsync_needed = 1;
+				break;
 			}
 		}
-		if (!want_rx)
-			revents |= events & (POLLIN | POLLRDNORM); /* we have data */
-	}
-#else /* old code */
-	for_rx_tx(t) {
-		for (i = priv->np_qfirst[t]; want[t] && i < priv->np_qlast[t]; i++) {
-			kring = &NMR(na, t)[i];
-			/* XXX compare ring->cur and kring->tail */
-			if (!nm_ring_empty(kring->ring)) {
-				revents |= want[t];
-				want[t] = 0;	/* also breaks the loop */
-			}
+		if (!rxsync_needed) {
+			revents |= want_rx;
+			want_rx = 0;
 		}
 	}
-#endif /* old code */
+#endif
+
+#ifdef linux
+	/* The selrecord must be unconditional on linux. */
+	nm_os_selrecord(sr, si[NR_RX]);
+	nm_os_selrecord(sr, si[NR_TX]);
+#endif /* linux */
 
 	/*
 	 * If we want to push packets out (priv->np_txpoll) or
 	 * want_tx is still set, we must issue txsync calls
 	 * (on all rings, to avoid that the tx rings stall).
-	 * XXX should also check cur != hwcur on the tx rings.
 	 * Fortunately, normal tx mode has np_txpoll set.
 	 */
 	if (priv->np_txpoll || want_tx) {
@@ -2628,10 +3798,16 @@ netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
 		for (i = priv->np_qfirst[NR_TX]; i < priv->np_qlast[NR_TX]; i++) {
 			int found = 0;
 
-			kring = &na->tx_rings[i];
+			kring = na->tx_rings[i];
 			ring = kring->ring;
 
-			if (!send_down && !want_tx && ring->cur == kring->nr_hwcur)
+			/*
+			 * Don't try to txsync this TX ring if we already found some
+			 * space in some of the TX rings (want_tx == 0) and there are no
+			 * TX slots in this ring that need to be flushed to the NIC
+			 * (head == hwcur).
+			 */
+			if (!send_down && !want_tx && ring->head == kring->nr_hwcur)
 				continue;
 
 			if (nm_kr_tryget(kring, 1, &revents))
@@ -2658,14 +3834,17 @@ netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
 			if (found) { /* notify other listeners */
 				revents |= want_tx;
 				want_tx = 0;
+#ifndef linux
 				kring->nm_notify(kring, 0);
+#endif /* linux */
 			}
 		}
 		/* if there were any packet to forward we must have handled them by now */
 		send_down = 0;
 		if (want_tx && retry_tx && sr) {
-			nm_os_selrecord(sr, check_all_tx ?
-			    &na->si[NR_TX] : &na->tx_rings[priv->np_qfirst[NR_TX]].si);
+#ifndef linux
+			nm_os_selrecord(sr, si[NR_TX]);
+#endif /* !linux */
 			retry_tx = 0;
 			goto flush_tx;
 		}
@@ -2681,7 +3860,7 @@ netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
 		for (i = priv->np_qfirst[NR_RX]; i < priv->np_qlast[NR_RX]; i++) {
 			int found = 0;
 
-			kring = &na->rx_rings[i];
+			kring = na->rx_rings[i];
 			ring = kring->ring;
 
 			if (unlikely(nm_kr_tryget(kring, 1, &revents)))
@@ -2716,14 +3895,17 @@ netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
 			if (found) {
 				revents |= want_rx;
 				retry_rx = 0;
+#ifndef linux
 				kring->nm_notify(kring, 0);
+#endif /* linux */
 			}
 		}
 
+#ifndef linux
 		if (retry_rx && sr) {
-			nm_os_selrecord(sr, check_all_rx ?
-			    &na->si[NR_RX] : &na->rx_rings[priv->np_qfirst[NR_RX]].si);
+			nm_os_selrecord(sr, si[NR_RX]);
 		}
+#endif /* !linux */
 		if (send_down || retry_rx) {
 			retry_rx = 0;
 			if (send_down)
@@ -2748,6 +3930,44 @@ netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
 #undef want_rx
 }
 
+int
+nma_intr_enable(struct netmap_adapter *na, int onoff)
+{
+	bool changed = false;
+	enum txrx t;
+	int i;
+
+	for_rx_tx(t) {
+		for (i = 0; i < nma_get_nrings(na, t); i++) {
+			struct netmap_kring *kring = NMR(na, t)[i];
+			int on = !(kring->nr_kflags & NKR_NOINTR);
+
+			if (!!onoff != !!on) {
+				changed = true;
+			}
+			if (onoff) {
+				kring->nr_kflags &= ~NKR_NOINTR;
+			} else {
+				kring->nr_kflags |= NKR_NOINTR;
+			}
+		}
+	}
+
+	if (!changed) {
+		return 0; /* nothing to do */
+	}
+
+	if (!na->nm_intr) {
+		nm_prerr("Cannot %s interrupts for %s", onoff ? "enable" : "disable",
+		  na->name);
+		return -1;
+	}
+
+	na->nm_intr(na, onoff);
+
+	return 0;
+}
+
 
 /*-------------------- driver support routines -------------------*/
 
@@ -2755,7 +3975,7 @@ netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
 static int
 netmap_notify(struct netmap_kring *kring, int flags)
 {
-	struct netmap_adapter *na = kring->na;
+	struct netmap_adapter *na = kring->notify_na;
 	enum txrx t = kring->tx;
 
 	nm_os_selwakeup(&kring->si);
@@ -2776,17 +3996,23 @@ netmap_notify(struct netmap_kring *kring, int flags)
 int
 netmap_attach_common(struct netmap_adapter *na)
 {
-	if (na->num_tx_rings == 0 || na->num_rx_rings == 0) {
-		D("%s: invalid rings tx %d rx %d",
-			na->name, na->num_tx_rings, na->num_rx_rings);
-		return EINVAL;
+	if (!na->rx_buf_maxsize) {
+		/* Set a conservative default (larger is safer). */
+		na->rx_buf_maxsize = PAGE_SIZE;
 	}
 
 #ifdef __FreeBSD__
 	if (na->na_flags & NAF_HOST_RINGS && na->ifp) {
-		na->if_input = na->ifp->if_input; /* for netmap_send_up */
+		na->if_input = if_getinputfn(na->ifp); /* for netmap_send_up */
 	}
+	na->pdev = na; /* make sure netmap_mem_map() is called */
 #endif /* __FreeBSD__ */
+	if (na->na_flags & NAF_HOST_RINGS) {
+		if (na->num_host_rx_rings == 0)
+			na->num_host_rx_rings = 1;
+		if (na->num_host_tx_rings == 0)
+			na->num_host_tx_rings = 1;
+	}
 	if (na->nm_krings_create == NULL) {
 		/* we assume that we have been called by a driver,
 		 * since other port types all provide their own
@@ -2800,36 +4026,18 @@ netmap_attach_common(struct netmap_adapter *na)
 	na->active_fds = 0;
 
 	if (na->nm_mem == NULL) {
-		/* use the global allocator */
-		na->nm_mem = netmap_mem_get(&nm_mem);
+		/* use iommu or global allocator */
+		na->nm_mem = netmap_mem_get_iommu(na);
 	}
-#ifdef WITH_VALE
 	if (na->nm_bdg_attach == NULL)
 		/* no special nm_bdg_attach callback. On VALE
 		 * attach, we need to interpose a bwrap
 		 */
-		na->nm_bdg_attach = netmap_bwrap_attach;
-#endif
+		na->nm_bdg_attach = netmap_default_bdg_attach;
 
 	return 0;
 }
 
-
-/* standard cleanup, called by all destructors */
-void
-netmap_detach_common(struct netmap_adapter *na)
-{
-	if (na->tx_rings) { /* XXX should not happen */
-		D("freeing leftover tx_rings");
-		na->nm_krings_delete(na);
-	}
-	netmap_pipe_dealloc(na);
-	if (na->nm_mem)
-		netmap_mem_put(na->nm_mem);
-	bzero(na, sizeof(*na));
-	nm_os_free(na);
-}
-
 /* Wrapper for the register callback provided netmap-enabled
  * hardware drivers.
  * nm_iszombie(na) means that the driver module has been
@@ -2866,41 +4074,66 @@ netmap_hw_reg(struct netmap_adapter *na, int onoff)
 static void
 netmap_hw_dtor(struct netmap_adapter *na)
 {
-	if (nm_iszombie(na) || na->ifp == NULL)
+	if (na->ifp == NULL)
 		return;
 
-	WNA(na->ifp) = NULL;
+	NM_DETACH_NA(na->ifp);
 }
 
 
 /*
- * Allocate a ``netmap_adapter`` object, and initialize it from the
+ * Allocate a netmap_adapter object, and initialize it from the
  * 'arg' passed by the driver on attach.
- * We allocate a block of memory with room for a struct netmap_adapter
- * plus two sets of N+2 struct netmap_kring (where N is the number
- * of hardware rings):
- * krings	0..N-1	are for the hardware queues.
- * kring	N	is for the host stack queue
- * kring	N+1	is only used for the selinfo for all queues. // XXX still true ?
+ * We allocate a block of memory of 'size' bytes, which has room
+ * for struct netmap_adapter plus additional room private to
+ * the caller.
  * Return 0 on success, ENOMEM otherwise.
  */
-static int
-_netmap_attach(struct netmap_adapter *arg, size_t size)
+int
+netmap_attach_ext(struct netmap_adapter *arg, size_t size, int override_reg)
 {
 	struct netmap_hw_adapter *hwna = NULL;
-	struct ifnet *ifp = NULL;
+	if_t ifp = NULL;
+
+	if (size < sizeof(struct netmap_hw_adapter)) {
+		if (netmap_debug & NM_DEBUG_ON)
+			nm_prerr("Invalid netmap adapter size %d", (int)size);
+		return EINVAL;
+	}
+
+	if (arg == NULL || arg->ifp == NULL) {
+		if (netmap_debug & NM_DEBUG_ON)
+			nm_prerr("either arg or arg->ifp is NULL");
+		return EINVAL;
+	}
+
+	if (arg->num_tx_rings == 0 || arg->num_rx_rings == 0) {
+		if (netmap_debug & NM_DEBUG_ON)
+			nm_prerr("%s: invalid rings tx %d rx %d",
+				arg->name, arg->num_tx_rings, arg->num_rx_rings);
+		return EINVAL;
+	}
 
-	if (arg == NULL || arg->ifp == NULL)
-		goto fail;
 	ifp = arg->ifp;
+	if (NM_NA_CLASH(ifp)) {
+		/* If NA(ifp) is not null but there is no valid netmap
+		 * adapter it means that someone else is using the same
+		 * pointer (e.g. ax25_ptr on linux). This happens for
+		 * instance when also PF_RING is in use. */
+		nm_prerr("Error: netmap adapter hook is busy");
+		return EBUSY;
+	}
+
 	hwna = nm_os_malloc(size);
 	if (hwna == NULL)
 		goto fail;
 	hwna->up = *arg;
 	hwna->up.na_flags |= NAF_HOST_RINGS | NAF_NATIVE;
-	strncpy(hwna->up.name, ifp->if_xname, sizeof(hwna->up.name));
-	hwna->nm_hw_register = hwna->up.nm_register;
-	hwna->up.nm_register = netmap_hw_reg;
+	strlcpy(hwna->up.name, if_name(ifp), sizeof(hwna->up.name));
+	if (override_reg) {
+		hwna->nm_hw_register = hwna->up.nm_register;
+		hwna->up.nm_register = netmap_hw_reg;
+	}
 	if (netmap_attach_common(&hwna->up)) {
 		nm_os_free(hwna);
 		goto fail;
@@ -2909,27 +4142,8 @@ _netmap_attach(struct netmap_adapter *arg, size_t size)
 
 	NM_ATTACH_NA(ifp, &hwna->up);
 
-#ifdef linux
-	if (ifp->netdev_ops) {
-		/* prepare a clone of the netdev ops */
-#ifndef NETMAP_LINUX_HAVE_NETDEV_OPS
-		hwna->nm_ndo.ndo_start_xmit = ifp->netdev_ops;
-#else
-		hwna->nm_ndo = *ifp->netdev_ops;
-#endif /* NETMAP_LINUX_HAVE_NETDEV_OPS */
-	}
-	hwna->nm_ndo.ndo_start_xmit = linux_netmap_start_xmit;
-	if (ifp->ethtool_ops) {
-		hwna->nm_eto = *ifp->ethtool_ops;
-	}
-	hwna->nm_eto.set_ringparam = linux_netmap_set_ringparam;
-#ifdef NETMAP_LINUX_HAVE_SET_CHANNELS
-	hwna->nm_eto.set_channels = linux_netmap_set_channels;
-#endif /* NETMAP_LINUX_HAVE_SET_CHANNELS */
-	if (arg->nm_config == NULL) {
-		hwna->up.nm_config = netmap_linux_config;
-	}
-#endif /* linux */
+	nm_os_onattach(ifp);
+
 	if (arg->nm_dtor == NULL) {
 		hwna->up.nm_dtor = netmap_hw_dtor;
 	}
@@ -2940,7 +4154,7 @@ _netmap_attach(struct netmap_adapter *arg, size_t size)
 	return 0;
 
 fail:
-	D("fail, arg %p ifp %p na %p", arg, ifp, hwna);
+	nm_prerr("fail, arg %p ifp %p na %p", arg, ifp, hwna);
 	return (hwna ? EINVAL : ENOMEM);
 }
 
@@ -2948,47 +4162,11 @@ _netmap_attach(struct netmap_adapter *arg, size_t size)
 int
 netmap_attach(struct netmap_adapter *arg)
 {
-	return _netmap_attach(arg, sizeof(struct netmap_hw_adapter));
+	return netmap_attach_ext(arg, sizeof(struct netmap_hw_adapter),
+			1 /* override nm_reg */);
 }
 
 
-#ifdef WITH_PTNETMAP_GUEST
-int
-netmap_pt_guest_attach(struct netmap_adapter *arg, void *csb,
-		       unsigned int nifp_offset, unsigned int memid)
-{
-	struct netmap_pt_guest_adapter *ptna;
-	struct ifnet *ifp = arg ? arg->ifp : NULL;
-	int error;
-
-	/* get allocator */
-	arg->nm_mem = netmap_mem_pt_guest_new(ifp, nifp_offset, memid);
-	if (arg->nm_mem == NULL)
-		return ENOMEM;
-	arg->na_flags |= NAF_MEM_OWNER;
-	error = _netmap_attach(arg, sizeof(struct netmap_pt_guest_adapter));
-	if (error)
-		return error;
-
-	/* get the netmap_pt_guest_adapter */
-	ptna = (struct netmap_pt_guest_adapter *) NA(ifp);
-	ptna->csb = csb;
-
-	/* Initialize a separate pass-through netmap adapter that is going to
-	 * be used by the ptnet driver only, and so never exposed to netmap
-         * applications. We only need a subset of the available fields. */
-	memset(&ptna->dr, 0, sizeof(ptna->dr));
-	ptna->dr.up.ifp = ifp;
-	ptna->dr.up.nm_mem = netmap_mem_get(ptna->hwup.up.nm_mem);
-        ptna->dr.up.nm_config = ptna->hwup.up.nm_config;
-
-	ptna->backend_regifs = 0;
-
-	return 0;
-}
-#endif /* WITH_PTNETMAP_GUEST */
-
-
 void
 NM_DBG(netmap_adapter_get)(struct netmap_adapter *na)
 {
@@ -3013,7 +4191,16 @@ NM_DBG(netmap_adapter_put)(struct netmap_adapter *na)
 	if (na->nm_dtor)
 		na->nm_dtor(na);
 
-	netmap_detach_common(na);
+	if (na->tx_rings) { /* XXX should not happen */
+		if (netmap_debug & NM_DEBUG_ON)
+			nm_prerr("freeing leftover tx_rings");
+		na->nm_krings_delete(na);
+	}
+	netmap_pipe_dealloc(na);
+	if (na->nm_mem)
+		netmap_mem_put(na->nm_mem);
+	bzero(na, sizeof(*na));
+	nm_os_free(na);
 
 	return 1;
 }
@@ -3025,8 +4212,11 @@ netmap_hw_krings_create(struct netmap_adapter *na)
 	int ret = netmap_krings_create(na, 0);
 	if (ret == 0) {
 		/* initialize the mbq for the sw rx ring */
-		mbq_safe_init(&na->rx_rings[na->num_rx_rings].rx_queue);
-		ND("initialized sw rx queue %d", na->num_rx_rings);
+		u_int lim = netmap_real_rings(na, NR_RX), i;
+		for (i = na->num_rx_rings; i < lim; i++) {
+			mbq_safe_init(&NMR(na, NR_RX)[i]->rx_queue);
+		}
+		nm_prdis("initialized sw rx queue %d", na->num_rx_rings);
 	}
 	return ret;
 }
@@ -3037,24 +4227,27 @@ netmap_hw_krings_create(struct netmap_adapter *na)
  * Called on module unload by the netmap-enabled drivers
  */
 void
-netmap_detach(struct ifnet *ifp)
+netmap_detach(if_t ifp)
 {
-	struct netmap_adapter *na = NA(ifp);
+	struct netmap_adapter *na;
 
-	if (!na)
+	NMG_LOCK();
+
+	if (!NM_NA_VALID(ifp)) {
+		NMG_UNLOCK();
 		return;
+	}
 
-	NMG_LOCK();
+	na = NA(ifp);
 	netmap_set_all_rings(na, NM_KR_LOCKED);
-	na->na_flags |= NAF_ZOMBIE;
 	/*
 	 * if the netmap adapter is not native, somebody
 	 * changed it, so we can not release it here.
 	 * The NAF_ZOMBIE flag will notify the new owner that
 	 * the driver is gone.
 	 */
-	if (na->na_flags & NAF_NATIVE) {
-	        netmap_adapter_put(na);
+	if (!(na->na_flags & NAF_NATIVE) || !netmap_adapter_put(na)) {
+		na->na_flags |= NAF_ZOMBIE;
 	}
 	/* give active users a chance to notice that NAF_ZOMBIE has been
 	 * turned on, so that they can stop and return an error to userspace.
@@ -3080,7 +4273,7 @@ netmap_detach(struct ifnet *ifp)
  * we make sure to make the mode change visible here.
  */
 int
-netmap_transmit(struct ifnet *ifp, struct mbuf *m)
+netmap_transmit(if_t ifp, struct mbuf *m)
 {
 	struct netmap_adapter *na = NA(ifp);
 	struct netmap_kring *kring, *tx_kring;
@@ -3089,14 +4282,20 @@ netmap_transmit(struct ifnet *ifp, struct mbuf *m)
 	unsigned int txr;
 	struct mbq *q;
 	int busy;
+	u_int i;
+
+	i = MBUF_TXQ(m);
+	if (i >= na->num_host_rx_rings) {
+		i = i % na->num_host_rx_rings;
+	}
+	kring = NMR(na, NR_RX)[nma_get_nrings(na, NR_RX) + i];
 
-	kring = &na->rx_rings[na->num_rx_rings];
 	// XXX [Linux] we do not need this lock
 	// if we follow the down/configure/up protocol -gl
 	// mtx_lock(&na->core_lock);
 
 	if (!nm_netmap_on(na)) {
-		D("%s not in netmap mode anymore", na->name);
+		nm_prerr("%s not in netmap mode anymore", na->name);
 		error = ENXIO;
 		goto done;
 	}
@@ -3105,7 +4304,7 @@ netmap_transmit(struct ifnet *ifp, struct mbuf *m)
 	if (txr >= na->num_tx_rings) {
 		txr %= na->num_tx_rings;
 	}
-	tx_kring = &NMR(na, NR_TX)[txr];
+	tx_kring = NMR(na, NR_TX)[txr];
 
 	if (tx_kring->nr_mode == NKR_NETMAP_OFF) {
 		return MBUF_TRANSMIT(na, ifp, m);
@@ -3115,16 +4314,27 @@ netmap_transmit(struct ifnet *ifp, struct mbuf *m)
 
 	// XXX reconsider long packets if we handle fragments
 	if (len > NETMAP_BUF_SIZE(na)) { /* too long for us */
-		D("%s from_host, drop packet size %d > %d", na->name,
+		nm_prerr("%s from_host, drop packet size %d > %d", na->name,
 			len, NETMAP_BUF_SIZE(na));
 		goto done;
 	}
 
-	if (nm_os_mbuf_has_offld(m)) {
-		RD(1, "%s drop mbuf that needs offloadings", na->name);
+	if (!netmap_generic_hwcsum) {
+		if (nm_os_mbuf_has_csum_offld(m)) {
+			nm_prlim(1, "%s drop mbuf that needs checksum offload", na->name);
+			goto done;
+		}
+	}
+
+	if (nm_os_mbuf_has_seg_offld(m)) {
+		nm_prlim(1, "%s drop mbuf that needs generic segmentation offload", na->name);
 		goto done;
 	}
 
+#ifdef __FreeBSD__
+	ETHER_BPF_MTAP(ifp, m);
+#endif /* __FreeBSD__ */
+
 	/* protect against netmap_rxsync_from_host(), netmap_sw_to_nic()
 	 * and maybe other instances of netmap_transmit (the latter
 	 * not possible on Linux).
@@ -3133,15 +4343,15 @@ netmap_transmit(struct ifnet *ifp, struct mbuf *m)
 	 */
 	mbq_lock(q);
 
-        busy = kring->nr_hwtail - kring->nr_hwcur;
-        if (busy < 0)
-                busy += kring->nkr_num_slots;
+	busy = kring->nr_hwtail - kring->nr_hwcur;
+	if (busy < 0)
+		busy += kring->nkr_num_slots;
 	if (busy + mbq_len(q) >= kring->nkr_num_slots - 1) {
-		RD(2, "%s full hwcur %d hwtail %d qlen %d", na->name,
+		nm_prlim(2, "%s full hwcur %d hwtail %d qlen %d", na->name,
 			kring->nr_hwcur, kring->nr_hwtail, mbq_len(q));
 	} else {
 		mbq_enqueue(q, m);
-		ND(2, "%s %d bufs in queue", na->name, mbq_len(q));
+		nm_prdis(2, "%s %d bufs in queue", na->name, mbq_len(q));
 		/* notify outside the lock */
 		m = NULL;
 		error = 0;
@@ -3149,8 +4359,10 @@ netmap_transmit(struct ifnet *ifp, struct mbuf *m)
 	mbq_unlock(q);
 
 done:
-	if (m)
+	if (m) {
+		if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
 		m_freem(m);
+	}
 	/* unconditionally wake up listeners */
 	kring->nm_notify(kring, 0);
 	/* this is normally netmap_notify(), but for nics
@@ -3163,86 +4375,72 @@ netmap_transmit(struct ifnet *ifp, struct mbuf *m)
 
 
 /*
- * netmap_reset() is called by the driver routines when reinitializing
- * a ring. The driver is in charge of locking to protect the kring.
- * If native netmap mode is not set just return NULL.
- * If native netmap mode is set, in particular, we have to set nr_mode to
- * NKR_NETMAP_ON.
+ * Reset function to be called by the driver routines when reinitializing
+ * a hardware ring. The driver is in charge of locking to protect the kring
+ * while this operation is being performed. This is normally achieved by
+ * calling netmap_disable_all_rings() before triggering a reset.
+ * If the kring is not in netmap mode, return NULL to inform the caller
+ * that this is the case.
+ * If the kring is in netmap mode, set hwofs so that the netmap indices
+ * seen by userspace (head/cut/tail) do not change, although the internal
+ * NIC indices have been reset to 0.
+ * In any case, adjust kring->nr_mode.
  */
 struct netmap_slot *
 netmap_reset(struct netmap_adapter *na, enum txrx tx, u_int n,
 	u_int new_cur)
 {
 	struct netmap_kring *kring;
-	int new_hwofs, lim;
+	u_int new_hwtail, new_hwofs;
 
 	if (!nm_native_on(na)) {
-		ND("interface not in native netmap mode");
+		nm_prdis("interface not in native netmap mode");
 		return NULL;	/* nothing to reinitialize */
 	}
 
-	/* XXX note- in the new scheme, we are not guaranteed to be
-	 * under lock (e.g. when called on a device reset).
-	 * In this case, we should set a flag and do not trust too
-	 * much the values. In practice: TODO
-	 * - set a RESET flag somewhere in the kring
-	 * - do the processing in a conservative way
-	 * - let the *sync() fixup at the end.
-	 */
 	if (tx == NR_TX) {
 		if (n >= na->num_tx_rings)
 			return NULL;
-
-		kring = na->tx_rings + n;
-
-		if (kring->nr_pending_mode == NKR_NETMAP_OFF) {
-			kring->nr_mode = NKR_NETMAP_OFF;
-			return NULL;
-		}
-
-		// XXX check whether we should use hwcur or rcur
-		new_hwofs = kring->nr_hwcur - new_cur;
+		kring = na->tx_rings[n];
+		/*
+		 * Set hwofs to rhead, so that slots[rhead] is mapped to
+		 * the NIC internal slot 0, and thus the netmap buffer
+		 * at rhead is the next to be transmitted. Transmissions
+		 * that were pending before the reset are considered as
+		 * sent, so that we can have hwcur = rhead. All the slots
+		 * are now owned by the user, so we can also reinit hwtail.
+		 */
+		new_hwofs = kring->rhead;
+		new_hwtail = nm_prev(kring->rhead, kring->nkr_num_slots - 1);
 	} else {
 		if (n >= na->num_rx_rings)
 			return NULL;
-		kring = na->rx_rings + n;
-
-		if (kring->nr_pending_mode == NKR_NETMAP_OFF) {
-			kring->nr_mode = NKR_NETMAP_OFF;
-			return NULL;
-		}
-
-		new_hwofs = kring->nr_hwtail - new_cur;
+		kring = na->rx_rings[n];
+		/*
+		 * Set hwofs to hwtail, so that slots[hwtail] is mapped to
+		 * the NIC internal slot 0, and thus the netmap buffer
+		 * at hwtail is the next to be given to the NIC.
+		 * Unread slots (the ones in [rhead,hwtail[) are owned by
+		 * the user, and thus the caller cannot give them
+		 * to the NIC right now.
+		 */
+		new_hwofs = kring->nr_hwtail;
+		new_hwtail = kring->nr_hwtail;
 	}
-	lim = kring->nkr_num_slots - 1;
-	if (new_hwofs > lim)
-		new_hwofs -= lim + 1;
-
-	/* Always set the new offset value and realign the ring. */
-	if (netmap_verbose)
-	    D("%s %s%d hwofs %d -> %d, hwtail %d -> %d",
-		na->name,
-		tx == NR_TX ? "TX" : "RX", n,
-		kring->nkr_hwofs, new_hwofs,
-		kring->nr_hwtail,
-		tx == NR_TX ? lim : kring->nr_hwtail);
-	kring->nkr_hwofs = new_hwofs;
-	if (tx == NR_TX) {
-		kring->nr_hwtail = kring->nr_hwcur + lim;
-		if (kring->nr_hwtail > lim)
-			kring->nr_hwtail -= lim + 1;
+	if (kring->nr_pending_mode == NKR_NETMAP_OFF) {
+		kring->nr_mode = NKR_NETMAP_OFF;
+		return NULL;
 	}
-
-#if 0 // def linux
-	/* XXX check that the mappings are correct */
-	/* need ring_nr, adapter->pdev, direction */
-	buffer_info->dma = dma_map_single(&pdev->dev, addr, adapter->rx_buffer_len, DMA_FROM_DEVICE);
-	if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) {
-		D("error mapping rx netmap buffer %d", i);
-		// XXX fix error handling
+	if (netmap_verbose) {
+	    nm_prinf("%s, hc %u->%u, ht %u->%u, ho %u->%u", kring->name,
+	        kring->nr_hwcur, kring->rhead,
+	        kring->nr_hwtail, new_hwtail,
+		kring->nkr_hwofs, new_hwofs);
 	}
+	kring->nr_hwcur = kring->rhead;
+	kring->nr_hwtail = new_hwtail;
+	kring->nkr_hwofs = new_hwofs;
 
-#endif /* linux */
 	/*
 	 * Wakeup on the individual and global selwait
 	 * We do the wakeup here, but the ring is not yet reconfigured.
@@ -3276,14 +4474,14 @@ netmap_common_irq(struct netmap_adapter *na, u_int q, u_int *work_done)
 
 	q &= NETMAP_RING_MASK;
 
-	if (netmap_verbose) {
-	        RD(5, "received %s queue %d", work_done ? "RX" : "TX" , q);
+	if (netmap_debug & (NM_DEBUG_RXINTR|NM_DEBUG_TXINTR)) {
+	        nm_prlim(5, "received %s queue %d", work_done ? "RX" : "TX" , q);
 	}
 
 	if (q >= nma_get_nrings(na, t))
 		return NM_IRQ_PASS; // not a physical queue
 
-	kring = NMR(na, t) + q;
+	kring = NMR(na, t)[q];
 
 	if (kring->nr_mode == NKR_NETMAP_OFF) {
 		return NM_IRQ_PASS;
@@ -3315,7 +4513,7 @@ netmap_common_irq(struct netmap_adapter *na, u_int q, u_int *work_done)
  * calls the proper forwarding routine.
  */
 int
-netmap_rx_irq(struct ifnet *ifp, u_int q, u_int *work_done)
+netmap_rx_irq(if_t ifp, u_int q, u_int *work_done)
 {
 	struct netmap_adapter *na = NA(ifp);
 
@@ -3329,13 +4527,63 @@ netmap_rx_irq(struct ifnet *ifp, u_int q, u_int *work_done)
 		return NM_IRQ_PASS;
 
 	if (na->na_flags & NAF_SKIP_INTR) {
-		ND("use regular interrupt");
+		nm_prdis("use regular interrupt");
 		return NM_IRQ_PASS;
 	}
 
 	return netmap_common_irq(na, q, work_done);
 }
 
+/* set/clear native flags and if_transmit/netdev_ops */
+void
+nm_set_native_flags(struct netmap_adapter *na)
+{
+	if_t ifp = na->ifp;
+
+	/* We do the setup for intercepting packets only if we are the
+	 * first user of this adapter. */
+	if (na->active_fds > 0) {
+		return;
+	}
+
+	na->na_flags |= NAF_NETMAP_ON;
+	nm_os_onenter(ifp);
+}
+
+void
+nm_clear_native_flags(struct netmap_adapter *na)
+{
+	if_t ifp = na->ifp;
+
+	/* We undo the setup for intercepting packets only if we are the
+	 * last user of this adapter. */
+	if (na->active_fds > 0) {
+		return;
+	}
+
+	nm_os_onexit(ifp);
+
+	na->na_flags &= ~NAF_NETMAP_ON;
+}
+
+void
+netmap_krings_mode_commit(struct netmap_adapter *na, int onoff)
+{
+	enum txrx t;
+
+	for_rx_tx(t) {
+		int i;
+
+		for (i = 0; i < netmap_real_rings(na, t); i++) {
+			struct netmap_kring *kring = NMR(na, t)[i];
+
+			if (onoff && nm_kring_pending_on(kring))
+				kring->nr_mode = NKR_NETMAP_ON;
+			else if (!onoff && nm_kring_pending_off(kring))
+				kring->nr_mode = NKR_NETMAP_OFF;
+		}
+	}
+}
 
 /*
  * Module loader and unloader
@@ -3361,7 +4609,7 @@ netmap_fini(void)
 	netmap_uninit_bridges();
 	netmap_mem_fini();
 	NMG_LOCK_DESTROY();
-	printf("netmap: unloaded module.\n");
+	nm_prinf("netmap: unloaded module.");
 }
 
 
@@ -3398,7 +4646,9 @@ netmap_init(void)
 	if (error)
 		goto fail;
 
-	printf("netmap: loaded module\n");
+#if !defined(__FreeBSD__) || defined(KLD_MODULE)
+	nm_prinf("netmap: loaded module");
+#endif
 	return (0);
 fail:
 	netmap_fini();
diff --git a/sys/dev/netmap/netmap_bdg.c b/sys/dev/netmap/netmap_bdg.c
new file mode 100644
index 000000000..4c19874d9
--- /dev/null
+++ b/sys/dev/netmap/netmap_bdg.c
@@ -0,0 +1,1844 @@
+/*
+ * Copyright (C) 2013-2016 Universita` di Pisa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+/*
+ * This module implements the VALE switch for netmap
+
+--- VALE SWITCH ---
+
+NMG_LOCK() serializes all modifications to switches and ports.
+A switch cannot be deleted until all ports are gone.
+
+For each switch, an SX lock (RWlock on linux) protects
+deletion of ports. When configuring or deleting a new port, the
+lock is acquired in exclusive mode (after holding NMG_LOCK).
+When forwarding, the lock is acquired in shared mode (without NMG_LOCK).
+The lock is held throughout the entire forwarding cycle,
+during which the thread may incur in a page fault.
+Hence it is important that sleepable shared locks are used.
+
+On the rx ring, the per-port lock is grabbed initially to reserve
+a number of slot in the ring, then the lock is released,
+packets are copied from source to destination, and then
+the lock is acquired again and the receive ring is updated.
+(A similar thing is done on the tx ring for NIC and host stack
+ports attached to the switch)
+
+ */
+
+/*
+ * OS-specific code that is used only within this file.
+ * Other OS-specific code that must be accessed by drivers
+ * is present in netmap_kern.h
+ */
+
+#if defined(__FreeBSD__)
+#include  /* prerequisite */
+#include 
+#include 
+#include 	/* defines used in kernel.h */
+#include 	/* types used in module initialization */
+#include 	/* cdevsw struct, UID, GID */
+#include 
+#include 	/* struct socket */
+#include 
+#include 
+#include 
+#include  /* sockaddrs */
+#include 
+#include 
+#include 
+#include 
+#include 		/* BIOCIMMEDIATE */
+#include 	/* bus_dmamap_* */
+#include 
+#include 
+#include 
+
+
+#elif defined(linux)
+
+#include "bsd_glue.h"
+
+#elif defined(__APPLE__)
+
+#warning OSX support is only partial
+#include "osx_glue.h"
+
+#elif defined(_WIN32)
+#include "win_glue.h"
+
+#else
+
+#error	Unsupported platform
+
+#endif /* unsupported */
+
+/*
+ * common headers
+ */
+
+#include 
+#include 
+#include 
+
+#include 
+
+const char*
+netmap_bdg_name(struct netmap_vp_adapter *vp)
+{
+	struct nm_bridge *b = vp->na_bdg;
+	if (b == NULL)
+		return NULL;
+	return b->bdg_basename;
+}
+
+
+#ifndef CONFIG_NET_NS
+/*
+ * XXX in principle nm_bridges could be created dynamically
+ * Right now we have a static array and deletions are protected
+ * by an exclusive lock.
+ */
+struct nm_bridge *nm_bridges;
+#endif /* !CONFIG_NET_NS */
+
+
+static int
+nm_is_id_char(const char c)
+{
+	return (c >= 'a' && c <= 'z') ||
+	       (c >= 'A' && c <= 'Z') ||
+	       (c >= '0' && c <= '9') ||
+	       (c == '_');
+}
+
+/* Validate the name of a bdg port and return the
+ * position of the ":" character. */
+static int
+nm_bdg_name_validate(const char *name, size_t prefixlen)
+{
+	int colon_pos = -1;
+	int i;
+
+	if (!name || strlen(name) < prefixlen) {
+		return -1;
+	}
+
+	for (i = 0; i < NM_BDG_IFNAMSIZ && name[i]; i++) {
+		if (name[i] == ':') {
+			colon_pos = i;
+			break;
+		} else if (!nm_is_id_char(name[i])) {
+			return -1;
+		}
+	}
+
+	if (strlen(name) - colon_pos > IFNAMSIZ) {
+		/* interface name too long */
+		return -1;
+	}
+
+	return colon_pos;
+}
+
+/*
+ * locate a bridge among the existing ones.
+ * MUST BE CALLED WITH NMG_LOCK()
+ *
+ * a ':' in the name terminates the bridge name. Otherwise, just NM_NAME.
+ * We assume that this is called with a name of at least NM_NAME chars.
+ */
+struct nm_bridge *
+nm_find_bridge(const char *name, int create, struct netmap_bdg_ops *ops)
+{
+	int i, namelen;
+	struct nm_bridge *b = NULL, *bridges;
+	u_int num_bridges;
+
+	NMG_LOCK_ASSERT();
+
+	netmap_bns_getbridges(&bridges, &num_bridges);
+
+	namelen = nm_bdg_name_validate(name,
+			(ops != NULL ? strlen(ops->name) : 0));
+	if (namelen < 0) {
+		nm_prerr("invalid bridge name %s", name ? name : NULL);
+		return NULL;
+	}
+
+	/* lookup the name, remember empty slot if there is one */
+	for (i = 0; i < num_bridges; i++) {
+		struct nm_bridge *x = bridges + i;
+
+		if ((x->bdg_flags & NM_BDG_ACTIVE) + x->bdg_active_ports == 0) {
+			if (create && b == NULL)
+				b = x;	/* record empty slot */
+		} else if (x->bdg_namelen != namelen) {
+			continue;
+		} else if (strncmp(name, x->bdg_basename, namelen) == 0) {
+			nm_prdis("found '%.*s' at %d", namelen, name, i);
+			b = x;
+			break;
+		}
+	}
+	if (i == num_bridges && b) { /* name not found, can create entry */
+		/* initialize the bridge */
+		nm_prdis("create new bridge %s with ports %d", b->bdg_basename,
+			b->bdg_active_ports);
+		b->ht = nm_os_malloc(sizeof(struct nm_hash_ent) * NM_BDG_HASH);
+		if (b->ht == NULL) {
+			nm_prerr("failed to allocate hash table");
+			return NULL;
+		}
+		strncpy(b->bdg_basename, name, namelen);
+		b->bdg_namelen = namelen;
+		b->bdg_active_ports = 0;
+		for (i = 0; i < NM_BDG_MAXPORTS; i++)
+			b->bdg_port_index[i] = i;
+		/* set the default function */
+		b->bdg_ops = b->bdg_saved_ops = *ops;
+		b->private_data = b->ht;
+		b->bdg_flags = 0;
+		NM_BNS_GET(b);
+	}
+	return b;
+}
+
+
+int
+netmap_bdg_free(struct nm_bridge *b)
+{
+	if ((b->bdg_flags & NM_BDG_ACTIVE) + b->bdg_active_ports != 0) {
+		return EBUSY;
+	}
+
+	nm_prdis("marking bridge %s as free", b->bdg_basename);
+	nm_os_free(b->ht);
+	memset(&b->bdg_ops, 0, sizeof(b->bdg_ops));
+	memset(&b->bdg_saved_ops, 0, sizeof(b->bdg_saved_ops));
+	b->bdg_flags = 0;
+	NM_BNS_PUT(b);
+	return 0;
+}
+
+/* Called by external kernel modules (e.g., Openvswitch).
+ * to modify the private data previously given to regops().
+ * 'name' may be just bridge's name (including ':' if it
+ * is not just NM_BDG_NAME).
+ * Called without NMG_LOCK.
+ */
+int
+netmap_bdg_update_private_data(const char *name, bdg_update_private_data_fn_t callback,
+	void *callback_data, void *auth_token)
+{
+	void *private_data = NULL;
+	struct nm_bridge *b;
+	int error = 0;
+
+	NMG_LOCK();
+	b = nm_find_bridge(name, 0 /* don't create */, NULL);
+	if (!b) {
+		error = EINVAL;
+		goto unlock_update_priv;
+	}
+	if (!nm_bdg_valid_auth_token(b, auth_token)) {
+		error = EACCES;
+		goto unlock_update_priv;
+	}
+	BDG_WLOCK(b);
+	private_data = callback(b->private_data, callback_data, &error);
+	b->private_data = private_data;
+	BDG_WUNLOCK(b);
+
+unlock_update_priv:
+	NMG_UNLOCK();
+	return error;
+}
+
+
+
+/* remove from bridge b the ports in slots hw and sw
+ * (sw can be -1 if not needed)
+ */
+void
+netmap_bdg_detach_common(struct nm_bridge *b, int hw, int sw)
+{
+	int s_hw = hw, s_sw = sw;
+	int i, lim =b->bdg_active_ports;
+	uint32_t *tmp = b->tmp_bdg_port_index;
+
+	/*
+	New algorithm:
+	make a copy of bdg_port_index;
+	lookup NA(ifp)->bdg_port and SWNA(ifp)->bdg_port
+	in the array of bdg_port_index, replacing them with
+	entries from the bottom of the array;
+	decrement bdg_active_ports;
+	acquire BDG_WLOCK() and copy back the array.
+	 */
+
+	if (netmap_debug & NM_DEBUG_BDG)
+		nm_prinf("detach %d and %d (lim %d)", hw, sw, lim);
+	/* make a copy of the list of active ports, update it,
+	 * and then copy back within BDG_WLOCK().
+	 */
+	memcpy(b->tmp_bdg_port_index, b->bdg_port_index, sizeof(b->tmp_bdg_port_index));
+	for (i = 0; (hw >= 0 || sw >= 0) && i < lim; ) {
+		if (hw >= 0 && tmp[i] == hw) {
+			nm_prdis("detach hw %d at %d", hw, i);
+			lim--; /* point to last active port */
+			tmp[i] = tmp[lim]; /* swap with i */
+			tmp[lim] = hw;	/* now this is inactive */
+			hw = -1;
+		} else if (sw >= 0 && tmp[i] == sw) {
+			nm_prdis("detach sw %d at %d", sw, i);
+			lim--;
+			tmp[i] = tmp[lim];
+			tmp[lim] = sw;
+			sw = -1;
+		} else {
+			i++;
+		}
+	}
+	if (hw >= 0 || sw >= 0) {
+		nm_prerr("delete failed hw %d sw %d, should panic...", hw, sw);
+	}
+
+	BDG_WLOCK(b);
+	if (b->bdg_ops.dtor)
+		b->bdg_ops.dtor(b->bdg_ports[s_hw]);
+	b->bdg_ports[s_hw] = NULL;
+	if (s_sw >= 0) {
+		b->bdg_ports[s_sw] = NULL;
+	}
+	memcpy(b->bdg_port_index, b->tmp_bdg_port_index, sizeof(b->tmp_bdg_port_index));
+	b->bdg_active_ports = lim;
+	BDG_WUNLOCK(b);
+
+	nm_prdis("now %d active ports", lim);
+	netmap_bdg_free(b);
+}
+
+
+/* nm_bdg_ctl callback for VALE ports */
+int
+netmap_vp_bdg_ctl(struct nmreq_header *hdr, struct netmap_adapter *na)
+{
+	struct netmap_vp_adapter *vpna = (struct netmap_vp_adapter *)na;
+	struct nm_bridge *b = vpna->na_bdg;
+
+	if (hdr->nr_reqtype == NETMAP_REQ_VALE_ATTACH) {
+		return 0; /* nothing to do */
+	}
+	if (b) {
+		netmap_set_all_rings(na, 0 /* disable */);
+		netmap_bdg_detach_common(b, vpna->bdg_port, -1);
+		vpna->na_bdg = NULL;
+		netmap_set_all_rings(na, 1 /* enable */);
+	}
+	/* I have took reference just for attach */
+	netmap_adapter_put(na);
+	return 0;
+}
+
+int
+netmap_default_bdg_attach(const char *name, struct netmap_adapter *na,
+		struct nm_bridge *b)
+{
+	return NM_NEED_BWRAP;
+}
+
+/* Try to get a reference to a netmap adapter attached to a VALE switch.
+ * If the adapter is found (or is created), this function returns 0, a
+ * non NULL pointer is returned into *na, and the caller holds a
+ * reference to the adapter.
+ * If an adapter is not found, then no reference is grabbed and the
+ * function returns an error code, or 0 if there is just a VALE prefix
+ * mismatch. Therefore the caller holds a reference when
+ * (*na != NULL && return == 0).
+ */
+int
+netmap_get_bdg_na(struct nmreq_header *hdr, struct netmap_adapter **na,
+	struct netmap_mem_d *nmd, int create, struct netmap_bdg_ops *ops)
+{
+	char *nr_name = hdr->nr_name;
+	const char *ifname;
+	if_t ifp = NULL;
+	int error = 0;
+	struct netmap_vp_adapter *vpna, *hostna = NULL;
+	struct nm_bridge *b;
+	uint32_t i, j;
+	uint32_t cand = NM_BDG_NOPORT, cand2 = NM_BDG_NOPORT;
+	int needed;
+
+	*na = NULL;     /* default return value */
+
+	/* first try to see if this is a bridge port. */
+	NMG_LOCK_ASSERT();
+	if (strncmp(nr_name, ops->name, strlen(ops->name) - 1)) {
+		return 0;  /* no error, but no VALE prefix */
+	}
+
+	b = nm_find_bridge(nr_name, create, ops);
+	if (b == NULL) {
+		nm_prdis("no bridges available for '%s'", nr_name);
+		return (create ? ENOMEM : ENXIO);
+	}
+	if (strlen(nr_name) < b->bdg_namelen) /* impossible */
+		panic("x");
+
+	/* Now we are sure that name starts with the bridge's name,
+	 * lookup the port in the bridge. We need to scan the entire
+	 * list. It is not important to hold a WLOCK on the bridge
+	 * during the search because NMG_LOCK already guarantees
+	 * that there are no other possible writers.
+	 */
+
+	/* lookup in the local list of ports */
+	for (j = 0; j < b->bdg_active_ports; j++) {
+		i = b->bdg_port_index[j];
+		vpna = b->bdg_ports[i];
+		nm_prdis("checking %s", vpna->up.name);
+		if (!strcmp(vpna->up.name, nr_name)) {
+			netmap_adapter_get(&vpna->up);
+			nm_prdis("found existing if %s refs %d", nr_name)
+			*na = &vpna->up;
+			return 0;
+		}
+	}
+	/* not found, should we create it? */
+	if (!create)
+		return ENXIO;
+	/* yes we should, see if we have space to attach entries */
+	needed = 2; /* in some cases we only need 1 */
+	if (b->bdg_active_ports + needed >= NM_BDG_MAXPORTS) {
+		nm_prerr("bridge full %d, cannot create new port", b->bdg_active_ports);
+		return ENOMEM;
+	}
+	/* record the next two ports available, but do not allocate yet */
+	cand = b->bdg_port_index[b->bdg_active_ports];
+	cand2 = b->bdg_port_index[b->bdg_active_ports + 1];
+	nm_prdis("+++ bridge %s port %s used %d avail %d %d",
+		b->bdg_basename, ifname, b->bdg_active_ports, cand, cand2);
+
+	/*
+	 * try see if there is a matching NIC with this name
+	 * (after the bridge's name)
+	 */
+	ifname = nr_name + b->bdg_namelen + 1;
+	ifp = ifunit_ref(ifname);
+	if (!ifp) {
+		/* Create an ephemeral virtual port.
+		 * This block contains all the ephemeral-specific logic.
+		 */
+
+		if (hdr->nr_reqtype != NETMAP_REQ_REGISTER) {
+			error = EINVAL;
+			goto out;
+		}
+
+		/* bdg_netmap_attach creates a struct netmap_adapter */
+		error = b->bdg_ops.vp_create(hdr, NULL, nmd, &vpna);
+		if (error) {
+			if (netmap_debug & NM_DEBUG_BDG)
+				nm_prerr("error %d", error);
+			goto out;
+		}
+		/* shortcut - we can skip get_hw_na(),
+		 * ownership check and nm_bdg_attach()
+		 */
+
+	} else {
+		struct netmap_adapter *hw;
+
+		/* the vale:nic syntax is only valid for some commands */
+		switch (hdr->nr_reqtype) {
+		case NETMAP_REQ_VALE_ATTACH:
+		case NETMAP_REQ_VALE_DETACH:
+		case NETMAP_REQ_VALE_POLLING_ENABLE:
+		case NETMAP_REQ_VALE_POLLING_DISABLE:
+			break; /* ok */
+		default:
+			error = EINVAL;
+			goto out;
+		}
+
+		error = netmap_get_hw_na(ifp, nmd, &hw);
+		if (error || hw == NULL)
+			goto out;
+
+		/* host adapter might not be created */
+		error = hw->nm_bdg_attach(nr_name, hw, b);
+		if (error == NM_NEED_BWRAP) {
+			error = b->bdg_ops.bwrap_attach(nr_name, hw);
+		}
+		if (error)
+			goto out;
+		vpna = hw->na_vp;
+		hostna = hw->na_hostvp;
+		if (hdr->nr_reqtype == NETMAP_REQ_VALE_ATTACH) {
+			/* Check if we need to skip the host rings. */
+			struct nmreq_vale_attach *areq =
+				(struct nmreq_vale_attach *)(uintptr_t)hdr->nr_body;
+			if (areq->reg.nr_mode != NR_REG_NIC_SW) {
+				hostna = NULL;
+			}
+		}
+	}
+
+	BDG_WLOCK(b);
+	vpna->bdg_port = cand;
+	nm_prdis("NIC  %p to bridge port %d", vpna, cand);
+	/* bind the port to the bridge (virtual ports are not active) */
+	b->bdg_ports[cand] = vpna;
+	vpna->na_bdg = b;
+	b->bdg_active_ports++;
+	if (hostna != NULL) {
+		/* also bind the host stack to the bridge */
+		b->bdg_ports[cand2] = hostna;
+		hostna->bdg_port = cand2;
+		hostna->na_bdg = b;
+		b->bdg_active_ports++;
+		nm_prdis("host %p to bridge port %d", hostna, cand2);
+	}
+	nm_prdis("if %s refs %d", ifname, vpna->up.na_refcount);
+	BDG_WUNLOCK(b);
+	*na = &vpna->up;
+	netmap_adapter_get(*na);
+
+out:
+	if (ifp)
+		if_rele(ifp);
+
+	return error;
+}
+
+/* Process NETMAP_REQ_VALE_ATTACH.
+ */
+int
+netmap_bdg_attach(struct nmreq_header *hdr, void *auth_token)
+{
+	struct nmreq_vale_attach *req =
+		(struct nmreq_vale_attach *)(uintptr_t)hdr->nr_body;
+	struct netmap_vp_adapter * vpna;
+	struct netmap_adapter *na = NULL;
+	struct netmap_mem_d *nmd = NULL;
+	struct nm_bridge *b = NULL;
+	int error;
+
+	NMG_LOCK();
+	/* permission check for modified bridges */
+	b = nm_find_bridge(hdr->nr_name, 0 /* don't create */, NULL);
+	if (b && !nm_bdg_valid_auth_token(b, auth_token)) {
+		error = EACCES;
+		goto unlock_exit;
+	}
+
+	if (req->reg.nr_mem_id) {
+		nmd = netmap_mem_find(req->reg.nr_mem_id);
+		if (nmd == NULL) {
+			error = EINVAL;
+			goto unlock_exit;
+		}
+	}
+
+	/* check for existing one */
+	error = netmap_get_vale_na(hdr, &na, nmd, 0);
+	if (na) {
+		error = EBUSY;
+		goto unref_exit;
+	}
+	error = netmap_get_vale_na(hdr, &na,
+				nmd, 1 /* create if not exists */);
+	if (error) { /* no device */
+		goto unlock_exit;
+	}
+
+	if (na == NULL) { /* VALE prefix missing */
+		error = EINVAL;
+		goto unlock_exit;
+	}
+
+	if (NETMAP_OWNED_BY_ANY(na)) {
+		error = EBUSY;
+		goto unref_exit;
+	}
+
+	if (na->nm_bdg_ctl) {
+		/* nop for VALE ports. The bwrap needs to put the hwna
+		 * in netmap mode (see netmap_bwrap_bdg_ctl)
+		 */
+		error = na->nm_bdg_ctl(hdr, na);
+		if (error)
+			goto unref_exit;
+		nm_prdis("registered %s to netmap-mode", na->name);
+	}
+	vpna = (struct netmap_vp_adapter *)na;
+	req->port_index = vpna->bdg_port;
+
+	if (nmd)
+		netmap_mem_put(nmd);
+
+	NMG_UNLOCK();
+	return 0;
+
+unref_exit:
+	netmap_adapter_put(na);
+unlock_exit:
+	if (nmd)
+		netmap_mem_put(nmd);
+
+	NMG_UNLOCK();
+	return error;
+}
+
+
+int
+nm_is_bwrap(struct netmap_adapter *na)
+{
+	return na->nm_register == netmap_bwrap_reg;
+}
+
+/* Process NETMAP_REQ_VALE_DETACH.
+ */
+int
+netmap_bdg_detach(struct nmreq_header *hdr, void *auth_token)
+{
+	int error;
+
+	NMG_LOCK();
+	error = netmap_bdg_detach_locked(hdr, auth_token);
+	NMG_UNLOCK();
+	return error;
+}
+
+int
+netmap_bdg_detach_locked(struct nmreq_header *hdr, void *auth_token)
+{
+	struct nmreq_vale_detach *nmreq_det = (void *)(uintptr_t)hdr->nr_body;
+	struct netmap_vp_adapter *vpna;
+	struct netmap_adapter *na;
+	struct nm_bridge *b = NULL;
+	int error;
+
+	/* permission check for modified bridges */
+	b = nm_find_bridge(hdr->nr_name, 0 /* don't create */, NULL);
+	if (b && !nm_bdg_valid_auth_token(b, auth_token)) {
+		error = EACCES;
+		goto error_exit;
+	}
+
+	error = netmap_get_vale_na(hdr, &na, NULL, 0 /* don't create */);
+	if (error) { /* no device, or another bridge or user owns the device */
+		goto error_exit;
+	}
+
+	if (na == NULL) { /* VALE prefix missing */
+		error = EINVAL;
+		goto error_exit;
+	} else if (nm_is_bwrap(na) &&
+		   ((struct netmap_bwrap_adapter *)na)->na_polling_state) {
+		/* Don't detach a NIC with polling */
+		error = EBUSY;
+		goto unref_exit;
+	}
+
+	vpna = (struct netmap_vp_adapter *)na;
+	if (na->na_vp != vpna) {
+		/* trying to detach first attach of VALE persistent port attached
+		 * to 2 bridges
+		 */
+		error = EBUSY;
+		goto unref_exit;
+	}
+	nmreq_det->port_index = vpna->bdg_port;
+
+	if (na->nm_bdg_ctl) {
+		/* remove the port from bridge. The bwrap
+		 * also needs to put the hwna in normal mode
+		 */
+		error = na->nm_bdg_ctl(hdr, na);
+	}
+
+unref_exit:
+	netmap_adapter_put(na);
+error_exit:
+	return error;
+
+}
+
+
+struct nm_bdg_polling_state;
+struct
+nm_bdg_kthread {
+	struct nm_kctx *nmk;
+	u_int qfirst;
+	u_int qlast;
+	struct nm_bdg_polling_state *bps;
+};
+
+struct nm_bdg_polling_state {
+	bool configured;
+	bool stopped;
+	struct netmap_bwrap_adapter *bna;
+	uint32_t mode;
+	u_int qfirst;
+	u_int qlast;
+	u_int cpu_from;
+	u_int ncpus;
+	struct nm_bdg_kthread *kthreads;
+};
+
+static void
+netmap_bwrap_polling(void *data)
+{
+	struct nm_bdg_kthread *nbk = data;
+	struct netmap_bwrap_adapter *bna;
+	u_int qfirst, qlast, i;
+	struct netmap_kring **kring0, *kring;
+
+	if (!nbk)
+		return;
+	qfirst = nbk->qfirst;
+	qlast = nbk->qlast;
+	bna = nbk->bps->bna;
+	kring0 = NMR(bna->hwna, NR_RX);
+
+	for (i = qfirst; i < qlast; i++) {
+		kring = kring0[i];
+		kring->nm_notify(kring, 0);
+	}
+}
+
+static int
+nm_bdg_create_kthreads(struct nm_bdg_polling_state *bps)
+{
+	struct nm_kctx_cfg kcfg;
+	int i, j;
+
+	bps->kthreads = nm_os_malloc(sizeof(struct nm_bdg_kthread) * bps->ncpus);
+	if (bps->kthreads == NULL)
+		return ENOMEM;
+
+	bzero(&kcfg, sizeof(kcfg));
+	kcfg.worker_fn = netmap_bwrap_polling;
+	for (i = 0; i < bps->ncpus; i++) {
+		struct nm_bdg_kthread *t = bps->kthreads + i;
+		int all = (bps->ncpus == 1 &&
+			bps->mode == NETMAP_POLLING_MODE_SINGLE_CPU);
+		int affinity = bps->cpu_from + i;
+
+		t->bps = bps;
+		t->qfirst = all ? bps->qfirst /* must be 0 */: affinity;
+		t->qlast = all ? bps->qlast : t->qfirst + 1;
+		if (netmap_verbose)
+			nm_prinf("kthread %d a:%u qf:%u ql:%u", i, affinity, t->qfirst,
+				t->qlast);
+
+		kcfg.type = i;
+		kcfg.worker_private = t;
+		t->nmk = nm_os_kctx_create(&kcfg, NULL);
+		if (t->nmk == NULL) {
+			goto cleanup;
+		}
+		nm_os_kctx_worker_setaff(t->nmk, affinity);
+	}
+	return 0;
+
+cleanup:
+	for (j = 0; j < i; j++) {
+		struct nm_bdg_kthread *t = bps->kthreads + i;
+		nm_os_kctx_destroy(t->nmk);
+	}
+	nm_os_free(bps->kthreads);
+	return EFAULT;
+}
+
+/* A variant of ptnetmap_start_kthreads() */
+static int
+nm_bdg_polling_start_kthreads(struct nm_bdg_polling_state *bps)
+{
+	int error, i, j;
+
+	if (!bps) {
+		nm_prerr("polling is not configured");
+		return EFAULT;
+	}
+	bps->stopped = false;
+
+	for (i = 0; i < bps->ncpus; i++) {
+		struct nm_bdg_kthread *t = bps->kthreads + i;
+		error = nm_os_kctx_worker_start(t->nmk);
+		if (error) {
+			nm_prerr("error in nm_kthread_start(): %d", error);
+			goto cleanup;
+		}
+	}
+	return 0;
+
+cleanup:
+	for (j = 0; j < i; j++) {
+		struct nm_bdg_kthread *t = bps->kthreads + i;
+		nm_os_kctx_worker_stop(t->nmk);
+	}
+	bps->stopped = true;
+	return error;
+}
+
+static void
+nm_bdg_polling_stop_delete_kthreads(struct nm_bdg_polling_state *bps)
+{
+	int i;
+
+	if (!bps)
+		return;
+
+	for (i = 0; i < bps->ncpus; i++) {
+		struct nm_bdg_kthread *t = bps->kthreads + i;
+		nm_os_kctx_worker_stop(t->nmk);
+		nm_os_kctx_destroy(t->nmk);
+	}
+	bps->stopped = true;
+}
+
+static int
+get_polling_cfg(struct nmreq_vale_polling *req, struct netmap_adapter *na,
+		struct nm_bdg_polling_state *bps)
+{
+	unsigned int avail_cpus, core_from;
+	unsigned int qfirst, qlast;
+	uint32_t i = req->nr_first_cpu_id;
+	uint32_t req_cpus = req->nr_num_polling_cpus;
+
+	avail_cpus = nm_os_ncpus();
+
+	if (req_cpus == 0) {
+		nm_prerr("req_cpus must be > 0");
+		return EINVAL;
+	} else if (req_cpus >= avail_cpus) {
+		nm_prerr("Cannot use all the CPUs in the system");
+		return EINVAL;
+	}
+
+	if (req->nr_mode == NETMAP_POLLING_MODE_MULTI_CPU) {
+		/* Use a separate core for each ring. If nr_num_polling_cpus>1
+		 * more consecutive rings are polled.
+		 * For example, if nr_first_cpu_id=2 and nr_num_polling_cpus=2,
+		 * ring 2 and 3 are polled by core 2 and 3, respectively. */
+		if (i + req_cpus > nma_get_nrings(na, NR_RX)) {
+			nm_prerr("Rings %u-%u not in range (have %d rings)",
+				i, i + req_cpus, nma_get_nrings(na, NR_RX));
+			return EINVAL;
+		}
+		qfirst = i;
+		qlast = qfirst + req_cpus;
+		core_from = qfirst;
+
+	} else if (req->nr_mode == NETMAP_POLLING_MODE_SINGLE_CPU) {
+		/* Poll all the rings using a core specified by nr_first_cpu_id.
+		 * the number of cores must be 1. */
+		if (req_cpus != 1) {
+			nm_prerr("ncpus must be 1 for NETMAP_POLLING_MODE_SINGLE_CPU "
+				"(was %d)", req_cpus);
+			return EINVAL;
+		}
+		qfirst = 0;
+		qlast = nma_get_nrings(na, NR_RX);
+		core_from = i;
+	} else {
+		nm_prerr("Invalid polling mode");
+		return EINVAL;
+	}
+
+	bps->mode = req->nr_mode;
+	bps->qfirst = qfirst;
+	bps->qlast = qlast;
+	bps->cpu_from = core_from;
+	bps->ncpus = req_cpus;
+	nm_prinf("%s qfirst %u qlast %u cpu_from %u ncpus %u",
+		req->nr_mode == NETMAP_POLLING_MODE_MULTI_CPU ?
+		"MULTI" : "SINGLE",
+		qfirst, qlast, core_from, req_cpus);
+	return 0;
+}
+
+static int
+nm_bdg_ctl_polling_start(struct nmreq_vale_polling *req, struct netmap_adapter *na)
+{
+	struct nm_bdg_polling_state *bps;
+	struct netmap_bwrap_adapter *bna;
+	int error;
+
+	bna = (struct netmap_bwrap_adapter *)na;
+	if (bna->na_polling_state) {
+		nm_prerr("ERROR adapter already in polling mode");
+		return EFAULT;
+	}
+
+	bps = nm_os_malloc(sizeof(*bps));
+	if (!bps)
+		return ENOMEM;
+	bps->configured = false;
+	bps->stopped = true;
+
+	if (get_polling_cfg(req, na, bps)) {
+		nm_os_free(bps);
+		return EINVAL;
+	}
+
+	if (nm_bdg_create_kthreads(bps)) {
+		nm_os_free(bps);
+		return EFAULT;
+	}
+
+	bps->configured = true;
+	bna->na_polling_state = bps;
+	bps->bna = bna;
+
+	/* disable interrupts if possible */
+	nma_intr_enable(bna->hwna, 0);
+	/* start kthread now */
+	error = nm_bdg_polling_start_kthreads(bps);
+	if (error) {
+		nm_prerr("ERROR nm_bdg_polling_start_kthread()");
+		nm_os_free(bps->kthreads);
+		nm_os_free(bps);
+		bna->na_polling_state = NULL;
+		nma_intr_enable(bna->hwna, 1);
+	}
+	return error;
+}
+
+static int
+nm_bdg_ctl_polling_stop(struct netmap_adapter *na)
+{
+	struct netmap_bwrap_adapter *bna = (struct netmap_bwrap_adapter *)na;
+	struct nm_bdg_polling_state *bps;
+
+	if (!bna->na_polling_state) {
+		nm_prerr("ERROR adapter is not in polling mode");
+		return EFAULT;
+	}
+	bps = bna->na_polling_state;
+	nm_bdg_polling_stop_delete_kthreads(bna->na_polling_state);
+	bps->configured = false;
+	nm_os_free(bps);
+	bna->na_polling_state = NULL;
+	/* re-enable interrupts */
+	nma_intr_enable(bna->hwna, 1);
+	return 0;
+}
+
+int
+nm_bdg_polling(struct nmreq_header *hdr)
+{
+	struct nmreq_vale_polling *req =
+		(struct nmreq_vale_polling *)(uintptr_t)hdr->nr_body;
+	struct netmap_adapter *na = NULL;
+	int error = 0;
+
+	NMG_LOCK();
+	error = netmap_get_vale_na(hdr, &na, NULL, /*create=*/0);
+	if (na && !error) {
+		if (!nm_is_bwrap(na)) {
+			error = EOPNOTSUPP;
+		} else if (hdr->nr_reqtype == NETMAP_BDG_POLLING_ON) {
+			error = nm_bdg_ctl_polling_start(req, na);
+			if (!error)
+				netmap_adapter_get(na);
+		} else {
+			error = nm_bdg_ctl_polling_stop(na);
+			if (!error)
+				netmap_adapter_put(na);
+		}
+		netmap_adapter_put(na);
+	} else if (!na && !error) {
+		/* Not VALE port. */
+		error = EINVAL;
+	}
+	NMG_UNLOCK();
+
+	return error;
+}
+
+/* Called by external kernel modules (e.g., Openvswitch).
+ * to set configure/lookup/dtor functions of a VALE instance.
+ * Register callbacks to the given bridge. 'name' may be just
+ * bridge's name (including ':' if it is not just NM_BDG_NAME).
+ *
+ * Called without NMG_LOCK.
+ */
+
+int
+netmap_bdg_regops(const char *name, struct netmap_bdg_ops *bdg_ops, void *private_data, void *auth_token)
+{
+	struct nm_bridge *b;
+	int error = 0;
+
+	NMG_LOCK();
+	b = nm_find_bridge(name, 0 /* don't create */, NULL);
+	if (!b) {
+		error = ENXIO;
+		goto unlock_regops;
+	}
+	if (!nm_bdg_valid_auth_token(b, auth_token)) {
+		error = EACCES;
+		goto unlock_regops;
+	}
+
+	BDG_WLOCK(b);
+	if (!bdg_ops) {
+		/* resetting the bridge */
+		bzero(b->ht, sizeof(struct nm_hash_ent) * NM_BDG_HASH);
+		b->bdg_ops = b->bdg_saved_ops;
+		b->private_data = b->ht;
+	} else {
+		/* modifying the bridge */
+		b->private_data = private_data;
+#define nm_bdg_override(m) if (bdg_ops->m) b->bdg_ops.m = bdg_ops->m
+		nm_bdg_override(lookup);
+		nm_bdg_override(config);
+		nm_bdg_override(dtor);
+		nm_bdg_override(vp_create);
+		nm_bdg_override(bwrap_attach);
+#undef nm_bdg_override
+
+	}
+	BDG_WUNLOCK(b);
+
+unlock_regops:
+	NMG_UNLOCK();
+	return error;
+}
+
+
+int
+netmap_bdg_config(struct nm_ifreq *nr)
+{
+	struct nm_bridge *b;
+	int error = EINVAL;
+
+	NMG_LOCK();
+	b = nm_find_bridge(nr->nifr_name, 0, NULL);
+	if (!b) {
+		NMG_UNLOCK();
+		return error;
+	}
+	NMG_UNLOCK();
+	/* Don't call config() with NMG_LOCK() held */
+	BDG_RLOCK(b);
+	if (b->bdg_ops.config != NULL)
+		error = b->bdg_ops.config(nr);
+	BDG_RUNLOCK(b);
+	return error;
+}
+
+
+/* nm_register callback for VALE ports */
+int
+netmap_vp_reg(struct netmap_adapter *na, int onoff)
+{
+	struct netmap_vp_adapter *vpna =
+		(struct netmap_vp_adapter*)na;
+
+	/* persistent ports may be put in netmap mode
+	 * before being attached to a bridge
+	 */
+	if (vpna->na_bdg)
+		BDG_WLOCK(vpna->na_bdg);
+	if (onoff) {
+		netmap_krings_mode_commit(na, onoff);
+		if (na->active_fds == 0)
+			na->na_flags |= NAF_NETMAP_ON;
+		 /* XXX on FreeBSD, persistent VALE ports should also
+		 * toggle IFCAP_NETMAP in na->ifp (2014-03-16)
+		 */
+	} else {
+		if (na->active_fds == 0)
+			na->na_flags &= ~NAF_NETMAP_ON;
+		netmap_krings_mode_commit(na, onoff);
+	}
+	if (vpna->na_bdg)
+		BDG_WUNLOCK(vpna->na_bdg);
+	return 0;
+}
+
+
+/* rxsync code used by VALE ports nm_rxsync callback and also
+ * internally by the brwap
+ */
+static int
+netmap_vp_rxsync_locked(struct netmap_kring *kring, int flags)
+{
+	struct netmap_adapter *na = kring->na;
+	struct netmap_ring *ring = kring->ring;
+	u_int nm_i, lim = kring->nkr_num_slots - 1;
+	u_int head = kring->rhead;
+	int n;
+
+	if (head > lim) {
+		nm_prerr("ouch dangerous reset!!!");
+		n = netmap_ring_reinit(kring);
+		goto done;
+	}
+
+	/* First part, import newly received packets. */
+	/* actually nothing to do here, they are already in the kring */
+
+	/* Second part, skip past packets that userspace has released. */
+	nm_i = kring->nr_hwcur;
+	if (nm_i != head) {
+		/* consistency check, but nothing really important here */
+		for (n = 0; likely(nm_i != head); n++) {
+			struct netmap_slot *slot = &ring->slot[nm_i];
+			void *addr = NMB(na, slot);
+
+			if (addr == NETMAP_BUF_BASE(kring->na)) { /* bad buf */
+				nm_prerr("bad buffer index %d, ignore ?",
+					slot->buf_idx);
+			}
+			slot->flags &= ~NS_BUF_CHANGED;
+			nm_i = nm_next(nm_i, lim);
+		}
+		kring->nr_hwcur = head;
+	}
+
+	n = 0;
+done:
+	return n;
+}
+
+/*
+ * nm_rxsync callback for VALE ports
+ * user process reading from a VALE switch.
+ * Already protected against concurrent calls from userspace,
+ * but we must acquire the queue's lock to protect against
+ * writers on the same queue.
+ */
+int
+netmap_vp_rxsync(struct netmap_kring *kring, int flags)
+{
+	int n;
+
+	mtx_lock(&kring->q_lock);
+	n = netmap_vp_rxsync_locked(kring, flags);
+	mtx_unlock(&kring->q_lock);
+	return n;
+}
+
+int
+netmap_bwrap_attach(const char *nr_name, struct netmap_adapter *hwna,
+		struct netmap_bdg_ops *ops)
+{
+	return ops->bwrap_attach(nr_name, hwna);
+}
+
+
+/* Bridge wrapper code (bwrap).
+ * This is used to connect a non-VALE-port netmap_adapter (hwna) to a
+ * VALE switch.
+ * The main task is to swap the meaning of tx and rx rings to match the
+ * expectations of the VALE switch code (see nm_bdg_flush).
+ *
+ * The bwrap works by interposing a netmap_bwrap_adapter between the
+ * rest of the system and the hwna. The netmap_bwrap_adapter looks like
+ * a netmap_vp_adapter to the rest the system, but, internally, it
+ * translates all callbacks to what the hwna expects.
+ *
+ * Note that we have to intercept callbacks coming from two sides:
+ *
+ *  - callbacks coming from the netmap module are intercepted by
+ *    passing around the netmap_bwrap_adapter instead of the hwna
+ *
+ *  - callbacks coming from outside of the netmap module only know
+ *    about the hwna. This, however, only happens in interrupt
+ *    handlers, where only the hwna->nm_notify callback is called.
+ *    What the bwrap does is to overwrite the hwna->nm_notify callback
+ *    with its own netmap_bwrap_intr_notify.
+ *    XXX This assumes that the hwna->nm_notify callback was the
+ *    standard netmap_notify(), as it is the case for nic adapters.
+ *    Any additional action performed by hwna->nm_notify will not be
+ *    performed by netmap_bwrap_intr_notify.
+ *
+ * Additionally, the bwrap can optionally attach the host rings pair
+ * of the wrapped adapter to a different port of the switch.
+ */
+
+
+static void
+netmap_bwrap_dtor(struct netmap_adapter *na)
+{
+	struct netmap_bwrap_adapter *bna = (struct netmap_bwrap_adapter*)na;
+	struct netmap_adapter *hwna = bna->hwna;
+	struct nm_bridge *b = bna->up.na_bdg,
+		*bh = bna->host.na_bdg;
+
+	if (bna->host.up.nm_mem)
+		netmap_mem_put(bna->host.up.nm_mem);
+
+	if (b) {
+		netmap_bdg_detach_common(b, bna->up.bdg_port,
+			    (bh ? bna->host.bdg_port : -1));
+	}
+
+	nm_prdis("na %p", na);
+	na->ifp = NULL;
+	bna->host.up.ifp = NULL;
+	hwna->na_vp = bna->saved_na_vp;
+	hwna->na_hostvp = NULL;
+	hwna->na_private = NULL;
+	hwna->na_flags &= ~NAF_BUSY;
+	netmap_adapter_put(hwna);
+
+}
+
+
+/*
+ * Intr callback for NICs connected to a bridge.
+ * Simply ignore tx interrupts (maybe we could try to recover space ?)
+ * and pass received packets from nic to the bridge.
+ *
+ * XXX TODO check locking: this is called from the interrupt
+ * handler so we should make sure that the interface is not
+ * disconnected while passing down an interrupt.
+ *
+ * Note, no user process can access this NIC or the host stack.
+ * The only part of the ring that is significant are the slots,
+ * and head/cur/tail are set from the kring as needed
+ * (part as a receive ring, part as a transmit ring).
+ *
+ * callback that overwrites the hwna notify callback.
+ * Packets come from the outside or from the host stack and are put on an
+ * hwna rx ring.
+ * The bridge wrapper then sends the packets through the bridge.
+ */
+int
+netmap_bwrap_intr_notify(struct netmap_kring *kring, int flags)
+{
+	struct netmap_adapter *na = kring->na;
+	struct netmap_bwrap_adapter *bna = na->na_private;
+	struct netmap_kring *bkring;
+	struct netmap_vp_adapter *vpna = &bna->up;
+	u_int ring_nr = kring->ring_id;
+	int ret = NM_IRQ_COMPLETED;
+	int error;
+
+	if (netmap_debug & NM_DEBUG_RXINTR)
+	    nm_prinf("%s %s 0x%x", na->name, kring->name, flags);
+
+	bkring = vpna->up.tx_rings[ring_nr];
+
+	/* make sure the ring is not disabled */
+	if (nm_kr_tryget(kring, 0 /* can't sleep */, NULL)) {
+		return EIO;
+	}
+
+	if (netmap_debug & NM_DEBUG_RXINTR)
+	    nm_prinf("%s head %d cur %d tail %d",  na->name,
+		kring->rhead, kring->rcur, kring->rtail);
+
+	/* simulate a user wakeup on the rx ring
+	 * fetch packets that have arrived.
+	 */
+	error = kring->nm_sync(kring, 0);
+	if (error)
+		goto put_out;
+	if (kring->nr_hwcur == kring->nr_hwtail) {
+		if (netmap_verbose)
+			nm_prlim(1, "interrupt with no packets on %s",
+				kring->name);
+		goto put_out;
+	}
+
+	/* new packets are kring->rcur to kring->nr_hwtail, and the bkring
+	 * had hwcur == bkring->rhead. So advance bkring->rhead to kring->nr_hwtail
+	 * to push all packets out.
+	 */
+	bkring->rhead = bkring->rcur = kring->nr_hwtail;
+
+	bkring->nm_sync(bkring, flags);
+
+	/* mark all buffers as released on this ring */
+	kring->rhead = kring->rcur = kring->rtail = kring->nr_hwtail;
+	/* another call to actually release the buffers */
+	error = kring->nm_sync(kring, 0);
+
+	/* The second rxsync may have further advanced hwtail. If this happens,
+	 *  return NM_IRQ_RESCHED, otherwise just return NM_IRQ_COMPLETED. */
+	if (kring->rcur != kring->nr_hwtail) {
+		ret = NM_IRQ_RESCHED;
+	}
+put_out:
+	nm_kr_put(kring);
+
+	return error ? error : ret;
+}
+
+
+/* nm_register callback for bwrap */
+int
+netmap_bwrap_reg(struct netmap_adapter *na, int onoff)
+{
+	struct netmap_bwrap_adapter *bna =
+		(struct netmap_bwrap_adapter *)na;
+	struct netmap_adapter *hwna = bna->hwna;
+	struct netmap_vp_adapter *hostna = &bna->host;
+	int error, i;
+	enum txrx t;
+
+	nm_prdis("%s %s", na->name, onoff ? "on" : "off");
+
+	if (onoff) {
+		/* netmap_do_regif has been called on the bwrap na.
+		 * We need to pass the information about the
+		 * memory allocator down to the hwna before
+		 * putting it in netmap mode
+		 */
+		hwna->na_lut = na->na_lut;
+
+		if (hostna->na_bdg) {
+			/* if the host rings have been attached to switch,
+			 * we need to copy the memory allocator information
+			 * in the hostna also
+			 */
+			hostna->up.na_lut = na->na_lut;
+		}
+
+	}
+
+	/* pass down the pending ring state information */
+	for_rx_tx(t) {
+		for (i = 0; i < netmap_all_rings(na, t); i++) {
+			NMR(hwna, nm_txrx_swap(t))[i]->nr_pending_mode =
+				NMR(na, t)[i]->nr_pending_mode;
+		}
+	}
+
+	/* forward the request to the hwna */
+	error = hwna->nm_register(hwna, onoff);
+	if (error)
+		return error;
+
+	/* copy up the current ring state information */
+	for_rx_tx(t) {
+		for (i = 0; i < netmap_all_rings(na, t); i++) {
+			struct netmap_kring *kring = NMR(hwna, nm_txrx_swap(t))[i];
+			NMR(na, t)[i]->nr_mode = kring->nr_mode;
+		}
+	}
+
+	/* impersonate a netmap_vp_adapter */
+	netmap_vp_reg(na, onoff);
+	if (hostna->na_bdg)
+		netmap_vp_reg(&hostna->up, onoff);
+
+	if (onoff) {
+		u_int i;
+		/* intercept the hwna nm_nofify callback on the hw rings */
+		for (i = 0; i < hwna->num_rx_rings; i++) {
+			hwna->rx_rings[i]->save_notify = hwna->rx_rings[i]->nm_notify;
+			hwna->rx_rings[i]->nm_notify = bna->nm_intr_notify;
+		}
+		i = hwna->num_rx_rings; /* for safety */
+		/* save the host ring notify unconditionally */
+		for (; i < netmap_real_rings(hwna, NR_RX); i++) {
+			hwna->rx_rings[i]->save_notify =
+				hwna->rx_rings[i]->nm_notify;
+			if (hostna->na_bdg) {
+				/* also intercept the host ring notify */
+				hwna->rx_rings[i]->nm_notify =
+					netmap_bwrap_intr_notify;
+				na->tx_rings[i]->nm_sync = na->nm_txsync;
+			}
+		}
+		if (na->active_fds == 0)
+			na->na_flags |= NAF_NETMAP_ON;
+	} else {
+		u_int i;
+
+		if (na->active_fds == 0)
+			na->na_flags &= ~NAF_NETMAP_ON;
+
+		/* reset all notify callbacks (including host ring) */
+		for (i = 0; i < netmap_all_rings(hwna, NR_RX); i++) {
+			hwna->rx_rings[i]->nm_notify =
+				hwna->rx_rings[i]->save_notify;
+			hwna->rx_rings[i]->save_notify = NULL;
+		}
+		hwna->na_lut.lut = NULL;
+		hwna->na_lut.plut = NULL;
+		hwna->na_lut.objtotal = 0;
+		hwna->na_lut.objsize = 0;
+
+		/* reset the number of host rings to default */
+		for_rx_tx(t) {
+			nma_set_host_nrings(hwna, t, 1);
+		}
+
+	}
+
+	return 0;
+}
+
+/* nm_config callback for bwrap */
+static int
+netmap_bwrap_config(struct netmap_adapter *na, struct nm_config_info *info)
+{
+	struct netmap_bwrap_adapter *bna =
+		(struct netmap_bwrap_adapter *)na;
+	struct netmap_adapter *hwna = bna->hwna;
+	int error;
+
+	/* cache the lut in the embedded host adapter */
+	error = netmap_mem_get_lut(hwna->nm_mem, &bna->host.up.na_lut);
+	if (error)
+		return error;
+
+	/* Forward the request to the hwna. It may happen that nobody
+	 * registered hwna yet, so netmap_mem_get_lut() may have not
+	 * been called yet. */
+	error = netmap_mem_get_lut(hwna->nm_mem, &hwna->na_lut);
+	if (error)
+		return error;
+	netmap_update_config(hwna);
+	/* swap the results and propagate */
+	info->num_tx_rings = hwna->num_rx_rings;
+	info->num_tx_descs = hwna->num_rx_desc;
+	info->num_rx_rings = hwna->num_tx_rings;
+	info->num_rx_descs = hwna->num_tx_desc;
+	info->rx_buf_maxsize = hwna->rx_buf_maxsize;
+
+	if (na->na_flags & NAF_HOST_RINGS) {
+		struct netmap_adapter *hostna = &bna->host.up;
+		enum txrx t;
+
+		/* limit the number of host rings to that of hw */
+		if (na->na_flags & NAF_HOST_ALL) {
+			hostna->num_tx_rings = nma_get_nrings(hwna, NR_RX);
+			hostna->num_rx_rings = nma_get_nrings(hwna, NR_TX);
+		} else {
+			nm_bound_var(&hostna->num_tx_rings, 1, 1,
+				nma_get_nrings(hwna, NR_TX), NULL);
+			nm_bound_var(&hostna->num_rx_rings, 1, 1,
+				nma_get_nrings(hwna, NR_RX), NULL);
+		}
+		for_rx_tx(t) {
+			enum txrx r = nm_txrx_swap(t);
+			u_int nr = nma_get_nrings(hostna, t);
+
+			nma_set_host_nrings(na, t, nr);
+			if (nma_get_host_nrings(hwna, t) < nr) {
+				nma_set_host_nrings(hwna, t, nr);
+			}
+			nma_set_ndesc(hostna, t, nma_get_ndesc(hwna, r));
+		}
+	}
+
+	return 0;
+}
+
+/* nm_bufcfg callback for bwrap */
+static int
+netmap_bwrap_bufcfg(struct netmap_kring *kring, uint64_t target)
+{
+	struct netmap_adapter *na = kring->na;
+	struct netmap_bwrap_adapter *bna =
+		(struct netmap_bwrap_adapter *)na;
+	struct netmap_adapter *hwna = bna->hwna;
+	struct netmap_kring *hwkring;
+	enum txrx r;
+	int error;
+
+	/* we need the hw kring that corresponds to the bwrap one:
+	 * remember that rx and tx are swapped
+	 */
+	r = nm_txrx_swap(kring->tx);
+	hwkring = NMR(hwna, r)[kring->ring_id];
+
+	/* copy down the offset information, forward the request
+	 * and copy up the results
+	 */
+	hwkring->offset_mask = kring->offset_mask;
+	hwkring->offset_max  = kring->offset_max;
+	hwkring->offset_gap  = kring->offset_gap;
+
+	error = hwkring->nm_bufcfg(hwkring, target);
+	if (error)
+		return error;
+
+	kring->hwbuf_len = hwkring->hwbuf_len;
+	kring->buf_align = hwkring->buf_align;
+
+	return 0;
+}
+
+/* nm_krings_create callback for bwrap */
+int
+netmap_bwrap_krings_create_common(struct netmap_adapter *na)
+{
+	struct netmap_bwrap_adapter *bna =
+		(struct netmap_bwrap_adapter *)na;
+	struct netmap_adapter *hwna = bna->hwna;
+	struct netmap_adapter *hostna = &bna->host.up;
+	int i, error = 0;
+	enum txrx t;
+
+	/* also create the hwna krings */
+	error = hwna->nm_krings_create(hwna);
+	if (error) {
+		return error;
+	}
+
+	/* increment the usage counter for all the hwna krings */
+	for_rx_tx(t) {
+		for (i = 0; i < netmap_all_rings(hwna, t); i++) {
+			NMR(hwna, t)[i]->users++;
+			/* this to prevent deletion of the rings through
+			 * our krings, instead of through the hwna ones */
+			NMR(na, t)[i]->nr_kflags |= NKR_NEEDRING;
+		}
+	}
+
+	/* now create the actual rings */
+	error = netmap_mem_rings_create(hwna);
+	if (error) {
+		goto err_dec_users;
+	}
+
+	/* cross-link the netmap rings
+	 * The original number of rings comes from hwna,
+	 * rx rings on one side equals tx rings on the other.
+	 */
+	for_rx_tx(t) {
+		enum txrx r = nm_txrx_swap(t); /* swap NR_TX <-> NR_RX */
+		for (i = 0; i < netmap_all_rings(hwna, r); i++) {
+			NMR(na, t)[i]->nkr_num_slots = NMR(hwna, r)[i]->nkr_num_slots;
+			NMR(na, t)[i]->ring = NMR(hwna, r)[i]->ring;
+		}
+	}
+
+	if (na->na_flags & NAF_HOST_RINGS) {
+		/* the hostna rings are the host rings of the bwrap.
+		 * The corresponding krings must point back to the
+		 * hostna
+		 */
+		hostna->tx_rings = &na->tx_rings[na->num_tx_rings];
+		hostna->rx_rings = &na->rx_rings[na->num_rx_rings];
+		for_rx_tx(t) {
+			for (i = 0; i < nma_get_nrings(hostna, t); i++) {
+				NMR(hostna, t)[i]->na = hostna;
+			}
+		}
+	}
+
+	return 0;
+
+err_dec_users:
+	for_rx_tx(t) {
+		for (i = 0; i < netmap_all_rings(hwna, t); i++) {
+			NMR(hwna, t)[i]->users--;
+			NMR(na, t)[i]->users--;
+		}
+	}
+	hwna->nm_krings_delete(hwna);
+	return error;
+}
+
+
+void
+netmap_bwrap_krings_delete_common(struct netmap_adapter *na)
+{
+	struct netmap_bwrap_adapter *bna =
+		(struct netmap_bwrap_adapter *)na;
+	struct netmap_adapter *hwna = bna->hwna;
+	enum txrx t;
+	int i;
+
+	nm_prdis("%s", na->name);
+
+	/* decrement the usage counter for all the hwna krings */
+	for_rx_tx(t) {
+		for (i = 0; i < netmap_all_rings(hwna, t); i++) {
+			NMR(hwna, t)[i]->users--;
+			NMR(na, t)[i]->users--;
+		}
+	}
+
+	/* delete any netmap rings that are no longer needed */
+	netmap_mem_rings_delete(hwna);
+	hwna->nm_krings_delete(hwna);
+}
+
+
+/* notify method for the bridge-->hwna direction */
+int
+netmap_bwrap_notify(struct netmap_kring *kring, int flags)
+{
+	struct netmap_adapter *na = kring->na;
+	struct netmap_bwrap_adapter *bna = na->na_private;
+	struct netmap_adapter *hwna = bna->hwna;
+	u_int ring_n = kring->ring_id;
+	u_int lim = kring->nkr_num_slots - 1;
+	struct netmap_kring *hw_kring;
+	int error;
+
+	nm_prdis("%s: na %s hwna %s",
+			(kring ? kring->name : "NULL!"),
+			(na ? na->name : "NULL!"),
+			(hwna ? hwna->name : "NULL!"));
+	hw_kring = hwna->tx_rings[ring_n];
+
+	if (nm_kr_tryget(hw_kring, 0, NULL)) {
+		return ENXIO;
+	}
+
+	/* first step: simulate a user wakeup on the rx ring */
+	netmap_vp_rxsync(kring, flags);
+	nm_prdis("%s[%d] PRE rx(c%3d t%3d l%3d) ring(h%3d c%3d t%3d) tx(c%3d ht%3d t%3d)",
+		na->name, ring_n,
+		kring->nr_hwcur, kring->nr_hwtail, kring->nkr_hwlease,
+		kring->rhead, kring->rcur, kring->rtail,
+		hw_kring->nr_hwcur, hw_kring->nr_hwtail, hw_kring->rtail);
+	/* second step: the new packets are sent on the tx ring
+	 * (which is actually the same ring)
+	 */
+	hw_kring->rhead = hw_kring->rcur = kring->nr_hwtail;
+	error = hw_kring->nm_sync(hw_kring, flags);
+	if (error)
+		goto put_out;
+
+	/* third step: now we are back the rx ring */
+	/* claim ownership on all hw owned bufs */
+	kring->rhead = kring->rcur = nm_next(hw_kring->nr_hwtail, lim); /* skip past reserved slot */
+
+	/* fourth step: the user goes to sleep again, causing another rxsync */
+	netmap_vp_rxsync(kring, flags);
+	nm_prdis("%s[%d] PST rx(c%3d t%3d l%3d) ring(h%3d c%3d t%3d) tx(c%3d ht%3d t%3d)",
+		na->name, ring_n,
+		kring->nr_hwcur, kring->nr_hwtail, kring->nkr_hwlease,
+		kring->rhead, kring->rcur, kring->rtail,
+		hw_kring->nr_hwcur, hw_kring->nr_hwtail, hw_kring->rtail);
+put_out:
+	nm_kr_put(hw_kring);
+
+	return error ? error : NM_IRQ_COMPLETED;
+}
+
+
+/* nm_bdg_ctl callback for the bwrap.
+ * Called on bridge-attach and detach, as an effect of vale-ctl -[ahd].
+ * On attach, it needs to provide a fake netmap_priv_d structure and
+ * perform a netmap_do_regif() on the bwrap. This will put both the
+ * bwrap and the hwna in netmap mode, with the netmap rings shared
+ * and cross linked. Moroever, it will start intercepting interrupts
+ * directed to hwna.
+ */
+static int
+netmap_bwrap_bdg_ctl(struct nmreq_header *hdr, struct netmap_adapter *na)
+{
+	struct netmap_priv_d *npriv;
+	struct netmap_bwrap_adapter *bna = (struct netmap_bwrap_adapter*)na;
+	int error = 0;
+
+	if (hdr->nr_reqtype == NETMAP_REQ_VALE_ATTACH) {
+		struct nmreq_vale_attach *req =
+			(struct nmreq_vale_attach *)(uintptr_t)hdr->nr_body;
+		if (req->reg.nr_ringid != 0 ||
+			(req->reg.nr_mode != NR_REG_ALL_NIC &&
+				req->reg.nr_mode != NR_REG_NIC_SW)) {
+			/* We only support attaching all the NIC rings
+			 * and/or the host stack. */
+			return EINVAL;
+		}
+		if (NETMAP_OWNED_BY_ANY(na)) {
+			return EBUSY;
+		}
+		if (bna->na_kpriv) {
+			/* nothing to do */
+			return 0;
+		}
+		npriv = netmap_priv_new();
+		if (npriv == NULL)
+			return ENOMEM;
+		npriv->np_ifp = na->ifp; /* let the priv destructor release the ref */
+		error = netmap_do_regif(npriv, na, hdr);
+		if (error) {
+			netmap_priv_delete(npriv);
+			netmap_mem_restore(bna->hwna);
+			return error;
+		}
+		bna->na_kpriv = npriv;
+		na->na_flags |= NAF_BUSY;
+	} else {
+		if (na->active_fds == 0) /* not registered */
+			return EINVAL;
+		netmap_priv_delete(bna->na_kpriv);
+		bna->na_kpriv = NULL;
+		na->na_flags &= ~NAF_BUSY;
+		netmap_mem_restore(bna->hwna);
+	}
+
+	return error;
+}
+
+/* attach a bridge wrapper to the 'real' device */
+int
+netmap_bwrap_attach_common(struct netmap_adapter *na,
+		struct netmap_adapter *hwna)
+{
+	struct netmap_bwrap_adapter *bna;
+	struct netmap_adapter *hostna = NULL;
+	int error = 0;
+	enum txrx t;
+
+	/* make sure the NIC is not already in use */
+	if (NETMAP_OWNED_BY_ANY(hwna)) {
+		nm_prerr("NIC %s busy, cannot attach to bridge", hwna->name);
+		return EBUSY;
+	}
+
+	bna = (struct netmap_bwrap_adapter *)na;
+	/* make bwrap ifp point to the real ifp */
+	na->ifp = hwna->ifp;
+	if_ref(na->ifp);
+	na->na_private = bna;
+	/* fill the ring data for the bwrap adapter with rx/tx meanings
+	 * swapped. The real cross-linking will be done during register,
+	 * when all the krings will have been created.
+	 */
+	for_rx_tx(t) {
+		enum txrx r = nm_txrx_swap(t); /* swap NR_TX <-> NR_RX */
+		nma_set_nrings(na, t, nma_get_nrings(hwna, r));
+		nma_set_ndesc(na, t, nma_get_ndesc(hwna, r));
+	}
+	na->nm_dtor = netmap_bwrap_dtor;
+	na->nm_config = netmap_bwrap_config;
+	na->nm_bufcfg = netmap_bwrap_bufcfg;
+	na->nm_bdg_ctl = netmap_bwrap_bdg_ctl;
+	na->pdev = hwna->pdev;
+	na->nm_mem = netmap_mem_get(hwna->nm_mem);
+	na->virt_hdr_len = hwna->virt_hdr_len;
+	na->rx_buf_maxsize = hwna->rx_buf_maxsize;
+
+	bna->hwna = hwna;
+	netmap_adapter_get(hwna);
+	hwna->na_private = bna; /* weak reference */
+	bna->saved_na_vp = hwna->na_vp;
+	hwna->na_vp = &bna->up;
+	bna->up.up.na_vp = &(bna->up);
+
+	if (hwna->na_flags & NAF_HOST_RINGS) {
+		if (hwna->na_flags & NAF_SW_ONLY)
+			na->na_flags |= NAF_SW_ONLY;
+		na->na_flags |= NAF_HOST_RINGS;
+		hostna = &bna->host.up;
+
+		snprintf(hostna->name, sizeof(hostna->name), "%s^", na->name);
+		hostna->ifp = hwna->ifp;
+		// hostna->nm_txsync = netmap_bwrap_host_txsync;
+		// hostna->nm_rxsync = netmap_bwrap_host_rxsync;
+		hostna->nm_mem = netmap_mem_get(na->nm_mem);
+		hostna->na_private = bna;
+		hostna->na_vp = &bna->up;
+		na->na_hostvp = hwna->na_hostvp =
+			hostna->na_hostvp = &bna->host;
+		hostna->na_flags = NAF_BUSY; /* prevent NIOCREGIF */
+		hostna->rx_buf_maxsize = hwna->rx_buf_maxsize;
+		/* bwrap_config() will determine the number of host rings */
+	}
+	if (hwna->na_flags & NAF_MOREFRAG)
+		na->na_flags |= NAF_MOREFRAG;
+
+	nm_prdis("%s<->%s txr %d txd %d rxr %d rxd %d",
+		na->name, if_name(ifp),
+		na->num_tx_rings, na->num_tx_desc,
+		na->num_rx_rings, na->num_rx_desc);
+
+	error = netmap_attach_common(na);
+	if (error) {
+		goto err_put;
+	}
+	hwna->na_flags |= NAF_BUSY;
+	return 0;
+
+err_put:
+	hwna->na_vp = hwna->na_hostvp = NULL;
+	netmap_adapter_put(hwna);
+	return error;
+
+}
+
+struct nm_bridge *
+netmap_init_bridges2(u_int n)
+{
+	int i;
+	struct nm_bridge *b;
+
+	b = nm_os_malloc(sizeof(struct nm_bridge) * n);
+	if (b == NULL)
+		return NULL;
+	for (i = 0; i < n; i++)
+		BDG_RWINIT(&b[i]);
+	return b;
+}
+
+void
+netmap_uninit_bridges2(struct nm_bridge *b, u_int n)
+{
+	int i;
+
+	if (b == NULL)
+		return;
+
+	for (i = 0; i < n; i++)
+		BDG_RWDESTROY(&b[i]);
+	nm_os_free(b);
+}
+
+int
+netmap_init_bridges(void)
+{
+#ifdef CONFIG_NET_NS
+	return netmap_bns_register();
+#else
+	nm_bridges = netmap_init_bridges2(vale_max_bridges);
+	if (nm_bridges == NULL)
+		return ENOMEM;
+	return 0;
+#endif
+}
+
+void
+netmap_uninit_bridges(void)
+{
+#ifdef CONFIG_NET_NS
+	netmap_bns_unregister();
+#else
+	netmap_uninit_bridges2(nm_bridges, vale_max_bridges);
+#endif
+}
diff --git a/sys/dev/netmap/netmap_bdg.h b/sys/dev/netmap/netmap_bdg.h
new file mode 100644
index 000000000..ca00e3ce3
--- /dev/null
+++ b/sys/dev/netmap/netmap_bdg.h
@@ -0,0 +1,196 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2013-2018 Universita` di Pisa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef _NET_NETMAP_BDG_H_
+#define _NET_NETMAP_BDG_H_
+
+#if defined(__FreeBSD__)
+#define BDG_RWLOCK_T		struct rwlock // struct rwlock
+
+#define	BDG_RWINIT(b)		\
+	rw_init_flags(&(b)->bdg_lock, "bdg lock", RW_NOWITNESS)
+#define BDG_WLOCK(b)		rw_wlock(&(b)->bdg_lock)
+#define BDG_WUNLOCK(b)		rw_wunlock(&(b)->bdg_lock)
+#define BDG_RLOCK(b)		rw_rlock(&(b)->bdg_lock)
+#define BDG_RTRYLOCK(b)		rw_try_rlock(&(b)->bdg_lock)
+#define BDG_RUNLOCK(b)		rw_runlock(&(b)->bdg_lock)
+#define BDG_RWDESTROY(b)	rw_destroy(&(b)->bdg_lock)
+
+#endif /* __FreeBSD__ */
+
+/*
+ * The following bridge-related functions are used by other
+ * kernel modules.
+ *
+ * VALE only supports unicast or broadcast. The lookup
+ * function can return 0 .. NM_BDG_MAXPORTS-1 for regular ports,
+ * NM_BDG_MAXPORTS for broadcast, NM_BDG_MAXPORTS+1 to indicate
+ * drop.
+ */
+typedef uint32_t (*bdg_lookup_fn_t)(struct nm_bdg_fwd *ft, uint8_t *ring_nr,
+		struct netmap_vp_adapter *, void *private_data);
+typedef int (*bdg_config_fn_t)(struct nm_ifreq *);
+typedef void (*bdg_dtor_fn_t)(const struct netmap_vp_adapter *);
+typedef void *(*bdg_update_private_data_fn_t)(void *private_data, void *callback_data, int *error);
+typedef int (*bdg_vp_create_fn_t)(struct nmreq_header *hdr,
+		if_t ifp, struct netmap_mem_d *nmd,
+		struct netmap_vp_adapter **ret);
+typedef int (*bdg_bwrap_attach_fn_t)(const char *nr_name, struct netmap_adapter *hwna);
+struct netmap_bdg_ops {
+	bdg_lookup_fn_t lookup;
+	bdg_config_fn_t config;
+	bdg_dtor_fn_t	dtor;
+	bdg_vp_create_fn_t	vp_create;
+	bdg_bwrap_attach_fn_t	bwrap_attach;
+	char name[IFNAMSIZ];
+};
+int netmap_bwrap_attach(const char *name, struct netmap_adapter *, struct netmap_bdg_ops *);
+int netmap_bdg_regops(const char *name, struct netmap_bdg_ops *bdg_ops, void *private_data, void *auth_token);
+
+#define	NM_BRIDGES		8	/* number of bridges */
+#define	NM_BDG_MAXPORTS		254	/* up to 254 */
+#define	NM_BDG_BROADCAST	NM_BDG_MAXPORTS
+#define	NM_BDG_NOPORT		(NM_BDG_MAXPORTS+1)
+
+/* XXX Should go away after fixing find_bridge() - Michio */
+#define NM_BDG_HASH		1024	/* forwarding table entries */
+
+/* XXX revise this */
+struct nm_hash_ent {
+	uint64_t	mac;	/* the top 2 bytes are the epoch */
+	uint64_t	ports;
+};
+
+/* Default size for the Maximum Frame Size. */
+#define NM_BDG_MFS_DEFAULT	1514
+
+/*
+ * nm_bridge is a descriptor for a VALE switch.
+ * Interfaces for a bridge are all in bdg_ports[].
+ * The array has fixed size, an empty entry does not terminate
+ * the search, but lookups only occur on attach/detach so we
+ * don't mind if they are slow.
+ *
+ * The bridge is non blocking on the transmit ports: excess
+ * packets are dropped if there is no room on the output port.
+ *
+ * bdg_lock protects accesses to the bdg_ports array.
+ * This is a rw lock (or equivalent).
+ */
+#define NM_BDG_IFNAMSIZ IFNAMSIZ
+struct nm_bridge {
+	/* XXX what is the proper alignment/layout ? */
+	BDG_RWLOCK_T	bdg_lock;	/* protects bdg_ports */
+	int		bdg_namelen;
+	uint32_t	bdg_active_ports;
+	char		bdg_basename[NM_BDG_IFNAMSIZ];
+
+	/* Indexes of active ports (up to active_ports)
+	 * and all other remaining ports.
+	 */
+	uint32_t	bdg_port_index[NM_BDG_MAXPORTS];
+	/* used by netmap_bdg_detach_common() */
+	uint32_t	tmp_bdg_port_index[NM_BDG_MAXPORTS];
+
+	struct netmap_vp_adapter *bdg_ports[NM_BDG_MAXPORTS];
+
+	/*
+	 * Programmable lookup functions to figure out the destination port.
+	 * It returns either of an index of the destination port,
+	 * NM_BDG_BROADCAST to broadcast this packet, or NM_BDG_NOPORT not to
+	 * forward this packet.  ring_nr is the source ring index, and the
+	 * function may overwrite this value to forward this packet to a
+	 * different ring index.
+	 * The function is set by netmap_bdg_regops().
+	 */
+	struct netmap_bdg_ops bdg_ops;
+	struct netmap_bdg_ops bdg_saved_ops;
+
+	/*
+	 * Contains the data structure used by the bdg_ops.lookup function.
+	 * By default points to *ht which is allocated on attach and used by the default lookup
+	 * otherwise will point to the data structure received by netmap_bdg_regops().
+	 */
+	void *private_data;
+	struct nm_hash_ent *ht;
+
+	/* Currently used to specify if the bridge is still in use while empty and
+	 * if it has been put in exclusive mode by an external module, see netmap_bdg_regops()
+	 * and netmap_bdg_create().
+	 */
+#define NM_BDG_ACTIVE		1
+#define NM_BDG_EXCLUSIVE	2
+#define NM_BDG_NEED_BWRAP	4
+	uint8_t			bdg_flags;
+
+
+#ifdef CONFIG_NET_NS
+	struct net *ns;
+#endif /* CONFIG_NET_NS */
+};
+
+static inline void *
+nm_bdg_get_auth_token(struct nm_bridge *b)
+{
+	return b->ht;
+}
+
+/* bridge not in exclusive mode ==> always valid
+ * bridge in exclusive mode (created through netmap_bdg_create()) ==> check authentication token
+ */
+static inline int
+nm_bdg_valid_auth_token(struct nm_bridge *b, void *auth_token)
+{
+	return !(b->bdg_flags & NM_BDG_EXCLUSIVE) || b->ht == auth_token;
+}
+
+int netmap_get_bdg_na(struct nmreq_header *hdr, struct netmap_adapter **na,
+	struct netmap_mem_d *nmd, int create, struct netmap_bdg_ops *ops);
+
+struct nm_bridge *nm_find_bridge(const char *name, int create, struct netmap_bdg_ops *ops);
+int netmap_bdg_free(struct nm_bridge *b);
+void netmap_bdg_detach_common(struct nm_bridge *b, int hw, int sw);
+int netmap_vp_bdg_ctl(struct nmreq_header *hdr, struct netmap_adapter *na);
+int netmap_bwrap_reg(struct netmap_adapter *, int onoff);
+int netmap_bdg_detach_locked(struct nmreq_header *hdr, void *auth_token);
+int netmap_vp_reg(struct netmap_adapter *na, int onoff);
+int netmap_vp_rxsync(struct netmap_kring *kring, int flags);
+int netmap_bwrap_intr_notify(struct netmap_kring *kring, int flags);
+int netmap_bwrap_notify(struct netmap_kring *kring, int flags);
+int netmap_bwrap_attach_common(struct netmap_adapter *na,
+		struct netmap_adapter *hwna);
+int netmap_bwrap_krings_create_common(struct netmap_adapter *na);
+void netmap_bwrap_krings_delete_common(struct netmap_adapter *na);
+struct nm_bridge *netmap_init_bridges2(u_int);
+void netmap_uninit_bridges2(struct nm_bridge *, u_int);
+int netmap_bdg_update_private_data(const char *name, bdg_update_private_data_fn_t callback,
+	void *callback_data, void *auth_token);
+int netmap_bdg_config(struct nm_ifreq *nifr);
+
+#define NM_NEED_BWRAP (-2)
+#endif /* _NET_NETMAP_BDG_H_ */
+
diff --git a/sys/dev/netmap/netmap_freebsd.c b/sys/dev/netmap/netmap_freebsd.c
index 047e990d4..6eb707a28 100644
--- a/sys/dev/netmap/netmap_freebsd.c
+++ b/sys/dev/netmap/netmap_freebsd.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2013-2014 Universita` di Pisa. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -23,13 +25,13 @@
  * SUCH DAMAGE.
  */
 
-/* $FreeBSD: head/sys/dev/netmap/netmap_freebsd.c 307706 2016-10-21 06:32:45Z sephe $ */
 #include "opt_inet.h"
 #include "opt_inet6.h"
 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include   /* POLLIN, POLLOUT */
 #include  /* types used in module initialization */
@@ -56,6 +58,7 @@
 #include  /* RFNOWAIT */
 #include  /* sched_bind() */
 #include  /* mp_maxid */
+#include  /* taskqueue_enqueue(), taskqueue_create(), ... */
 #include 
 #include 
 #include  /* IFT_ETHER */
@@ -73,17 +76,50 @@
 
 /* ======================== FREEBSD-SPECIFIC ROUTINES ================== */
 
-void nm_os_selinfo_init(NM_SELINFO_T *si) {
-	struct mtx *m = &si->m;
-	mtx_init(m, "nm_kn_lock", NULL, MTX_DEF);
-	knlist_init_mtx(&si->si.si_note, m);
+static void
+nm_kqueue_notify(void *opaque, int pending)
+{
+	struct nm_selinfo *si = opaque;
+
+	/* We use a non-zero hint to distinguish this notification call
+	 * from the call done in kqueue_scan(), which uses hint=0.
+	 */
+	KNOTE_UNLOCKED(&si->si.si_note, /*hint=*/0x100);
+}
+
+int nm_os_selinfo_init(NM_SELINFO_T *si, const char *name) {
+	int err;
+
+	TASK_INIT(&si->ntfytask, 0, nm_kqueue_notify, si);
+	si->ntfytq = taskqueue_create(name, M_NOWAIT,
+	    taskqueue_thread_enqueue, &si->ntfytq);
+	if (si->ntfytq == NULL)
+		return -ENOMEM;
+	err = taskqueue_start_threads(&si->ntfytq, 1, PI_NET, "tq %s", name);
+	if (err) {
+		taskqueue_free(si->ntfytq);
+		si->ntfytq = NULL;
+		return err;
+	}
+
+	snprintf(si->mtxname, sizeof(si->mtxname), "nmkl%s", name);
+	mtx_init(&si->m, si->mtxname, NULL, MTX_DEF);
+	knlist_init_mtx(&si->si.si_note, &si->m);
+	si->kqueue_users = 0;
+
+	return (0);
 }
 
 void
 nm_os_selinfo_uninit(NM_SELINFO_T *si)
 {
-	/* XXX kqueue(9) needed; these will mirror knlist_init. */
-	knlist_delete(&si->si.si_note, curthread, 0 /* not locked */ );
+	if (si->ntfytq == NULL) {
+		return;	/* si was not initialized */
+	}
+	taskqueue_drain(si->ntfytq, &si->ntfytask);
+	taskqueue_free(si->ntfytq);
+	si->ntfytq = NULL;
+	knlist_delete(&si->si.si_note, curthread, /*islocked=*/0);
 	knlist_destroy(&si->si.si_note);
 	/* now we don't need the mutex anymore */
 	mtx_destroy(&si->m);
@@ -134,15 +170,15 @@ nm_os_put_module(void)
 }
 
 static void
-netmap_ifnet_arrival_handler(void *arg __unused, struct ifnet *ifp)
+netmap_ifnet_arrival_handler(void *arg __unused, if_t ifp)
 {
-        netmap_undo_zombie(ifp);
+	netmap_undo_zombie(ifp);
 }
 
 static void
-netmap_ifnet_departure_handler(void *arg __unused, struct ifnet *ifp)
+netmap_ifnet_departure_handler(void *arg __unused, if_t ifp)
 {
-        netmap_make_zombie(ifp);
+	netmap_make_zombie(ifp);
 }
 
 static eventhandler_tag nm_ifnet_ah_tag;
@@ -151,24 +187,30 @@ static eventhandler_tag nm_ifnet_dh_tag;
 int
 nm_os_ifnet_init(void)
 {
-        nm_ifnet_ah_tag =
-                EVENTHANDLER_REGISTER(ifnet_arrival_event,
-                        netmap_ifnet_arrival_handler,
-                        NULL, EVENTHANDLER_PRI_ANY);
-        nm_ifnet_dh_tag =
-                EVENTHANDLER_REGISTER(ifnet_departure_event,
-                        netmap_ifnet_departure_handler,
-                        NULL, EVENTHANDLER_PRI_ANY);
-        return 0;
+	nm_ifnet_ah_tag =
+		EVENTHANDLER_REGISTER(ifnet_arrival_event,
+				netmap_ifnet_arrival_handler,
+				NULL, EVENTHANDLER_PRI_ANY);
+	nm_ifnet_dh_tag =
+		EVENTHANDLER_REGISTER(ifnet_departure_event,
+				netmap_ifnet_departure_handler,
+				NULL, EVENTHANDLER_PRI_ANY);
+	return 0;
 }
 
 void
 nm_os_ifnet_fini(void)
 {
-        EVENTHANDLER_DEREGISTER(ifnet_arrival_event,
-                nm_ifnet_ah_tag);
-        EVENTHANDLER_DEREGISTER(ifnet_departure_event,
-                nm_ifnet_dh_tag);
+	EVENTHANDLER_DEREGISTER(ifnet_arrival_event,
+			nm_ifnet_ah_tag);
+	EVENTHANDLER_DEREGISTER(ifnet_departure_event,
+			nm_ifnet_dh_tag);
+}
+
+unsigned
+nm_os_ifnet_mtu(if_t ifp)
+{
+	return if_getmtu(ifp);
 }
 
 rawsum_t
@@ -217,7 +259,7 @@ nm_os_csum_tcpudp_ipv4(struct nm_iphdr *iph, void *data,
 #ifdef INET
 	uint16_t pseudolen = datalen + iph->protocol;
 
-	/* Compute and insert the pseudo-header cheksum. */
+	/* Compute and insert the pseudo-header checksum. */
 	*check = in_pseudo(iph->saddr, iph->daddr,
 				 htobe16(pseudolen));
 	/* Compute the checksum on TCP/UDP header + payload
@@ -228,7 +270,7 @@ nm_os_csum_tcpudp_ipv4(struct nm_iphdr *iph, void *data,
 	static int notsupported = 0;
 	if (!notsupported) {
 		notsupported = 1;
-		D("inet4 segmentation not supported");
+		nm_prerr("inet4 segmentation not supported");
 	}
 #endif
 }
@@ -244,36 +286,47 @@ nm_os_csum_tcpudp_ipv6(struct nm_ipv6hdr *ip6h, void *data,
 	static int notsupported = 0;
 	if (!notsupported) {
 		notsupported = 1;
-		D("inet6 segmentation not supported");
+		nm_prerr("inet6 segmentation not supported");
 	}
 #endif
 }
 
 /* on FreeBSD we send up one packet at a time */
 void *
-nm_os_send_up(struct ifnet *ifp, struct mbuf *m, struct mbuf *prev)
+nm_os_send_up(if_t ifp, struct mbuf *m, struct mbuf *prev)
 {
 	NA(ifp)->if_input(ifp, m);
 	return NULL;
 }
 
 int
-nm_os_mbuf_has_offld(struct mbuf *m)
+nm_os_mbuf_has_csum_offld(struct mbuf *m)
 {
 	return m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP | CSUM_SCTP |
 					 CSUM_TCP_IPV6 | CSUM_UDP_IPV6 |
-					 CSUM_SCTP_IPV6 | CSUM_TSO);
+					 CSUM_SCTP_IPV6);
+}
+
+int
+nm_os_mbuf_has_seg_offld(struct mbuf *m)
+{
+	return m->m_pkthdr.csum_flags & CSUM_TSO;
 }
 
 static void
-freebsd_generic_rx_handler(struct ifnet *ifp, struct mbuf *m)
+freebsd_generic_rx_handler(if_t ifp, struct mbuf *m)
 {
-	struct netmap_generic_adapter *gna =
-			(struct netmap_generic_adapter *)NA(ifp);
-	int stolen = generic_rx_handler(ifp, m);
+	int stolen;
+
+	if (unlikely(!NM_NA_VALID(ifp))) {
+		nm_prlim(1, "Warning: RX packet intercepted, but no"
+				" emulated adapter");
+		return;
+	}
 
+	stolen = generic_rx_handler(ifp, m);
 	if (!stolen) {
-		gna->save_if_input(ifp, m);
+		NA(ifp)->if_input(ifp, m);
 	}
 }
 
@@ -285,25 +338,20 @@ int
 nm_os_catch_rx(struct netmap_generic_adapter *gna, int intercept)
 {
 	struct netmap_adapter *na = &gna->up.up;
-	struct ifnet *ifp = na->ifp;
+	if_t ifp = na->ifp;
+	int ret = 0;
 
+	nm_os_ifnet_lock();
 	if (intercept) {
-		if (gna->save_if_input) {
-			D("cannot intercept again");
-			return EINVAL; /* already set */
-		}
-		gna->save_if_input = ifp->if_input;
-		ifp->if_input = freebsd_generic_rx_handler;
+		if_setcapenablebit(ifp, IFCAP_NETMAP, 0);
+		if_setinputfn(ifp, freebsd_generic_rx_handler);
 	} else {
-		if (!gna->save_if_input){
-			D("cannot restore");
-			return EINVAL;  /* not saved */
-		}
-		ifp->if_input = gna->save_if_input;
-		gna->save_if_input = NULL;
+		if_setcapenablebit(ifp, 0, IFCAP_NETMAP);
+		if_setinputfn(ifp, na->if_input);
 	}
+	nm_os_ifnet_unlock();
 
-	return 0;
+	return ret;
 }
 
 
@@ -317,14 +365,16 @@ int
 nm_os_catch_tx(struct netmap_generic_adapter *gna, int intercept)
 {
 	struct netmap_adapter *na = &gna->up.up;
-	struct ifnet *ifp = netmap_generic_getifp(gna);
+	if_t ifp = netmap_generic_getifp(gna);
 
+	nm_os_ifnet_lock();
 	if (intercept) {
-		na->if_transmit = ifp->if_transmit;
-		ifp->if_transmit = netmap_transmit;
+		na->if_transmit = if_gettransmitfn(ifp);
+		if_settransmitfn(ifp, netmap_transmit);
 	} else {
-		ifp->if_transmit = na->if_transmit;
+		if_settransmitfn(ifp, na->if_transmit);
 	}
+	nm_os_ifnet_unlock();
 
 	return 0;
 }
@@ -351,30 +401,15 @@ nm_os_generic_xmit_frame(struct nm_os_gen_arg *a)
 {
 	int ret;
 	u_int len = a->len;
-	struct ifnet *ifp = a->ifp;
+	if_t ifp = a->ifp;
 	struct mbuf *m = a->m;
 
-#if __FreeBSD_version < 1100000
-	/*
-	 * Old FreeBSD versions. The mbuf has a cluster attached,
-	 * we need to copy from the cluster to the netmap buffer.
-	 */
-	if (MBUF_REFCNT(m) != 1) {
-		D("invalid refcnt %d for %p", MBUF_REFCNT(m), m);
-		panic("in generic_xmit_frame");
-	}
-	if (m->m_ext.ext_size < len) {
-		RD(5, "size %d < len %d", m->m_ext.ext_size, len);
-		len = m->m_ext.ext_size;
-	}
-	bcopy(a->addr, m->m_data, len);
-#else  /* __FreeBSD_version >= 1100000 */
-	/* New FreeBSD versions. Link the external storage to
+	/* Link the external storage to
 	 * the netmap buffer, so that no copy is necessary. */
 	m->m_ext.ext_buf = m->m_data = a->addr;
 	m->m_ext.ext_size = len;
-#endif /* __FreeBSD_version >= 1100000 */
 
+	m->m_flags |= M_PKTHDR;
 	m->m_len = m->m_pkthdr.len = len;
 
 	/* mbuf refcnt is not contended, no need to use atomic
@@ -383,32 +418,32 @@ nm_os_generic_xmit_frame(struct nm_os_gen_arg *a)
 	M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE);
 	m->m_pkthdr.flowid = a->ring_nr;
 	m->m_pkthdr.rcvif = ifp; /* used for tx notification */
+	CURVNET_SET(if_getvnet(ifp));
 	ret = NA(ifp)->if_transmit(ifp, m);
+	CURVNET_RESTORE();
 	return ret ? -1 : 0;
 }
 
 
-#if __FreeBSD_version >= 1100005
 struct netmap_adapter *
 netmap_getna(if_t ifp)
 {
-	return (NA((struct ifnet *)ifp));
+	return (NA(ifp));
 }
-#endif /* __FreeBSD_version >= 1100005 */
 
 /*
  * The following two functions are empty until we have a generic
  * way to extract the info from the ifp
  */
 int
-nm_os_generic_find_num_desc(struct ifnet *ifp, unsigned int *tx, unsigned int *rx)
+nm_os_generic_find_num_desc(if_t ifp, unsigned int *tx, unsigned int *rx)
 {
 	return 0;
 }
 
 
 void
-nm_os_generic_find_num_queues(struct ifnet *ifp, u_int *txq, u_int *rxq)
+nm_os_generic_find_num_queues(if_t ifp, u_int *txq, u_int *rxq)
 {
 	unsigned num_rings = netmap_generic_rings ? netmap_generic_rings : 1;
 
@@ -427,7 +462,6 @@ nm_os_generic_set_features(struct netmap_generic_adapter *gna)
 void
 nm_os_mitigation_init(struct nm_generic_mit *mit, int idx, struct netmap_adapter *na)
 {
-	ND("called");
 	mit->mit_pending = 0;
 	mit->mit_ring_idx = idx;
 	mit->mit_na = na;
@@ -437,21 +471,19 @@ nm_os_mitigation_init(struct nm_generic_mit *mit, int idx, struct netmap_adapter
 void
 nm_os_mitigation_start(struct nm_generic_mit *mit)
 {
-	ND("called");
 }
 
 
 void
 nm_os_mitigation_restart(struct nm_generic_mit *mit)
 {
-	ND("called");
 }
 
 
 int
 nm_os_mitigation_active(struct nm_generic_mit *mit)
 {
-	ND("called");
+
 	return 0;
 }
 
@@ -459,17 +491,17 @@ nm_os_mitigation_active(struct nm_generic_mit *mit)
 void
 nm_os_mitigation_cleanup(struct nm_generic_mit *mit)
 {
-	ND("called");
 }
 
 static int
-nm_vi_dummy(struct ifnet *ifp, u_long cmd, caddr_t addr)
+nm_vi_dummy(if_t ifp, u_long cmd, caddr_t addr)
 {
+
 	return EINVAL;
 }
 
 static void
-nm_vi_start(struct ifnet *ifp)
+nm_vi_start(if_t ifp)
 {
 	panic("nm_vi_start() must not be called");
 }
@@ -527,7 +559,7 @@ nm_vi_free_index(uint8_t val)
 		}
 	}
 	if (lim == nm_vi_indices.active)
-		D("funny, index %u didn't found", val);
+		nm_prerr("Index %u not found", val);
 	mtx_unlock(&nm_vi_indices.lock);
 }
 #undef NM_VI_MAX
@@ -543,9 +575,9 @@ nm_vi_free_index(uint8_t val)
  * increment this refcount on if_attach().
  */
 int
-nm_os_vi_persist(const char *name, struct ifnet **ret)
+nm_os_vi_persist(const char *name, if_t *ret)
 {
-	struct ifnet *ifp;
+	if_t ifp;
 	u_short macaddr_hi;
 	uint32_t macaddr_mid;
 	u_char eaddr[6];
@@ -565,19 +597,18 @@ nm_os_vi_persist(const char *name, struct ifnet **ret)
 
 	ifp = if_alloc(IFT_ETHER);
 	if (ifp == NULL) {
-		D("if_alloc failed");
+		nm_prerr("if_alloc failed");
 		return ENOMEM;
 	}
 	if_initname(ifp, name, IF_DUNIT_NONE);
-	ifp->if_mtu = 65536;
-	ifp->if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST;
-	ifp->if_init = (void *)nm_vi_dummy;
-	ifp->if_ioctl = nm_vi_dummy;
-	ifp->if_start = nm_vi_start;
-	ifp->if_mtu = ETHERMTU;
-	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
-	ifp->if_capabilities |= IFCAP_LINKSTATE;
-	ifp->if_capenable |= IFCAP_LINKSTATE;
+	if_setflags(ifp, IFF_UP | IFF_SIMPLEX | IFF_MULTICAST);
+	if_setinitfn(ifp, (void *)nm_vi_dummy);
+	if_setioctlfn(ifp, nm_vi_dummy);
+	if_setstartfn(ifp, nm_vi_start);
+	if_setmtu(ifp, ETHERMTU);
+	if_setsendqlen(ifp, ifqmaxlen);
+	if_setcapabilitiesbit(ifp, IFCAP_LINKSTATE, 0);
+	if_setcapenablebit(ifp, IFCAP_LINKSTATE, 0);
 
 	ether_ifattach(ifp, eaddr);
 	*ret = ifp;
@@ -586,16 +617,130 @@ nm_os_vi_persist(const char *name, struct ifnet **ret)
 
 /* unregister from the system and drop the final refcount */
 void
-nm_os_vi_detach(struct ifnet *ifp)
+nm_os_vi_detach(if_t ifp)
 {
-	nm_vi_free_index(((char *)IF_LLADDR(ifp))[5]);
+	nm_vi_free_index(((char *)if_getlladdr(ifp))[5]);
 	ether_ifdetach(ifp);
 	if_free(ifp);
 }
 
-/* ======================== PTNETMAP SUPPORT ========================== */
+#ifdef WITH_EXTMEM
+#include 
+#include 
+#include 
+struct nm_os_extmem {
+	vm_object_t obj;
+	vm_offset_t kva;
+	vm_offset_t size;
+	uintptr_t scan;
+};
+
+void
+nm_os_extmem_delete(struct nm_os_extmem *e)
+{
+	nm_prinf("freeing %zx bytes", (size_t)e->size);
+	vm_map_remove(kernel_map, e->kva, e->kva + e->size);
+	nm_os_free(e);
+}
+
+char *
+nm_os_extmem_nextpage(struct nm_os_extmem *e)
+{
+	char *rv = NULL;
+	if (e->scan < e->kva + e->size) {
+		rv = (char *)e->scan;
+		e->scan += PAGE_SIZE;
+	}
+	return rv;
+}
+
+int
+nm_os_extmem_isequal(struct nm_os_extmem *e1, struct nm_os_extmem *e2)
+{
+	return (e1->obj == e2->obj);
+}
+
+int
+nm_os_extmem_nr_pages(struct nm_os_extmem *e)
+{
+	return e->size >> PAGE_SHIFT;
+}
+
+struct nm_os_extmem *
+nm_os_extmem_create(unsigned long p, struct nmreq_pools_info *pi, int *perror)
+{
+	vm_map_t map;
+	vm_map_entry_t entry;
+	vm_object_t obj;
+	vm_prot_t prot;
+	vm_pindex_t index;
+	boolean_t wired;
+	struct nm_os_extmem *e = NULL;
+	int rv, error = 0;
+
+	e = nm_os_malloc(sizeof(*e));
+	if (e == NULL) {
+		error = ENOMEM;
+		goto out;
+	}
+
+	map = &curthread->td_proc->p_vmspace->vm_map;
+	rv = vm_map_lookup(&map, p, VM_PROT_RW, &entry,
+			&obj, &index, &prot, &wired);
+	if (rv != KERN_SUCCESS) {
+		nm_prerr("address %lx not found", p);
+		error = vm_mmap_to_errno(rv);
+		goto out_free;
+	}
+	vm_object_reference(obj);
+
+	/* check that we are given the whole vm_object ? */
+	vm_map_lookup_done(map, entry);
+
+	e->obj = obj;
+	/* Wire the memory and add the vm_object to the kernel map,
+	 * to make sure that it is not freed even if all the processes
+	 * that are mmap()ing should munmap() it.
+	 */
+	e->kva = vm_map_min(kernel_map);
+	e->size = obj->size << PAGE_SHIFT;
+	rv = vm_map_find(kernel_map, obj, 0, &e->kva, e->size, 0,
+			VMFS_OPTIMAL_SPACE, VM_PROT_READ | VM_PROT_WRITE,
+			VM_PROT_READ | VM_PROT_WRITE, 0);
+	if (rv != KERN_SUCCESS) {
+		nm_prerr("vm_map_find(%zx) failed", (size_t)e->size);
+		error = vm_mmap_to_errno(rv);
+		goto out_rel;
+	}
+	rv = vm_map_wire(kernel_map, e->kva, e->kva + e->size,
+			VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES);
+	if (rv != KERN_SUCCESS) {
+		nm_prerr("vm_map_wire failed");
+		error = vm_mmap_to_errno(rv);
+		goto out_rem;
+	}
 
-#ifdef WITH_PTNETMAP_GUEST
+	e->scan = e->kva;
+
+	return e;
+
+out_rem:
+	vm_map_remove(kernel_map, e->kva, e->kva + e->size);
+out_rel:
+	vm_object_deallocate(e->obj);
+	e->obj = NULL;
+out_free:
+	nm_os_free(e);
+out:
+	if (perror)
+		*perror = error;
+	return NULL;
+}
+#endif /* WITH_EXTMEM */
+
+/* ================== PTNETMAP GUEST SUPPORT ==================== */
+
+#ifdef WITH_PTNETMAP
 #include 
 #include 
 #include         /* bus_dmamap_* */
@@ -638,9 +783,8 @@ static driver_t ptn_memdev_driver = {
 
 /* We use (SI_ORDER_MIDDLE+1) here, see DEV_MODULE_ORDERED() invocation
  * below. */
-static devclass_t ptnetmap_devclass;
-DRIVER_MODULE_ORDERED(ptn_memdev, pci, ptn_memdev_driver, ptnetmap_devclass,
-		      NULL, NULL, SI_ORDER_MIDDLE + 1);
+DRIVER_MODULE_ORDERED(ptn_memdev, pci, ptn_memdev_driver, NULL, NULL,
+		      SI_ORDER_MIDDLE + 1);
 
 /*
  * Map host netmap memory through PCI-BAR in the guest OS,
@@ -653,7 +797,7 @@ nm_os_pt_memdev_iomap(struct ptnetmap_memdev *ptn_dev, vm_paddr_t *nm_paddr,
 {
 	int rid;
 
-	D("ptn_memdev_driver iomap");
+	nm_prinf("ptn_memdev_driver iomap");
 
 	rid = PCIR_BAR(PTNETMAP_MEM_PCI_BAR);
 	*mem_size = bus_read_4(ptn_dev->pci_io, PTNET_MDEV_IO_MEMSIZE_HI);
@@ -665,14 +809,14 @@ nm_os_pt_memdev_iomap(struct ptnetmap_memdev *ptn_dev, vm_paddr_t *nm_paddr,
 			&rid, 0, ~0, *mem_size, RF_ACTIVE);
 	if (ptn_dev->pci_mem == NULL) {
 		*nm_paddr = 0;
-		*nm_addr = 0;
+		*nm_addr = NULL;
 		return ENOMEM;
 	}
 
 	*nm_paddr = rman_get_start(ptn_dev->pci_mem);
 	*nm_addr = rman_get_virtual(ptn_dev->pci_mem);
 
-	D("=== BAR %d start %lx len %lx mem_size %lx ===",
+	nm_prinf("=== BAR %d start %lx len %lx mem_size %lx ===",
 			PTNETMAP_MEM_PCI_BAR,
 			(unsigned long)(*nm_paddr),
 			(unsigned long)rman_get_size(ptn_dev->pci_mem),
@@ -690,7 +834,7 @@ nm_os_pt_memdev_ioread(struct ptnetmap_memdev *ptn_dev, unsigned int reg)
 void
 nm_os_pt_memdev_iounmap(struct ptnetmap_memdev *ptn_dev)
 {
-	D("ptn_memdev_driver iounmap");
+	nm_prinf("ptn_memdev_driver iounmap");
 
 	if (ptn_dev->pci_mem) {
 		bus_release_resource(ptn_dev->dev, SYS_RES_MEMORY,
@@ -726,8 +870,6 @@ ptn_memdev_attach(device_t dev)
 	int rid;
 	uint16_t mem_id;
 
-	D("ptn_memdev_driver attach");
-
 	ptn_dev = device_get_softc(dev);
 	ptn_dev->dev = dev;
 
@@ -751,7 +893,7 @@ ptn_memdev_attach(device_t dev)
 	}
 	netmap_mem_get(ptn_dev->nm_mem);
 
-	D("ptn_memdev_driver probe OK - host_mem_id: %d", mem_id);
+	nm_prinf("ptnetmap memdev attached, host memid: %u", mem_id);
 
 	return (0);
 }
@@ -762,10 +904,11 @@ ptn_memdev_detach(device_t dev)
 {
 	struct ptnetmap_memdev *ptn_dev;
 
-	D("ptn_memdev_driver detach");
 	ptn_dev = device_get_softc(dev);
 
 	if (ptn_dev->nm_mem) {
+		nm_prinf("ptnetmap memdev detached, host memid %u",
+			netmap_mem_get_id(ptn_dev->nm_mem));
 		netmap_mem_put(ptn_dev->nm_mem);
 		ptn_dev->nm_mem = NULL;
 	}
@@ -786,11 +929,10 @@ ptn_memdev_detach(device_t dev)
 static int
 ptn_memdev_shutdown(device_t dev)
 {
-	D("ptn_memdev_driver shutdown");
 	return bus_generic_shutdown(dev);
 }
 
-#endif /* WITH_PTNETMAP_GUEST */
+#endif /* WITH_PTNETMAP */
 
 /*
  * In order to track whether pages are still mapped, we hook into
@@ -806,12 +948,12 @@ struct netmap_vm_handle_t {
 
 static int
 netmap_dev_pager_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
-    vm_ooffset_t foff, struct ucred *cred, u_short *color)
+		vm_ooffset_t foff, struct ucred *cred, u_short *color)
 {
 	struct netmap_vm_handle_t *vmh = handle;
 
 	if (netmap_verbose)
-		D("handle %p size %jd prot %d foff %jd",
+		nm_prinf("handle %p size %jd prot %d foff %jd",
 			handle, (intmax_t)size, prot, (intmax_t)foff);
 	if (color)
 		*color = 0;
@@ -828,7 +970,7 @@ netmap_dev_pager_dtor(void *handle)
 	struct netmap_priv_d *priv = vmh->priv;
 
 	if (netmap_verbose)
-		D("handle %p", handle);
+		nm_prinf("handle %p", handle);
 	netmap_dtor(priv);
 	free(vmh, M_DEVBUF);
 	dev_rel(dev);
@@ -845,12 +987,10 @@ netmap_dev_pager_fault(vm_object_t object, vm_ooffset_t offset,
 	vm_paddr_t paddr;
 	vm_page_t page;
 	vm_memattr_t memattr;
-	vm_pindex_t pidx;
 
-	ND("object %p offset %jd prot %d mres %p",
+	nm_prdis("object %p offset %jd prot %d mres %p",
 			object, (intmax_t)offset, prot, mres);
 	memattr = object->memattr;
-	pidx = OFF_TO_IDX(offset);
 	paddr = netmap_mem_ofstophys(na->nm_mem, offset);
 	if (paddr == 0)
 		return VM_PAGER_FAIL;
@@ -867,19 +1007,11 @@ netmap_dev_pager_fault(vm_object_t object, vm_ooffset_t offset,
 		 * Replace the passed in reqpage page with our own fake page and
 		 * free up the all of the original pages.
 		 */
-#ifndef VM_OBJECT_WUNLOCK	/* FreeBSD < 10.x */
-#define VM_OBJECT_WUNLOCK VM_OBJECT_UNLOCK
-#define VM_OBJECT_WLOCK	VM_OBJECT_LOCK
-#endif /* VM_OBJECT_WUNLOCK */
-
 		VM_OBJECT_WUNLOCK(object);
 		page = vm_page_getfake(paddr, memattr);
 		VM_OBJECT_WLOCK(object);
-		vm_page_lock(*mres);
-		vm_page_free(*mres);
-		vm_page_unlock(*mres);
+		vm_page_replace(page, object, (*mres)->pindex, *mres);
 		*mres = page;
-		vm_page_insert(page, object, pidx);
 	}
 	page->valid = VM_PAGE_BITS_ALL;
 	return (VM_PAGER_OK);
@@ -903,7 +1035,7 @@ netmap_mmap_single(struct cdev *cdev, vm_ooffset_t *foff,
 	vm_object_t obj;
 
 	if (netmap_verbose)
-		D("cdev %p foff %jd size %jd objp %p prot %d", cdev,
+		nm_prinf("cdev %p foff %jd size %jd objp %p prot %d", cdev,
 		    (intmax_t )*foff, (intmax_t )objsize, objp, prot);
 
 	vmh = malloc(sizeof(struct netmap_vm_handle_t), M_DEVBUF,
@@ -928,7 +1060,7 @@ netmap_mmap_single(struct cdev *cdev, vm_ooffset_t *foff,
 		&netmap_cdev_pager_ops, objsize, prot,
 		*foff, NULL);
 	if (obj == NULL) {
-		D("cdev_pager_allocate failed");
+		nm_prerr("cdev_pager_allocate failed");
 		error = EINVAL;
 		goto err_deref;
 	}
@@ -962,7 +1094,7 @@ static int
 netmap_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
 {
 	if (netmap_verbose)
-		D("dev %p fflag 0x%x devtype %d td %p",
+		nm_prinf("dev %p fflag 0x%x devtype %d td %p",
 			dev, fflag, devtype, td);
 	return 0;
 }
@@ -1002,73 +1134,34 @@ nm_os_ncpus(void)
 	return mp_maxid + 1;
 }
 
-struct nm_kthread_ctx {
-	struct thread *user_td;		/* thread user-space (kthread creator) to send ioctl */
-	struct ptnetmap_cfgentry_bhyve	cfg;
+struct nm_kctx_ctx {
+	/* Userspace thread (kthread creator). */
+	struct thread *user_td;
 
 	/* worker function and parameter */
-	nm_kthread_worker_fn_t worker_fn;
+	nm_kctx_worker_fn_t worker_fn;
 	void *worker_private;
 
-	struct nm_kthread *nmk;
+	struct nm_kctx *nmk;
 
 	/* integer to manage multiple worker contexts (e.g., RX or TX on ptnetmap) */
 	long type;
 };
 
-struct nm_kthread {
+struct nm_kctx {
 	struct thread *worker;
 	struct mtx worker_lock;
-	uint64_t scheduled; 		/* pending wake_up request */
-	struct nm_kthread_ctx worker_ctx;
+	struct nm_kctx_ctx worker_ctx;
 	int run;			/* used to stop kthread */
 	int attach_user;		/* kthread attached to user_process */
 	int affinity;
 };
 
-void inline
-nm_os_kthread_wakeup_worker(struct nm_kthread *nmk)
-{
-	/*
-	 * There may be a race between FE and BE,
-	 * which call both this function, and worker kthread,
-	 * that reads nmk->scheduled.
-	 *
-	 * For us it is not important the counter value,
-	 * but simply that it has changed since the last
-	 * time the kthread saw it.
-	 */
-	mtx_lock(&nmk->worker_lock);
-	nmk->scheduled++;
-	if (nmk->worker_ctx.cfg.wchan) {
-		wakeup((void *)(uintptr_t)nmk->worker_ctx.cfg.wchan);
-	}
-	mtx_unlock(&nmk->worker_lock);
-}
-
-void inline
-nm_os_kthread_send_irq(struct nm_kthread *nmk)
-{
-	struct nm_kthread_ctx *ctx = &nmk->worker_ctx;
-	int err;
-
-	if (ctx->user_td && ctx->cfg.ioctl_fd > 0) {
-		err = kern_ioctl(ctx->user_td, ctx->cfg.ioctl_fd, ctx->cfg.ioctl_cmd,
-				 (caddr_t)&ctx->cfg.ioctl_data);
-		if (err) {
-			D("kern_ioctl error: %d ioctl parameters: fd %d com %lu data %p",
-				err, ctx->cfg.ioctl_fd, (unsigned long)ctx->cfg.ioctl_cmd,
-				&ctx->cfg.ioctl_data);
-		}
-	}
-}
-
 static void
-nm_kthread_worker(void *data)
+nm_kctx_worker(void *data)
 {
-	struct nm_kthread *nmk = data;
-	struct nm_kthread_ctx *ctx = &nmk->worker_ctx;
-	uint64_t old_scheduled = nmk->scheduled;
+	struct nm_kctx *nmk = data;
+	struct nm_kctx_ctx *ctx = &nmk->worker_ctx;
 
 	if (nmk->affinity >= 0) {
 		thread_lock(curthread);
@@ -1089,51 +1182,23 @@ nm_kthread_worker(void *data)
 			kthread_suspend_check();
 		}
 
-		/*
-		 * if wchan is not defined, we don't have notification
-		 * mechanism and we continually execute worker_fn()
-		 */
-		if (!ctx->cfg.wchan) {
-			ctx->worker_fn(ctx->worker_private); /* worker body */
-		} else {
-			/* checks if there is a pending notification */
-			mtx_lock(&nmk->worker_lock);
-			if (likely(nmk->scheduled != old_scheduled)) {
-				old_scheduled = nmk->scheduled;
-				mtx_unlock(&nmk->worker_lock);
-
-				ctx->worker_fn(ctx->worker_private); /* worker body */
-
-				continue;
-			} else if (nmk->run) {
-				/* wait on event with one second timeout */
-				msleep((void *)(uintptr_t)ctx->cfg.wchan, &nmk->worker_lock,
-					0, "nmk_ev", hz);
-				nmk->scheduled++;
-			}
-			mtx_unlock(&nmk->worker_lock);
-		}
+		/* Continuously execute worker process. */
+		ctx->worker_fn(ctx->worker_private); /* worker body */
 	}
 
 	kthread_exit();
 }
 
 void
-nm_os_kthread_set_affinity(struct nm_kthread *nmk, int affinity)
+nm_os_kctx_worker_setaff(struct nm_kctx *nmk, int affinity)
 {
 	nmk->affinity = affinity;
 }
 
-struct nm_kthread *
-nm_os_kthread_create(struct nm_kthread_cfg *cfg, unsigned int cfgtype,
-		     void *opaque)
+struct nm_kctx *
+nm_os_kctx_create(struct nm_kctx_cfg *cfg, void *opaque)
 {
-	struct nm_kthread *nmk = NULL;
-
-	if (cfgtype != PTNETMAP_CFGTYPE_BHYVE) {
-		D("Unsupported cfgtype %u", cfgtype);
-		return NULL;
-	}
+	struct nm_kctx *nmk = NULL;
 
 	nmk = malloc(sizeof(*nmk),  M_DEVBUF, M_NOWAIT | M_ZERO);
 	if (!nmk)
@@ -1148,23 +1213,22 @@ nm_os_kthread_create(struct nm_kthread_cfg *cfg, unsigned int cfgtype,
 	/* attach kthread to user process (ptnetmap) */
 	nmk->attach_user = cfg->attach_user;
 
-	/* store kick/interrupt configuration */
-	if (opaque) {
-		nmk->worker_ctx.cfg = *((struct ptnetmap_cfgentry_bhyve *)opaque);
-	}
-
 	return nmk;
 }
 
 int
-nm_os_kthread_start(struct nm_kthread *nmk)
+nm_os_kctx_worker_start(struct nm_kctx *nmk)
 {
 	struct proc *p = NULL;
 	int error = 0;
 
-	if (nmk->worker) {
+	/* Temporarily disable this function as it is currently broken
+	 * and causes kernel crashes. The failure can be triggered by
+	 * the "vale_polling_enable_disable" test in ctrl-api-test.c. */
+	return EOPNOTSUPP;
+
+	if (nmk->worker)
 		return EBUSY;
-	}
 
 	/* check if we want to attach kthread to user process */
 	if (nmk->attach_user) {
@@ -1175,47 +1239,44 @@ nm_os_kthread_start(struct nm_kthread *nmk)
 	/* enable kthread main loop */
 	nmk->run = 1;
 	/* create kthread */
-	if((error = kthread_add(nm_kthread_worker, nmk, p,
+	if((error = kthread_add(nm_kctx_worker, nmk, p,
 			&nmk->worker, RFNOWAIT /* to be checked */, 0, "nm-kthread-%ld",
 			nmk->worker_ctx.type))) {
 		goto err;
 	}
 
-	D("nm_kthread started td %p", nmk->worker);
+	nm_prinf("nm_kthread started td %p", nmk->worker);
 
 	return 0;
 err:
-	D("nm_kthread start failed err %d", error);
+	nm_prerr("nm_kthread start failed err %d", error);
 	nmk->worker = NULL;
 	return error;
 }
 
 void
-nm_os_kthread_stop(struct nm_kthread *nmk)
+nm_os_kctx_worker_stop(struct nm_kctx *nmk)
 {
-	if (!nmk->worker) {
+	if (!nmk->worker)
 		return;
-	}
+
 	/* tell to kthread to exit from main loop */
 	nmk->run = 0;
 
 	/* wake up kthread if it sleeps */
 	kthread_resume(nmk->worker);
-	nm_os_kthread_wakeup_worker(nmk);
 
 	nmk->worker = NULL;
 }
 
 void
-nm_os_kthread_delete(struct nm_kthread *nmk)
+nm_os_kctx_destroy(struct nm_kctx *nmk)
 {
 	if (!nmk)
 		return;
-	if (nmk->worker) {
-		nm_os_kthread_stop(nmk);
-	}
 
-	memset(&nmk->worker_ctx.cfg, 0, sizeof(nmk->worker_ctx.cfg));
+	if (nmk->worker)
+		nm_os_kctx_worker_stop(nmk);
 
 	free(nmk, M_DEVBUF);
 }
@@ -1223,34 +1284,36 @@ nm_os_kthread_delete(struct nm_kthread *nmk)
 /******************** kqueue support ****************/
 
 /*
- * nm_os_selwakeup also needs to issue a KNOTE_UNLOCKED.
- * We use a non-zero argument to distinguish the call from the one
- * in kevent_scan() which instead also needs to run netmap_poll().
- * The knote uses a global mutex for the time being. We might
- * try to reuse the one in the si, but it is not allocated
- * permanently so it might be a bit tricky.
+ * In addition to calling selwakeuppri(), nm_os_selwakeup() also
+ * needs to call knote() to wake up kqueue listeners.
+ * This operation is deferred to a taskqueue in order to avoid possible
+ * lock order reversals; these may happen because knote() grabs a
+ * private lock associated to the 'si' (see struct selinfo,
+ * struct nm_selinfo, and nm_os_selinfo_init), and nm_os_selwakeup()
+ * can be called while holding the lock associated to a different
+ * 'si'.
+ * When calling knote() we use a non-zero 'hint' argument to inform
+ * the netmap_knrw() function that it is being called from
+ * 'nm_os_selwakeup'; this is necessary because when netmap_knrw() is
+ * called by the kevent subsystem (i.e. kevent_scan()) we also need to
+ * call netmap_poll().
  *
- * The *kqfilter function registers one or another f_event
- * depending on read or write mode.
- * In the call to f_event() td_fpop is NULL so any child function
- * calling devfs_get_cdevpriv() would fail - and we need it in
- * netmap_poll(). As a workaround we store priv into kn->kn_hook
- * and pass it as first argument to netmap_poll(), which then
- * uses the failure to tell that we are called from f_event()
- * and do not need the selrecord().
+ * The netmap_kqfilter() function registers one or another f_event
+ * depending on read or write mode. A pointer to the struct
+ * 'netmap_priv_d' is stored into kn->kn_hook, so that it can later
+ * be passed to netmap_poll(). We pass NULL as a third argument to
+ * netmap_poll(), so that the latter only runs the txsync/rxsync
+ * (if necessary), and skips the nm_os_selrecord() calls.
  */
 
 
 void
 nm_os_selwakeup(struct nm_selinfo *si)
 {
-	if (netmap_verbose)
-		D("on knote %p", &si->si.si_note);
 	selwakeuppri(&si->si, PI_NET);
-	/* use a non-zero hint to tell the notification from the
-	 * call done in kqueue_scan() which uses 0
-	 */
-	KNOTE_UNLOCKED(&si->si.si_note, 0x100 /* notification */);
+	if (si->kqueue_users > 0) {
+		taskqueue_enqueue(si->ntfytq, &si->ntfytask);
+	}
 }
 
 void
@@ -1263,27 +1326,36 @@ static void
 netmap_knrdetach(struct knote *kn)
 {
 	struct netmap_priv_d *priv = (struct netmap_priv_d *)kn->kn_hook;
-	struct selinfo *si = &priv->np_si[NR_RX]->si;
+	struct nm_selinfo *si = priv->np_si[NR_RX];
 
-	D("remove selinfo %p", si);
-	knlist_remove(&si->si_note, kn, 0);
+	knlist_remove(&si->si.si_note, kn, /*islocked=*/0);
+	NMG_LOCK();
+	KASSERT(si->kqueue_users > 0, ("kqueue_user underflow on %s",
+	    si->mtxname));
+	si->kqueue_users--;
+	nm_prinf("kqueue users for %s: %d", si->mtxname, si->kqueue_users);
+	NMG_UNLOCK();
 }
 
 static void
 netmap_knwdetach(struct knote *kn)
 {
 	struct netmap_priv_d *priv = (struct netmap_priv_d *)kn->kn_hook;
-	struct selinfo *si = &priv->np_si[NR_TX]->si;
+	struct nm_selinfo *si = priv->np_si[NR_TX];
 
-	D("remove selinfo %p", si);
-	knlist_remove(&si->si_note, kn, 0);
+	knlist_remove(&si->si.si_note, kn, /*islocked=*/0);
+	NMG_LOCK();
+	si->kqueue_users--;
+	nm_prinf("kqueue users for %s: %d", si->mtxname, si->kqueue_users);
+	NMG_UNLOCK();
 }
 
 /*
- * callback from notifies (generated externally) and our
- * calls to kevent(). The former we just return 1 (ready)
- * since we do not know better.
- * In the latter we call netmap_poll and return 0/1 accordingly.
+ * Callback triggered by netmap notifications (see netmap_notify()),
+ * and by the application calling kevent(). In the former case we
+ * just return 1 (events ready), since we are not able to do better.
+ * In the latter case we use netmap_poll() to see which events are
+ * ready.
  */
 static int
 netmap_knrw(struct knote *kn, long hint, int events)
@@ -1292,21 +1364,17 @@ netmap_knrw(struct knote *kn, long hint, int events)
 	int revents;
 
 	if (hint != 0) {
-		ND(5, "call from notify");
-		return 1; /* assume we are ready */
-	}
-	priv = kn->kn_hook;
-	/* the notification may come from an external thread,
-	 * in which case we do not want to run the netmap_poll
-	 * This should be filtered above, but check just in case.
-	 */
-	if (curthread != priv->np_td) { /* should not happen */
-		RD(5, "curthread changed %p %p", curthread, priv->np_td);
+		/* Called from netmap_notify(), typically from a
+		 * thread different from the one issuing kevent().
+		 * Assume we are ready. */
 		return 1;
-	} else {
-		revents = netmap_poll(priv, events, NULL);
-		return (events & revents) ? 1 : 0;
 	}
+
+	/* Called from kevent(). */
+	priv = kn->kn_hook;
+	revents = netmap_poll(priv, events, /*thread=*/NULL);
+
+	return (events & revents) ? 1 : 0;
 }
 
 static int
@@ -1337,7 +1405,7 @@ static struct filterops netmap_wfiltops = {
 /*
  * This is called when a thread invokes kevent() to record
  * a change in the configuration of the kqueue().
- * The 'priv' should be the same as in the netmap device.
+ * The 'priv' is the one associated to the open netmap device.
  */
 static int
 netmap_kqfilter(struct cdev *dev, struct knote *kn)
@@ -1349,31 +1417,30 @@ netmap_kqfilter(struct cdev *dev, struct knote *kn)
 	int ev = kn->kn_filter;
 
 	if (ev != EVFILT_READ && ev != EVFILT_WRITE) {
-		D("bad filter request %d", ev);
+		nm_prerr("bad filter request %d", ev);
 		return 1;
 	}
 	error = devfs_get_cdevpriv((void**)&priv);
 	if (error) {
-		D("device not yet setup");
+		nm_prerr("device not yet setup");
 		return 1;
 	}
 	na = priv->np_na;
 	if (na == NULL) {
-		D("no netmap adapter for this file descriptor");
+		nm_prerr("no netmap adapter for this file descriptor");
 		return 1;
 	}
 	/* the si is indicated in the priv */
 	si = priv->np_si[(ev == EVFILT_WRITE) ? NR_TX : NR_RX];
-	// XXX lock(priv) ?
 	kn->kn_fop = (ev == EVFILT_WRITE) ?
 		&netmap_wfiltops : &netmap_rfiltops;
 	kn->kn_hook = priv;
-	knlist_add(&si->si.si_note, kn, 1);
-	// XXX unlock(priv)
-	ND("register %p %s td %p priv %p kn %p np_nifp %p kn_fp/fpop %s",
-		na, na->ifp->if_xname, curthread, priv, kn,
-		priv->np_nifp,
-		kn->kn_fp == curthread->td_fpop ? "match" : "MISMATCH");
+	NMG_LOCK();
+	si->kqueue_users++;
+	nm_prinf("kqueue users for %s: %d", si->mtxname, si->kqueue_users);
+	NMG_UNLOCK();
+	knlist_add(&si->si.si_note, kn, /*islocked=*/0);
+
 	return 0;
 }
 
@@ -1389,7 +1456,7 @@ freebsd_netmap_poll(struct cdev *cdevi __unused, int events, struct thread *td)
 
 static int
 freebsd_netmap_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
-        int ffla __unused, struct thread *td)
+		int ffla __unused, struct thread *td)
 {
 	int error;
 	struct netmap_priv_d *priv;
@@ -1403,13 +1470,38 @@ freebsd_netmap_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
 			error = ENXIO;
 		goto out;
 	}
-	error = netmap_ioctl(priv, cmd, data, td);
+	error = netmap_ioctl(priv, cmd, data, td, /*nr_body_is_user=*/1);
 out:
 	CURVNET_RESTORE();
 
 	return error;
 }
 
+void
+nm_os_onattach(if_t ifp)
+{
+	if_setcapabilitiesbit(ifp, IFCAP_NETMAP, 0);
+}
+
+void
+nm_os_onenter(if_t ifp)
+{
+	struct netmap_adapter *na = NA(ifp);
+
+	na->if_transmit = if_gettransmitfn(ifp);
+	if_settransmitfn(ifp, netmap_transmit);
+	if_setcapenablebit(ifp, IFCAP_NETMAP, 0);
+}
+
+void
+nm_os_onexit(if_t ifp)
+{
+	struct netmap_adapter *na = NA(ifp);
+
+	if_settransmitfn(ifp, na->if_transmit);
+	if_setcapenablebit(ifp, 0, IFCAP_NETMAP);
+}
+
 extern struct cdevsw netmap_cdevsw; /* XXX used in netmap.c, should go elsewhere */
 struct cdevsw netmap_cdevsw = {
 	.d_version = D_VERSION,
@@ -1446,7 +1538,7 @@ netmap_loader(__unused struct module *module, int event, __unused void *arg)
 		 * then the module can not be unloaded.
 		 */
 		if (netmap_use_count) {
-			D("netmap module can not be unloaded - netmap_use_count: %d",
+			nm_prerr("netmap module can not be unloaded - netmap_use_count: %d",
 					netmap_use_count);
 			error = EBUSY;
 			break;
diff --git a/sys/dev/netmap/netmap_generic.c b/sys/dev/netmap/netmap_generic.c
index 5969427a8..41e89b893 100644
--- a/sys/dev/netmap/netmap_generic.c
+++ b/sys/dev/netmap/netmap_generic.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2013-2016 Vincenzo Maffione
  * Copyright (C) 2013-2016 Luigi Rizzo
  * All rights reserved.
@@ -65,8 +67,6 @@
 #ifdef __FreeBSD__
 
 #include  /* prerequisite */
-__FBSDID("$FreeBSD: head/sys/dev/netmap/netmap_generic.c 274353 2014-11-10 20:19:58Z luigi $");
-
 #include 
 #include 
 #include 
@@ -75,129 +75,21 @@ __FBSDID("$FreeBSD: head/sys/dev/netmap/netmap_generic.c 274353 2014-11-10 20:19
 #include  /* sockaddrs */
 #include 
 #include 
+#include 
 #include 
 #include         /* bus_dmamap_* in netmap_kern.h */
 
-// XXX temporary - D() defined here
 #include 
 #include 
 #include 
 
-#define rtnl_lock()	ND("rtnl_lock called")
-#define rtnl_unlock()	ND("rtnl_unlock called")
 #define MBUF_RXQ(m)	((m)->m_pkthdr.flowid)
 #define smp_mb()
 
-/*
- * FreeBSD mbuf allocator/deallocator in emulation mode:
- */
-#if __FreeBSD_version < 1100000
-
-/*
- * For older versions of FreeBSD:
- *
- * We allocate EXT_PACKET mbuf+clusters, but need to set M_NOFREE
- * so that the destructor, if invoked, will not free the packet.
- * In principle we should set the destructor only on demand,
- * but since there might be a race we better do it on allocation.
- * As a consequence, we also need to set the destructor or we
- * would leak buffers.
- */
-
-/* mbuf destructor, also need to change the type to EXT_EXTREF,
- * add an M_NOFREE flag, and then clear the flag and
- * chain into uma_zfree(zone_pack, mf)
- * (or reinstall the buffer ?)
- */
-#define SET_MBUF_DESTRUCTOR(m, fn)	do {		\
-	(m)->m_ext.ext_free = (void *)fn;	\
-	(m)->m_ext.ext_type = EXT_EXTREF;	\
-} while (0)
-
-static int
-void_mbuf_dtor(struct mbuf *m, void *arg1, void *arg2)
-{
-	/* restore original mbuf */
-	m->m_ext.ext_buf = m->m_data = m->m_ext.ext_arg1;
-	m->m_ext.ext_arg1 = NULL;
-	m->m_ext.ext_type = EXT_PACKET;
-	m->m_ext.ext_free = NULL;
-	if (MBUF_REFCNT(m) == 0)
-		SET_MBUF_REFCNT(m, 1);
-	uma_zfree(zone_pack, m);
-
-	return 0;
-}
-
-static inline struct mbuf *
-nm_os_get_mbuf(struct ifnet *ifp, int len)
-{
-	struct mbuf *m;
-
-	(void)ifp;
-	m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
-	if (m) {
-		/* m_getcl() (mb_ctor_mbuf) has an assert that checks that
-		 * M_NOFREE flag is not specified as third argument,
-		 * so we have to set M_NOFREE after m_getcl(). */
-		m->m_flags |= M_NOFREE;
-		m->m_ext.ext_arg1 = m->m_ext.ext_buf; // XXX save
-		m->m_ext.ext_free = (void *)void_mbuf_dtor;
-		m->m_ext.ext_type = EXT_EXTREF;
-		ND(5, "create m %p refcnt %d", m, MBUF_REFCNT(m));
-	}
-	return m;
-}
-
-#else /* __FreeBSD_version >= 1100000 */
-
-/*
- * Newer versions of FreeBSD, using a straightforward scheme.
- *
- * We allocate mbufs with m_gethdr(), since the mbuf header is needed
- * by the driver. We also attach a customly-provided external storage,
- * which in this case is a netmap buffer. When calling m_extadd(), however
- * we pass a NULL address, since the real address (and length) will be
- * filled in by nm_os_generic_xmit_frame() right before calling
- * if_transmit().
- *
- * The dtor function does nothing, however we need it since mb_free_ext()
- * has a KASSERT(), checking that the mbuf dtor function is not NULL.
- */
-
-static void void_mbuf_dtor(struct mbuf *m, void *arg1, void *arg2) { }
-
-#define SET_MBUF_DESTRUCTOR(m, fn)	do {		\
-	(m)->m_ext.ext_free = fn ? (void *)fn : (void *)void_mbuf_dtor;	\
-} while (0)
-
-static inline struct mbuf *
-nm_os_get_mbuf(struct ifnet *ifp, int len)
-{
-	struct mbuf *m;
-
-	(void)ifp;
-	(void)len;
-
-	m = m_gethdr(M_NOWAIT, MT_DATA);
-	if (m == NULL) {
-		return m;
-	}
-
-	m_extadd(m, NULL /* buf */, 0 /* size */, void_mbuf_dtor,
-		 NULL, NULL, 0, EXT_NET_DRV);
-
-	return m;
-}
-
-#endif /* __FreeBSD_version >= 1100000 */
-
 #elif defined _WIN32
 
 #include "win_glue.h"
 
-#define rtnl_lock()	ND("rtnl_lock called")
-#define rtnl_unlock()	ND("rtnl_unlock called")
 #define MBUF_TXQ(m) 	0//((m)->m_pkthdr.flowid)
 #define MBUF_RXQ(m)	    0//((m)->m_pkthdr.flowid)
 #define smp_mb()		//XXX: to be correctly defined
@@ -206,14 +98,13 @@ nm_os_get_mbuf(struct ifnet *ifp, int len)
 
 #include "bsd_glue.h"
 
-#include     /* rtnl_[un]lock() */
 #include       /* struct ethtool_ops, get_ringparam */
 #include 
 
 static inline struct mbuf *
 nm_os_get_mbuf(struct ifnet *ifp, int len)
 {
-	return alloc_skb(ifp->needed_headroom + len +
+	return alloc_skb(LL_RESERVED_SPACE(ifp) + len +
 			 ifp->needed_tailroom, GFP_ATOMIC);
 }
 
@@ -227,17 +118,17 @@ nm_os_get_mbuf(struct ifnet *ifp, int len)
 
 
 #define for_each_kring_n(_i, _k, _karr, _n) \
-	for (_k=_karr, _i = 0; _i < _n; (_k)++, (_i)++)
+	for ((_k)=*(_karr), (_i) = 0; (_i) < (_n); (_i)++, (_k) = (_karr)[(_i)])
 
 #define for_each_tx_kring(_i, _k, _na) \
-            for_each_kring_n(_i, _k, (_na)->tx_rings, (_na)->num_tx_rings)
+		for_each_kring_n(_i, _k, (_na)->tx_rings, (_na)->num_tx_rings)
 #define for_each_tx_kring_h(_i, _k, _na) \
-            for_each_kring_n(_i, _k, (_na)->tx_rings, (_na)->num_tx_rings + 1)
+		for_each_kring_n(_i, _k, (_na)->tx_rings, (_na)->num_tx_rings + 1)
 
 #define for_each_rx_kring(_i, _k, _na) \
-            for_each_kring_n(_i, _k, (_na)->rx_rings, (_na)->num_rx_rings)
+		for_each_kring_n(_i, _k, (_na)->rx_rings, (_na)->num_rx_rings)
 #define for_each_rx_kring_h(_i, _k, _na) \
-            for_each_kring_n(_i, _k, (_na)->rx_rings, (_na)->num_rx_rings + 1)
+		for_each_kring_n(_i, _k, (_na)->rx_rings, (_na)->num_rx_rings + 1)
 
 
 /* ======================== PERFORMANCE STATISTICS =========================== */
@@ -285,19 +176,19 @@ static void rate_callback(unsigned long arg)
 	r = mod_timer(&ctx->timer, jiffies +
 			msecs_to_jiffies(RATE_PERIOD * 1000));
 	if (unlikely(r))
-		D("[v1000] Error: mod_timer()");
+		nm_prerr("mod_timer() failed");
 }
 
 static struct rate_context rate_ctx;
 
 void generic_rate(int txp, int txs, int txi, int rxp, int rxs, int rxi)
 {
-    if (txp) rate_ctx.new.txpkt++;
-    if (txs) rate_ctx.new.txsync++;
-    if (txi) rate_ctx.new.txirq++;
-    if (rxp) rate_ctx.new.rxpkt++;
-    if (rxs) rate_ctx.new.rxsync++;
-    if (rxi) rate_ctx.new.rxirq++;
+	if (txp) rate_ctx.new.txpkt++;
+	if (txs) rate_ctx.new.txsync++;
+	if (txi) rate_ctx.new.txirq++;
+	if (rxp) rate_ctx.new.rxpkt++;
+	if (rxs) rate_ctx.new.rxsync++;
+	if (rxi) rate_ctx.new.rxirq++;
 }
 
 #else /* !RATE */
@@ -305,7 +196,7 @@ void generic_rate(int txp, int txs, int txi, int rxp, int rxs, int rxi)
 #endif /* !RATE */
 
 
-/* =============== GENERIC NETMAP ADAPTER SUPPORT ================= */
+/* ========== GENERIC (EMULATED) NETMAP ADAPTER SUPPORT ============= */
 
 /*
  * Wrapper used by the generic adapter layer to notify
@@ -335,32 +226,16 @@ generic_netmap_unregister(struct netmap_adapter *na)
 	int i, r;
 
 	if (na->active_fds == 0) {
-		D("Generic adapter %p goes off", na);
-		rtnl_lock();
-
 		na->na_flags &= ~NAF_NETMAP_ON;
 
-		/* Release packet steering control. */
-		nm_os_catch_tx(gna, 0);
-
 		/* Stop intercepting packets on the RX path. */
 		nm_os_catch_rx(gna, 0);
 
-		rtnl_unlock();
+		/* Release packet steering control. */
+		nm_os_catch_tx(gna, 0);
 	}
 
-	for_each_rx_kring_h(r, kring, na) {
-		if (nm_kring_pending_off(kring)) {
-			D("RX ring %d of generic adapter %p goes off", r, na);
-			kring->nr_mode = NKR_NETMAP_OFF;
-		}
-	}
-	for_each_tx_kring_h(r, kring, na) {
-		if (nm_kring_pending_off(kring)) {
-			kring->nr_mode = NKR_NETMAP_OFF;
-			D("TX ring %d of generic adapter %p goes off", r, na);
-		}
-	}
+	netmap_krings_mode_commit(na, /*onoff=*/0);
 
 	for_each_rx_kring(r, kring, na) {
 		/* Free the mbufs still pending in the RX queues,
@@ -411,10 +286,11 @@ generic_netmap_unregister(struct netmap_adapter *na)
 
 #ifdef RATE_GENERIC
 		if (--rate_ctx.refcount == 0) {
-			D("del_timer()");
+			nm_prinf("del_timer()");
 			del_timer(&rate_ctx.timer);
 		}
 #endif
+		nm_prinf("Emulated adapter for %s deactivated", na->name);
 	}
 
 	return 0;
@@ -439,14 +315,14 @@ generic_netmap_register(struct netmap_adapter *na, int enable)
 	}
 
 	if (na->active_fds == 0) {
-		D("Generic adapter %p goes on", na);
+		nm_prinf("Emulated adapter for %s activated", na->name);
 		/* Do all memory allocations when (na->active_fds == 0), to
 		 * simplify error management. */
 
 		/* Allocate memory for mitigation support on all the rx queues. */
 		gna->mit = nm_os_malloc(na->num_rx_rings * sizeof(struct nm_generic_mit));
 		if (!gna->mit) {
-			D("mitigation allocation failed");
+			nm_prerr("mitigation allocation failed");
 			error = ENOMEM;
 			goto out;
 		}
@@ -473,7 +349,7 @@ generic_netmap_register(struct netmap_adapter *na, int enable)
 			kring->tx_pool =
 				nm_os_malloc(na->num_tx_desc * sizeof(struct mbuf *));
 			if (!kring->tx_pool) {
-				D("tx_pool allocation failed");
+				nm_prerr("tx_pool allocation failed");
 				error = ENOMEM;
 				goto free_tx_pools;
 			}
@@ -482,19 +358,7 @@ generic_netmap_register(struct netmap_adapter *na, int enable)
 		}
 	}
 
-	for_each_rx_kring_h(r, kring, na) {
-		if (nm_kring_pending_on(kring)) {
-			D("RX ring %d of generic adapter %p goes on", r, na);
-			kring->nr_mode = NKR_NETMAP_ON;
-		}
-
-	}
-	for_each_tx_kring_h(r, kring, na) {
-		if (nm_kring_pending_on(kring)) {
-			D("TX ring %d of generic adapter %p goes on", r, na);
-			kring->nr_mode = NKR_NETMAP_ON;
-		}
-	}
+	netmap_krings_mode_commit(na, /*onoff=*/1);
 
 	for_each_tx_kring(r, kring, na) {
 		/* Initialize tx_pool and tx_event. */
@@ -506,33 +370,29 @@ generic_netmap_register(struct netmap_adapter *na, int enable)
 	}
 
 	if (na->active_fds == 0) {
-		rtnl_lock();
-
 		/* Prepare to intercept incoming traffic. */
 		error = nm_os_catch_rx(gna, 1);
 		if (error) {
-			D("nm_os_catch_rx(1) failed (%d)", error);
-			goto register_handler;
+			nm_prerr("nm_os_catch_rx(1) failed (%d)", error);
+			goto free_tx_pools;
 		}
 
-		/* Make netmap control the packet steering. */
+		/* Let netmap control the packet steering. */
 		error = nm_os_catch_tx(gna, 1);
 		if (error) {
-			D("nm_os_catch_tx(1) failed (%d)", error);
+			nm_prerr("nm_os_catch_tx(1) failed (%d)", error);
 			goto catch_rx;
 		}
 
-		rtnl_unlock();
-
 		na->na_flags |= NAF_NETMAP_ON;
 
 #ifdef RATE_GENERIC
 		if (rate_ctx.refcount == 0) {
-			D("setup_timer()");
+			nm_prinf("setup_timer()");
 			memset(&rate_ctx, 0, sizeof(rate_ctx));
 			setup_timer(&rate_ctx.timer, &rate_callback, (unsigned long)&rate_ctx);
 			if (mod_timer(&rate_ctx.timer, jiffies + msecs_to_jiffies(1500))) {
-				D("Error: mod_timer()");
+				nm_prerr("Error: mod_timer()");
 			}
 		}
 		rate_ctx.refcount++;
@@ -544,8 +404,6 @@ generic_netmap_register(struct netmap_adapter *na, int enable)
 	/* Here (na->active_fds == 0) holds. */
 catch_rx:
 	nm_os_catch_rx(gna, 0);
-register_handler:
-	rtnl_unlock();
 free_tx_pools:
 	for_each_tx_kring(r, kring, na) {
 		mtx_destroy(&kring->tx_event_lock);
@@ -578,23 +436,22 @@ generic_mbuf_destructor(struct mbuf *m)
 	unsigned int r_orig = r;
 
 	if (unlikely(!nm_netmap_on(na) || r >= na->num_tx_rings)) {
-		D("Error: no netmap adapter on device %p",
-		  GEN_TX_MBUF_IFP(m));
+		nm_prerr("Error: no netmap adapter on device %s", na->name);
 		return;
 	}
 
 	/*
 	 * First, clear the event mbuf.
 	 * In principle, the event 'm' should match the one stored
-	 * on ring 'r'. However we check it explicitely to stay
+	 * on ring 'r'. However we check it explicitly to stay
 	 * safe against lower layers (qdisc, driver, etc.) changing
 	 * MBUF_TXQ(m) under our feet. If the match is not found
 	 * on 'r', we try to see if it belongs to some other ring.
 	 */
-        for (;;) {
+	for (;;) {
 		bool match = false;
 
-		kring = &na->tx_rings[r];
+		kring = na->tx_rings[r];
 		mtx_lock_spin(&kring->tx_event_lock);
 		if (kring->tx_event == m) {
 			kring->tx_event = NULL;
@@ -604,7 +461,7 @@ generic_mbuf_destructor(struct mbuf *m)
 
 		if (match) {
 			if (r != r_orig) {
-				RD(1, "event %p migrated: ring %u --> %u",
+				nm_prlim(1, "event %p migrated: ring %u --> %u",
 				      m, r_orig, r);
 			}
 			break;
@@ -613,7 +470,7 @@ generic_mbuf_destructor(struct mbuf *m)
 		if (++r == na->num_tx_rings) r = 0;
 
 		if (r == r_orig) {
-			RD(1, "Cannot match event %p", m);
+			nm_prlim(1, "Cannot match event %p", m);
 			return;
 		}
 	}
@@ -622,7 +479,7 @@ generic_mbuf_destructor(struct mbuf *m)
 	 * txsync. */
 	netmap_generic_irq(na, r, NULL);
 #ifdef __FreeBSD__
-	void_mbuf_dtor(m, NULL, NULL);
+	void_mbuf_dtor(m);
 #endif
 }
 
@@ -640,7 +497,7 @@ generic_netmap_tx_clean(struct netmap_kring *kring, int txqdisc)
 	u_int n = 0;
 	struct mbuf **tx_pool = kring->tx_pool;
 
-	ND("hwcur = %d, hwtail = %d", kring->nr_hwcur, kring->nr_hwtail);
+	nm_prdis("hwcur = %d, hwtail = %d", kring->nr_hwcur, kring->nr_hwtail);
 
 	while (nm_i != hwcur) { /* buffers not completed */
 		struct mbuf *m = tx_pool[nm_i];
@@ -649,7 +506,7 @@ generic_netmap_tx_clean(struct netmap_kring *kring, int txqdisc)
 			if (m == NULL) {
 				/* Nothing to do, this is going
 				 * to be replenished. */
-				RD(3, "Is this happening?");
+				nm_prlim(3, "Is this happening?");
 
 			} else if (MBUF_QUEUED(m)) {
 				break; /* Not dequeued yet. */
@@ -688,7 +545,7 @@ generic_netmap_tx_clean(struct netmap_kring *kring, int txqdisc)
 		nm_i = nm_next(nm_i, lim);
 	}
 	kring->nr_hwtail = nm_prev(nm_i, lim);
-	ND("tx completed [%d] -> hwtail %d", n, kring->nr_hwtail);
+	nm_prdis("tx completed [%d] -> hwtail %d", n, kring->nr_hwtail);
 
 	return n;
 }
@@ -710,7 +567,7 @@ ring_middle(u_int inf, u_int sup, u_int lim)
 	}
 
 	if (unlikely(e >= n)) {
-		D("This cannot happen");
+		nm_prerr("This cannot happen");
 		e = 0;
 	}
 
@@ -766,7 +623,7 @@ generic_set_tx_event(struct netmap_kring *kring, u_int hwcur)
 
 	kring->tx_pool[e] = NULL;
 
-	ND(5, "Request Event at %d mbuf %p refcnt %d", e, m, m ? MBUF_REFCNT(m) : -2 );
+	nm_prdis("Request Event at %d mbuf %p refcnt %d", e, m, m ? MBUF_REFCNT(m) : -2 );
 
 	/* Decrement the refcount. This will free it if we lose the race
 	 * with the driver. */
@@ -787,7 +644,7 @@ generic_netmap_txsync(struct netmap_kring *kring, int flags)
 {
 	struct netmap_adapter *na = kring->na;
 	struct netmap_generic_adapter *gna = (struct netmap_generic_adapter *)na;
-	struct ifnet *ifp = na->ifp;
+	if_t ifp = na->ifp;
 	struct netmap_ring *ring = kring->ring;
 	u_int nm_i;	/* index into the netmap ring */ // j
 	u_int const lim = kring->nkr_num_slots - 1;
@@ -805,13 +662,18 @@ generic_netmap_txsync(struct netmap_kring *kring, int flags)
 	if (nm_i != head) {	/* we have new packets to send */
 		struct nm_os_gen_arg a;
 		u_int event = -1;
+#ifdef __FreeBSD__
+		struct epoch_tracker et;
+
+		NET_EPOCH_ENTER(et);
+#endif
 
 		if (gna->txqdisc && nm_kr_txempty(kring)) {
 			/* In txqdisc mode, we ask for a delayed notification,
 			 * but only when cur == hwtail, which means that the
 			 * client is going to block. */
 			event = ring_middle(nm_i, head, lim);
-			ND(3, "Place txqdisc event (hwcur=%u,event=%u,"
+			nm_prdis("Place txqdisc event (hwcur=%u,event=%u,"
 			      "head=%u,hwtail=%u)", nm_i, event, head,
 			      kring->nr_hwtail);
 		}
@@ -822,22 +684,23 @@ generic_netmap_txsync(struct netmap_kring *kring, int flags)
 
 		while (nm_i != head) {
 			struct netmap_slot *slot = &ring->slot[nm_i];
+			uint64_t offset = nm_get_offset(kring, slot);
+			void *addr = NMB_O(kring, slot);
 			u_int len = slot->len;
-			void *addr = NMB(na, slot);
 			/* device-specific */
 			struct mbuf *m;
 			int tx_ret;
 
-			NM_CHECK_ADDR_LEN(na, addr, len);
+			NM_CHECK_ADDR_LEN_OFF(na, len, offset);
 
-			/* Tale a mbuf from the tx pool (replenishing the pool
+			/* Take a mbuf from the tx pool (replenishing the pool
 			 * entry if necessary) and copy in the user packet. */
 			m = kring->tx_pool[nm_i];
 			if (unlikely(m == NULL)) {
 				kring->tx_pool[nm_i] = m =
 					nm_os_get_mbuf(ifp, NETMAP_BUF_SIZE(na));
 				if (m == NULL) {
-					RD(2, "Failed to replenish mbuf");
+					nm_prlim(2, "Failed to replenish mbuf");
 					/* Here we could schedule a timer which
 					 * retries to replenish after a while,
 					 * and notifies the client when it
@@ -912,6 +775,10 @@ generic_netmap_txsync(struct netmap_kring *kring, int flags)
 		/* Update hwcur to the next slot to transmit. Here nm_i
 		 * is not necessarily head, we could break early. */
 		kring->nr_hwcur = nm_i;
+
+#ifdef __FreeBSD__
+		NET_EPOCH_EXIT(et);
+#endif
 	}
 
 	/*
@@ -942,7 +809,7 @@ generic_netmap_txsync(struct netmap_kring *kring, int flags)
  * Returns 1 if the packet was stolen, 0 otherwise.
  */
 int
-generic_rx_handler(struct ifnet *ifp, struct mbuf *m)
+generic_rx_handler(if_t ifp, struct mbuf *m)
 {
 	struct netmap_adapter *na = NA(ifp);
 	struct netmap_generic_adapter *gna = (struct netmap_generic_adapter *)na;
@@ -954,7 +821,7 @@ generic_rx_handler(struct ifnet *ifp, struct mbuf *m)
 		r = r % na->num_rx_rings;
 	}
 
-	kring = &na->rx_rings[r];
+	kring = na->rx_rings[r];
 
 	if (kring->nr_mode == NKR_NETMAP_OFF) {
 		/* We must not intercept this mbuf. */
@@ -966,10 +833,12 @@ generic_rx_handler(struct ifnet *ifp, struct mbuf *m)
 		/* This may happen when GRO/LRO features are enabled for
 		 * the NIC driver when the generic adapter does not
 		 * support RX scatter-gather. */
-		RD(2, "Warning: driver pushed up big packet "
+		nm_prlim(2, "Warning: driver pushed up big packet "
 				"(size=%d)", (int)MBUF_LEN(m));
+		if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
 		m_freem(m);
-	} else if (unlikely(mbq_len(&kring->rx_queue) > 1024)) {
+	} else if (unlikely(mbq_len(&kring->rx_queue) > na->num_rx_desc)) {
+		if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
 		m_freem(m);
 	} else {
 		mbq_safe_enqueue(&kring->rx_queue, m);
@@ -1013,7 +882,6 @@ generic_netmap_rxsync(struct netmap_kring *kring, int flags)
 	int force_update = (flags & NAF_FORCE_READ) || kring->nr_kflags & NKR_PENDINTR;
 
 	/* Adapter-specific variables. */
-	uint16_t slot_flags = kring->nkr_slot_flags;
 	u_int nm_buf_len = NETMAP_BUF_SIZE(na);
 	struct mbq tmpq;
 	struct mbuf *m;
@@ -1092,7 +960,7 @@ generic_netmap_rxsync(struct netmap_kring *kring, int flags)
 			avail -= nm_buf_len;
 
 			ring->slot[nm_i].len = copy;
-			ring->slot[nm_i].flags = slot_flags | (mlen ? NS_MOREFRAG : 0);
+			ring->slot[nm_i].flags = (mlen ? NS_MOREFRAG : 0);
 			nm_i = nm_next(nm_i, lim);
 		}
 
@@ -1105,8 +973,7 @@ generic_netmap_rxsync(struct netmap_kring *kring, int flags)
 	nm_i = kring->nr_hwtail;
 
 	for (;;) {
-		void *nmaddr;
-		int ofs = 0;
+		int mbuf_ofs = 0;
 		int morefrag;
 
 		m = mbq_dequeue(&tmpq);
@@ -1115,8 +982,10 @@ generic_netmap_rxsync(struct netmap_kring *kring, int flags)
 		}
 
 		do {
-			nmaddr = NMB(na, &ring->slot[nm_i]);
-			/* We only check the address here on generic rx rings. */
+			struct netmap_slot *slot = ring->slot + nm_i;
+			uint64_t nm_offset = nm_get_offset(kring, slot);
+			char *nmaddr = NMB(na, slot);
+
 			if (nmaddr == NETMAP_BUF_BASE(na)) { /* Bad buffer */
 				m_freem(m);
 				mbq_purge(&tmpq);
@@ -1124,10 +993,10 @@ generic_netmap_rxsync(struct netmap_kring *kring, int flags)
 				return netmap_ring_reinit(kring);
 			}
 
-			copy = ring->slot[nm_i].len;
-			m_copydata(m, ofs, copy, nmaddr);
-			ofs += copy;
-			morefrag = ring->slot[nm_i].flags & NS_MOREFRAG;
+			copy = slot->len;
+			m_copydata(m, mbuf_ofs, copy, nmaddr + nm_offset);
+			mbuf_ofs += copy;
+			morefrag = slot->flags & NS_MOREFRAG;
 			nm_i = nm_next(nm_i, lim);
 		} while (morefrag);
 
@@ -1149,11 +1018,10 @@ static void
 generic_netmap_dtor(struct netmap_adapter *na)
 {
 	struct netmap_generic_adapter *gna = (struct netmap_generic_adapter*)na;
-	struct ifnet *ifp = netmap_generic_getifp(gna);
+	if_t ifp = netmap_generic_getifp(gna);
 	struct netmap_adapter *prev_na = gna->prev;
 
 	if (prev_na != NULL) {
-		D("Released generic NA %p", gna);
 		netmap_adapter_put(prev_na);
 		if (nm_iszombie(na)) {
 		        /*
@@ -1162,14 +1030,17 @@ generic_netmap_dtor(struct netmap_adapter *na)
 		         */
 		        netmap_adapter_put(prev_na);
 		}
+		nm_prinf("Native netmap adapter for %s restored", prev_na->name);
 	}
-	NM_ATTACH_NA(ifp, prev_na);
-	/*
-	 * netmap_detach_common(), that it's called after this function,
-	 * overrides WNA(ifp) if na->ifp is not NULL.
-	 */
+	NM_RESTORE_NA(ifp, prev_na);
 	na->ifp = NULL;
-	D("Restored native NA %p", prev_na);
+	nm_prinf("Emulated netmap adapter for %s destroyed", na->name);
+}
+
+int
+na_is_generic(struct netmap_adapter *na)
+{
+	return na->nm_register == generic_netmap_register;
 }
 
 /*
@@ -1184,32 +1055,48 @@ generic_netmap_dtor(struct netmap_adapter *na)
  * actual configuration.
  */
 int
-generic_netmap_attach(struct ifnet *ifp)
+generic_netmap_attach(if_t ifp)
 {
 	struct netmap_adapter *na;
 	struct netmap_generic_adapter *gna;
 	int retval;
 	u_int num_tx_desc, num_rx_desc;
 
+#ifdef __FreeBSD__
+	if (if_gettype(ifp) == IFT_LOOP) {
+		nm_prerr("if_loop is not supported by %s", __func__);
+		return EINVAL;
+	}
+#endif
+
+	if (NM_NA_CLASH(ifp)) {
+		/* If NA(ifp) is not null but there is no valid netmap
+		 * adapter it means that someone else is using the same
+		 * pointer (e.g. ax25_ptr on linux). This happens for
+		 * instance when also PF_RING is in use. */
+		nm_prerr("Error: netmap adapter hook is busy");
+		return EBUSY;
+	}
+
 	num_tx_desc = num_rx_desc = netmap_generic_ringsize; /* starting point */
 
 	nm_os_generic_find_num_desc(ifp, &num_tx_desc, &num_rx_desc); /* ignore errors */
-	ND("Netmap ring size: TX = %d, RX = %d", num_tx_desc, num_rx_desc);
 	if (num_tx_desc == 0 || num_rx_desc == 0) {
-		D("Device has no hw slots (tx %u, rx %u)", num_tx_desc, num_rx_desc);
+		nm_prerr("Device has no hw slots (tx %u, rx %u)", num_tx_desc, num_rx_desc);
 		return EINVAL;
 	}
 
 	gna = nm_os_malloc(sizeof(*gna));
 	if (gna == NULL) {
-		D("no memory on attach, give up");
+		nm_prerr("no memory on attach, give up");
 		return ENOMEM;
 	}
 	na = (struct netmap_adapter *)gna;
-	strncpy(na->name, ifp->if_xname, sizeof(na->name));
+	strlcpy(na->name, if_name(ifp), sizeof(na->name));
 	na->ifp = ifp;
 	na->num_tx_desc = num_tx_desc;
 	na->num_rx_desc = num_rx_desc;
+	na->rx_buf_maxsize = 32768;
 	na->nm_register = &generic_netmap_register;
 	na->nm_txsync = &generic_netmap_txsync;
 	na->nm_rxsync = &generic_netmap_rxsync;
@@ -1217,12 +1104,12 @@ generic_netmap_attach(struct ifnet *ifp)
 	/* when using generic, NAF_NETMAP_ON is set so we force
 	 * NAF_SKIP_INTR to use the regular interrupt handler
 	 */
-	na->na_flags = NAF_SKIP_INTR | NAF_HOST_RINGS;
+	na->na_flags = NAF_SKIP_INTR | NAF_HOST_RINGS | NAF_OFFSETS;
 
-	ND("[GNA] num_tx_queues(%d), real_num_tx_queues(%d), len(%lu)",
+	nm_prdis("[GNA] num_tx_queues(%d), real_num_tx_queues(%d), len(%lu)",
 			ifp->num_tx_queues, ifp->real_num_tx_queues,
 			ifp->tx_queue_len);
-	ND("[GNA] num_rx_queues(%d), real_num_rx_queues(%d)",
+	nm_prdis("[GNA] num_rx_queues(%d), real_num_rx_queues(%d)",
 			ifp->num_rx_queues, ifp->real_num_rx_queues);
 
 	nm_os_generic_find_num_queues(ifp, &na->num_tx_rings, &na->num_rx_rings);
@@ -1233,15 +1120,16 @@ generic_netmap_attach(struct ifnet *ifp)
 		return retval;
 	}
 
-	gna->prev = NA(ifp); /* save old na */
-	if (gna->prev != NULL) {
+	if (NM_NA_VALID(ifp)) {
+		gna->prev = NA(ifp); /* save old na */
 		netmap_adapter_get(gna->prev);
 	}
 	NM_ATTACH_NA(ifp, na);
 
 	nm_os_generic_set_features(gna);
 
-	D("Created generic NA %p (prev %p)", gna, gna->prev);
+	nm_prinf("Emulated adapter for %s created (prev was %s)", na->name,
+	    gna->prev ? gna->prev->name : "NULL");
 
 	return retval;
 }
diff --git a/sys/dev/netmap/netmap_kern.h b/sys/dev/netmap/netmap_kern.h
index a22d91f41..4f66efb31 100644
--- a/sys/dev/netmap/netmap_kern.h
+++ b/sys/dev/netmap/netmap_kern.h
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo
  * Copyright (C) 2013-2016 Universita` di Pisa
  * All rights reserved.
@@ -26,8 +28,6 @@
  */
 
 /*
- * $FreeBSD: head/sys/dev/netmap/netmap_kern.h 238985 2012-08-02 11:59:43Z luigi $
- *
  * The header contains the definitions of constants and function
  * prototypes used only in kernelspace.
  */
@@ -37,6 +37,9 @@
 
 #if defined(linux)
 
+#if defined(CONFIG_NETMAP_EXTMEM)
+#define WITH_EXTMEM
+#endif
 #if  defined(CONFIG_NETMAP_VALE)
 #define WITH_VALE
 #endif
@@ -49,30 +52,30 @@
 #if defined(CONFIG_NETMAP_GENERIC)
 #define WITH_GENERIC
 #endif
-#if defined(CONFIG_NETMAP_PTNETMAP_GUEST)
-#define WITH_PTNETMAP_GUEST
-#endif
-#if defined(CONFIG_NETMAP_PTNETMAP_HOST)
-#define WITH_PTNETMAP_HOST
+#if defined(CONFIG_NETMAP_PTNETMAP)
+#define WITH_PTNETMAP
 #endif
 #if defined(CONFIG_NETMAP_SINK)
 #define WITH_SINK
 #endif
+#if defined(CONFIG_NETMAP_NULL)
+#define WITH_NMNULL
+#endif
 
 #elif defined (_WIN32)
 #define WITH_VALE	// comment out to disable VALE support
 #define WITH_PIPES
 #define WITH_MONITOR
 #define WITH_GENERIC
+#define WITH_NMNULL
 
 #else	/* neither linux nor windows */
 #define WITH_VALE	// comment out to disable VALE support
 #define WITH_PIPES
 #define WITH_MONITOR
 #define WITH_GENERIC
-#define WITH_PTNETMAP_HOST	/* ptnetmap host support */
-#define WITH_PTNETMAP_GUEST	/* ptnetmap guest support */
-
+#define WITH_EXTMEM
+#define WITH_NMNULL
 #endif
 
 #if defined(__FreeBSD__)
@@ -88,6 +91,7 @@
 #define NM_MTX_INIT(m)		sx_init(&(m), #m)
 #define NM_MTX_DESTROY(m)	sx_destroy(&(m))
 #define NM_MTX_LOCK(m)		sx_xlock(&(m))
+#define NM_MTX_SPINLOCK(m)	while (!sx_try_xlock(&(m))) ;
 #define NM_MTX_UNLOCK(m)	sx_xunlock(&(m))
 #define NM_MTX_ASSERT(m)	sx_assert(&(m), SA_XLOCKED)
 
@@ -98,45 +102,35 @@
 #define MBUF_TRANSMIT(na, ifp, m)	((na)->if_transmit(ifp, m))
 #define	GEN_TX_MBUF_IFP(m)	((m)->m_pkthdr.rcvif)
 
-#define NM_ATOMIC_T	volatile int	// XXX ?
+#define NM_ATOMIC_T	volatile int /* required by atomic/bitops.h */
 /* atomic operations */
 #include 
 #define NM_ATOMIC_TEST_AND_SET(p)       (!atomic_cmpset_acq_int((p), 0, 1))
 #define NM_ATOMIC_CLEAR(p)              atomic_store_rel_int((p), 0)
 
-#if __FreeBSD_version >= 1100030
-#define	WNA(_ifp)	(_ifp)->if_netmap
-#else /* older FreeBSD */
-#define	WNA(_ifp)	(_ifp)->if_pspare[0]
-#endif /* older FreeBSD */
-
-#if __FreeBSD_version >= 1100005
 struct netmap_adapter *netmap_getna(if_t ifp);
-#endif
 
-#if __FreeBSD_version >= 1100027
 #define MBUF_REFCNT(m)		((m)->m_ext.ext_count)
 #define SET_MBUF_REFCNT(m, x)   (m)->m_ext.ext_count = x
-#else
-#define MBUF_REFCNT(m)		((m)->m_ext.ref_cnt ? *((m)->m_ext.ref_cnt) : -1)
-#define SET_MBUF_REFCNT(m, x)   *((m)->m_ext.ref_cnt) = x
-#endif
 
 #define MBUF_QUEUED(m)		1
 
 struct nm_selinfo {
+	/* Support for select(2) and poll(2). */
 	struct selinfo si;
+	/* Support for kqueue(9). See comments in netmap_freebsd.c */
+	struct taskqueue *ntfytq;
+	struct task ntfytask;
 	struct mtx m;
+	char mtxname[32];
+	int kqueue_users;
 };
 
 
-// XXX linux struct, not used in FreeBSD
-struct net_device_ops {
-};
-struct ethtool_ops {
-};
 struct hrtimer {
+    /* Not used in FreeBSD. */
 };
+
 #define NM_BNS_GET(b)
 #define NM_BNS_PUT(b)
 
@@ -154,7 +148,7 @@ struct hrtimer {
 	})
 
 /* See explanation in nm_os_generic_xmit_frame. */
-#define	GEN_TX_MBUF_IFP(m)	((struct ifnet *)skb_shinfo(m)->destructor_arg)
+#define	GEN_TX_MBUF_IFP(m)	((if_t)skb_shinfo(m)->destructor_arg)
 
 #define NM_ATOMIC_T	volatile long unsigned int
 
@@ -200,14 +194,6 @@ struct hrtimer {
 #define NETMAP_KERNEL_XCHANGE_POINTERS		_IO('i', 180)
 #define NETMAP_KERNEL_SEND_SHUTDOWN_SIGNAL	_IO_direct('i', 195)
 
-//Empty data structures are not permitted by MSVC compiler
-//XXX_ale, try to solve this problem
-struct net_device_ops{
-	char data[1];
-};
-typedef struct ethtool_ops{
-	char data[1];
-};
 typedef struct hrtimer{
 	KTIMER timer;
 	BOOLEAN active;
@@ -243,18 +229,40 @@ typedef struct hrtimer{
 #define	NMG_UNLOCK()		NM_MTX_UNLOCK(netmap_global_lock)
 #define	NMG_LOCK_ASSERT()	NM_MTX_ASSERT(netmap_global_lock)
 
-#define ND(format, ...)
-#define D(format, ...)						\
+#if defined(__FreeBSD__)
+#define nm_prerr_int	printf
+#define nm_prinf_int	printf
+#elif defined (_WIN32)
+#define nm_prerr_int	DbgPrint
+#define nm_prinf_int	DbgPrint
+#elif defined(linux)
+#define nm_prerr_int(fmt, arg...)    printk(KERN_ERR fmt, ##arg)
+#define nm_prinf_int(fmt, arg...)    printk(KERN_INFO fmt, ##arg)
+#endif
+
+#define nm_prinf(format, ...)					\
 	do {							\
 		struct timeval __xxts;				\
 		microtime(&__xxts);				\
-		printf("%03d.%06d [%4d] %-25s " format "\n",	\
+		nm_prinf_int("%03d.%06d [%4d] %-25s " format "\n",\
 		(int)__xxts.tv_sec % 1000, (int)__xxts.tv_usec,	\
 		__LINE__, __FUNCTION__, ##__VA_ARGS__);		\
 	} while (0)
 
-/* rate limited, lps indicates how many per second */
-#define RD(lps, format, ...)					\
+#define nm_prerr(format, ...)					\
+	do {							\
+		struct timeval __xxts;				\
+		microtime(&__xxts);				\
+		nm_prerr_int("%03d.%06d [%4d] %-25s " format "\n",\
+		(int)__xxts.tv_sec % 1000, (int)__xxts.tv_usec,	\
+		__LINE__, __FUNCTION__, ##__VA_ARGS__);		\
+	} while (0)
+
+/* Disabled printf (used to be nm_prdis). */
+#define nm_prdis(format, ...)
+
+/* Rate limited, lps indicates how many per second. */
+#define nm_prlim(lps, format, ...)				\
 	do {							\
 		static int t0, __cnt;				\
 		if (t0 != time_second) {			\
@@ -262,16 +270,17 @@ typedef struct hrtimer{
 			__cnt = 0;				\
 		}						\
 		if (__cnt++ < lps)				\
-			D(format, ##__VA_ARGS__);		\
+			nm_prinf(format, ##__VA_ARGS__);	\
 	} while (0)
 
 struct netmap_adapter;
 struct nm_bdg_fwd;
 struct nm_bridge;
 struct netmap_priv_d;
+struct nm_bdg_args;
 
-/* os-specific NM_SELINFO_T initialzation/destruction functions */
-void nm_os_selinfo_init(NM_SELINFO_T *);
+/* os-specific NM_SELINFO_T initialization/destruction functions */
+int nm_os_selinfo_init(NM_SELINFO_T *, const char *name);
 void nm_os_selinfo_uninit(NM_SELINFO_T *);
 
 const char *nm_dump_buf(char *p, int len, int lim, char *dst);
@@ -284,16 +293,26 @@ void nm_os_ifnet_fini(void);
 void nm_os_ifnet_lock(void);
 void nm_os_ifnet_unlock(void);
 
+unsigned nm_os_ifnet_mtu(if_t ifp);
+
 void nm_os_get_module(void);
 void nm_os_put_module(void);
 
-void netmap_make_zombie(struct ifnet *);
-void netmap_undo_zombie(struct ifnet *);
+void netmap_make_zombie(if_t);
+void netmap_undo_zombie(if_t);
 
 /* os independent alloc/realloc/free */
 void *nm_os_malloc(size_t);
+void *nm_os_vmalloc(size_t);
 void *nm_os_realloc(void *, size_t new_size, size_t old_size);
 void nm_os_free(void *);
+void nm_os_vfree(void *);
+
+/* os specific attach/detach enter/exit-netmap-mode routines */
+void nm_os_onattach(if_t);
+void nm_os_ondetach(if_t);
+void nm_os_onenter(if_t);
+void nm_os_onexit(if_t);
 
 /* passes a packet up to the host stack.
  * If the packet is sent (or dropped) immediately it returns NULL,
@@ -301,9 +320,10 @@ void nm_os_free(void *);
  * In this case, a final call with m=NULL and prev != NULL will send up
  * the entire chain to the host stack.
  */
-void *nm_os_send_up(struct ifnet *, struct mbuf *m, struct mbuf *prev);
+void *nm_os_send_up(if_t, struct mbuf *m, struct mbuf *prev);
 
-int nm_os_mbuf_has_offld(struct mbuf *m);
+int nm_os_mbuf_has_seg_offld(struct mbuf *m);
+int nm_os_mbuf_has_csum_offld(struct mbuf *m);
 
 #include "netmap_mbq.h"
 
@@ -357,10 +377,6 @@ struct netmap_zmon_list {
  * 	the next empty buffer as known by the hardware (next_to_check or so).
  * TX rings: hwcur + hwofs coincides with next_to_send
  *
- * For received packets, slot->flags is set to nkr_slot_flags
- * so we can provide a proper initial value (e.g. set NS_FORWARD
- * when operating in 'transparent' mode).
- *
  * The following fields are used to implement lock-free copy of packets
  * from input to output ports in VALE switch:
  *	nkr_hwlease	buffer after the last one being copied.
@@ -389,10 +405,11 @@ struct netmap_zmon_list {
  * RX rings attached to the VALE switch are accessed by both senders
  * and receiver. They are protected through the q_lock on the RX ring.
  */
+#define NM_KRING_ALIGNMENT 64
 struct netmap_kring {
 	struct netmap_ring	*ring;
 
-	uint32_t	nr_hwcur;
+	uint32_t	nr_hwcur;  /* should be nr_hwhead */
 	uint32_t	nr_hwtail;
 
 	/*
@@ -414,6 +431,8 @@ struct netmap_kring {
 					 * (used internally by pipes and
 					 *  by ptnetmap host ports)
 					 */
+#define NKR_NOINTR      0x10            /* don't use interrupts on this ring */
+#define NKR_FAKERING	0x20		/* don't allocate/free buffers */
 
 	uint32_t	nr_mode;
 	uint32_t	nr_pending_mode;
@@ -426,10 +445,16 @@ struct netmap_kring {
 	 * On a NIC reset, the NIC ring indexes may be reset but the
 	 * indexes in the netmap rings remain the same. nkr_hwofs
 	 * keeps track of the offset between the two.
+	 *
+	 * Moreover, during reset, we can restore only the subset of
+	 * the NIC ring that corresponds to the kernel-owned part of
+	 * the netmap ring. The rest of the slots must be restored
+	 * by the *sync routines when the user releases more slots.
+	 * The nkr_to_refill field keeps track of the number of slots
+	 * that still need to be restored.
 	 */
 	int32_t		nkr_hwofs;
-
-	uint16_t	nkr_slot_flags;	/* initial value for flags */
+	int32_t		nkr_to_refill;
 
 	/* last_reclaim is opaque marker to help reduce the frequency
 	 * of operations such as reclaiming tx buffers. A possible use
@@ -442,8 +467,15 @@ struct netmap_kring {
 	NM_LOCK_T	q_lock;		/* protects kring and ring. */
 	NM_ATOMIC_T	nr_busy;	/* prevent concurrent syscalls */
 
+	/* the adapter the owns this kring */
 	struct netmap_adapter *na;
 
+	/* the adapter that wants to be notified when this kring has
+	 * new slots available. This is usually the same as the above,
+	 * but wrappers may let it point to themselves
+	 */
+	struct netmap_adapter *notify_na;
+
 	/* The following fields are for VALE switch support */
 	struct nm_bdg_fwd *nkr_ft;
 	uint32_t	*nkr_leases;
@@ -494,11 +526,40 @@ struct netmap_kring {
 	struct netmap_kring *pipe;	/* if this is a pipe ring,
 					 * pointer to the other end
 					 */
+	uint32_t pipe_tail;		/* hwtail updated by the other end */
 #endif /* WITH_PIPES */
 
-#ifdef WITH_VALE
+	/* mask for the offset-related part of the ptr field in the slots */
+	uint64_t offset_mask;
+	/* maximum user-specified offset, as stipulated at bind time.
+	 * Larger offset requests will be silently capped to offset_max.
+	 */
+	uint64_t offset_max;
+	/* minimum gap between two consecutive offsets into the same
+	 * buffer, as stipulated at bind time. This is used to choose
+	 * the hwbuf_len, but is not otherwise checked for compliance
+	 * at runtime.
+	 */
+	uint64_t offset_gap;
+
+	/* size of hardware buffer. This may be less than the size of
+	 * the netmap buffers because of non-zero offsets, or because
+	 * the netmap buffer size exceeds the capability of the hardware.
+	 */
+	uint64_t hwbuf_len;
+
+	/* required alignment (in bytes) for the buffers used by this ring.
+	 * Netmap buffers are aligned to cachelines, which should suffice
+	 * for most NICs. If the user is passing offsets, though, we need
+	 * to check that the resulting buf address complies with any
+	 * alignment restriction.
+	 */
+	uint64_t buf_align;
+
+	/* hardware specific logic for the selection of the hwbuf_len */
+	int (*nm_bufcfg)(struct netmap_kring *kring, uint64_t target);
+
 	int (*save_notify)(struct netmap_kring *kring, int flags);
-#endif
 
 #ifdef WITH_MONITOR
 	/* array of krings that are monitoring this kring */
@@ -522,9 +583,9 @@ struct netmap_kring {
 #endif
 }
 #ifdef _WIN32
-__declspec(align(64));
+__declspec(align(NM_KRING_ALIGNMENT));
 #else
-__attribute__((__aligned__(64)));
+__attribute__((__aligned__(NM_KRING_ALIGNMENT)));
 #endif
 
 /* return 1 iff the kring needs to be turned on */
@@ -567,7 +628,7 @@ nm_prev(uint32_t i, uint32_t lim)
 
       +-----------------+            +-----------------+
       |                 |            |                 |
-      |XXX free slot XXX|            |XXX free slot XXX|
+      |      free       |            |      free       |
       +-----------------+            +-----------------+
 head->| owned by user   |<-hwcur     | not sent to nic |<-hwcur
       |                 |            | yet             |
@@ -608,14 +669,37 @@ tail->|                 |<-hwtail    |                 |<-hwlease
  *    a circular array where completions should be reported.
  */
 
+struct lut_entry;
+#ifdef __FreeBSD__
+#define plut_entry lut_entry
+#endif
 
 struct netmap_lut {
 	struct lut_entry *lut;
+	struct plut_entry *plut;
 	uint32_t objtotal;	/* max buffer index */
 	uint32_t objsize;	/* buffer size */
 };
 
 struct netmap_vp_adapter; // forward
+struct nm_bridge;
+
+/* Struct to be filled by nm_config callbacks. */
+struct nm_config_info {
+	unsigned num_tx_rings;
+	unsigned num_rx_rings;
+	unsigned num_tx_descs;
+	unsigned num_rx_descs;
+	unsigned rx_buf_maxsize;
+};
+
+/*
+ * default type for the magic field.
+ * May be overridden in glue code.
+ */
+#ifndef NM_OS_MAGIC
+#define NM_OS_MAGIC uint32_t
+#endif /* !NM_OS_MAGIC */
 
 /*
  * The "struct netmap_adapter" extends the "struct adapter"
@@ -633,7 +717,7 @@ struct netmap_adapter {
 	 * always exists and is at least 32 bits) contains a magic
 	 * value which we can use to detect that the interface is good.
 	 */
-	uint32_t magic;
+	NM_OS_MAGIC magic;
 	uint32_t na_flags;	/* enabled, and other flags */
 #define NAF_SKIP_INTR	1	/* use the regular interrupt handler.
 				 * useful during initialization
@@ -657,7 +741,10 @@ struct netmap_adapter {
 				 */
 #define NAF_HOST_RINGS  64	/* the adapter supports the host rings */
 #define NAF_FORCE_NATIVE 128	/* the adapter is always NATIVE */
-#define NAF_PTNETMAP_HOST 256	/* the adapter supports ptnetmap in the host */
+/* free */
+#define NAF_MOREFRAG	512	/* the adapter supports NS_MOREFRAG */
+#define NAF_OFFSETS	1024	/* the adapter supports the slot offsets */
+#define NAF_HOST_ALL	2048	/* the adapter wants as many host rings as hw */
 #define NAF_ZOMBIE	(1U<<30) /* the nic driver has been unloaded */
 #define	NAF_BUSY	(1U<<31) /* the adapter is used internally and
 				  * cannot be registered from userspace
@@ -668,16 +755,18 @@ struct netmap_adapter {
 
 	u_int num_rx_rings; /* number of adapter receive rings */
 	u_int num_tx_rings; /* number of adapter transmit rings */
+	u_int num_host_rx_rings; /* number of host receive rings */
+	u_int num_host_tx_rings; /* number of host transmit rings */
 
 	u_int num_tx_desc;  /* number of descriptor in each queue */
 	u_int num_rx_desc;
 
-	/* tx_rings and rx_rings are private but allocated
-	 * as a contiguous chunk of memory. Each array has
-	 * N+1 entries, for the adapter queues and for the host queue.
+	/* tx_rings and rx_rings are private but allocated as a
+	 * contiguous chunk of memory. Each array has N+K entries,
+	 * N for the hardware rings and K for the host rings.
 	 */
-	struct netmap_kring *tx_rings; /* array of TX rings. */
-	struct netmap_kring *rx_rings; /* array of RX rings. */
+	struct netmap_kring **tx_rings; /* array of TX rings. */
+	struct netmap_kring **rx_rings; /* array of RX rings. */
 
 	void *tailroom;		       /* space below the rings array */
 				       /* (used for leases) */
@@ -693,15 +782,14 @@ struct netmap_adapter {
 	/* copy of if_qflush and if_transmit pointers, to intercept
 	 * packets from the network stack when netmap is active.
 	 */
-	int     (*if_transmit)(struct ifnet *, struct mbuf *);
+	int     (*if_transmit)(if_t, struct mbuf *);
 
 	/* copy of if_input for netmap_send_up() */
-	void     (*if_input)(struct ifnet *, struct mbuf *);
+	void     (*if_input)(if_t, struct mbuf *);
 
-	/* references to the ifnet and device routines, used by
-	 * the generic netmap functions.
-	 */
-	struct ifnet *ifp; /* adapter is ifp->if_softc */
+	/* Back reference to the parent ifnet struct. Used for
+	 * hardware ports (emulated netmap included). */
+	if_t ifp; /* adapter is if_getsoftc(ifp) */
 
 	/*---- callbacks for this netmap adapter -----*/
 	/*
@@ -720,6 +808,22 @@ struct netmap_adapter {
 	 * nm_config() returns configuration information from the OS
 	 *	Called with NMG_LOCK held.
 	 *
+	 * nm_bufcfg()
+	 *      the purpose of this callback is to fill the kring->hwbuf_len
+	 *      (l) and kring->buf_align fields. The l value is most important
+	 *      for RX rings, where we want to disallow writes outside of the
+	 *      netmap buffer. The l value must be computed taking into account
+	 *      the stipulated max_offset (o), possibly increased if there are
+	 *      alignment constraints, the maxframe (m), if known, and the
+	 *      current NETMAP_BUF_SIZE (b) of the memory region used by the
+	 *      adapter. We want the largest supported l such that o + l <= b.
+	 *      If m is known to be <= b - o, the callback may also choose the
+	 *      largest l <= m, ignoring the offset.  The buf_align field is
+	 *      most important for TX rings when there are offsets.  The user
+	 *      will see this value in the ring->buf_align field.  Misaligned
+	 *      offsets will cause the corresponding packets to be silently
+	 *      dropped.
+	 *
 	 * nm_krings_create() create and init the tx_rings and
 	 * 	rx_rings arrays of kring structures. In particular,
 	 * 	set the nm_sync callbacks for each ring.
@@ -749,15 +853,14 @@ struct netmap_adapter {
 	int (*nm_txsync)(struct netmap_kring *kring, int flags);
 	int (*nm_rxsync)(struct netmap_kring *kring, int flags);
 	int (*nm_notify)(struct netmap_kring *kring, int flags);
+	int (*nm_bufcfg)(struct netmap_kring *kring, uint64_t target);
 #define NAF_FORCE_READ      1
 #define NAF_FORCE_RECLAIM   2
 #define NAF_CAN_FORWARD_DOWN 4
 	/* return configuration information */
-	int (*nm_config)(struct netmap_adapter *,
-		u_int *txr, u_int *txd, u_int *rxr, u_int *rxd);
+	int (*nm_config)(struct netmap_adapter *, struct nm_config_info *info);
 	int (*nm_krings_create)(struct netmap_adapter *);
 	void (*nm_krings_delete)(struct netmap_adapter *);
-#ifdef WITH_VALE
 	/*
 	 * nm_bdg_attach() initializes the na_vp field to point
 	 *      to an adapter that can be attached to a VALE switch. If the
@@ -773,15 +876,15 @@ struct netmap_adapter {
 	 *      initializations
 	 *      Called with NMG_LOCK held.
 	 */
-	int (*nm_bdg_attach)(const char *bdg_name, struct netmap_adapter *);
-	int (*nm_bdg_ctl)(struct netmap_adapter *, struct nmreq *, int);
+	int (*nm_bdg_attach)(const char *bdg_name, struct netmap_adapter *,
+			struct nm_bridge *);
+	int (*nm_bdg_ctl)(struct nmreq_header *, struct netmap_adapter *);
 
 	/* adapter used to attach this adapter to a VALE switch (if any) */
 	struct netmap_vp_adapter *na_vp;
 	/* adapter used to attach the host rings of this adapter
 	 * to a VALE switch (if any) */
 	struct netmap_vp_adapter *na_hostvp;
-#endif
 
 	/* standard refcount to control the lifetime of the adapter
 	 * (it should be equal to the lifetime of the corresponding ifp)
@@ -793,6 +896,7 @@ struct netmap_adapter {
 	 * buffer addresses, the total number of buffers and the buffer size.
 	 */
  	struct netmap_mem_d *nm_mem;
+	struct netmap_mem_d *nm_mem_prev;
 	struct netmap_lut na_lut;
 
 	/* additional information attached to this adapter
@@ -809,7 +913,17 @@ struct netmap_adapter {
 	/* Offset of ethernet header for each packet. */
 	u_int virt_hdr_len;
 
-	char name[64];
+	/* Max number of bytes that the NIC can store in the buffer
+	 * referenced by each RX descriptor. This translates to the maximum
+	 * bytes that a single netmap slot can reference. Larger packets
+	 * require NS_MOREFRAG support. */
+	unsigned rx_buf_maxsize;
+
+	char name[NETMAP_REQ_IFNAMSIZ]; /* used at least by pipes */
+
+#ifdef WITH_MONITOR
+	unsigned long	monitor_id;	/* debugging */
+#endif
 };
 
 static __inline u_int
@@ -833,6 +947,12 @@ nma_get_nrings(struct netmap_adapter *na, enum txrx t)
 	return (t == NR_TX ? na->num_tx_rings : na->num_rx_rings);
 }
 
+static __inline u_int
+nma_get_host_nrings(struct netmap_adapter *na, enum txrx t)
+{
+	return (t == NR_TX ? na->num_host_tx_rings : na->num_host_rx_rings);
+}
+
 static __inline void
 nma_set_nrings(struct netmap_adapter *na, enum txrx t, u_int v)
 {
@@ -842,12 +962,23 @@ nma_set_nrings(struct netmap_adapter *na, enum txrx t, u_int v)
 		na->num_rx_rings = v;
 }
 
-static __inline struct netmap_kring*
+static __inline void
+nma_set_host_nrings(struct netmap_adapter *na, enum txrx t, u_int v)
+{
+	if (t == NR_TX)
+		na->num_host_tx_rings = v;
+	else
+		na->num_host_rx_rings = v;
+}
+
+static __inline struct netmap_kring**
 NMR(struct netmap_adapter *na, enum txrx t)
 {
 	return (t == NR_TX ? na->tx_rings : na->rx_rings);
 }
 
+int nma_intr_enable(struct netmap_adapter *na, int onoff);
+
 /*
  * If the NIC is owned by the kernel
  * (i.e., bridge), neither another bridge nor user can use it;
@@ -885,8 +1016,10 @@ struct netmap_vp_adapter {	/* VALE software port */
 struct netmap_hw_adapter {	/* physical device */
 	struct netmap_adapter up;
 
-	struct net_device_ops nm_ndo;	// XXX linux only
-	struct ethtool_ops    nm_eto;	// XXX linux only
+#ifdef linux
+	struct net_device_ops nm_ndo;
+	struct ethtool_ops    nm_eto;
+#endif
 	const struct ethtool_ops*   save_ethtool;
 
 	int (*nm_hw_register)(struct netmap_adapter *, int onoff);
@@ -907,17 +1040,12 @@ struct netmap_generic_adapter {	/* emulated device */
 	/* Pointer to a previously used netmap adapter. */
 	struct netmap_adapter *prev;
 
-	/* generic netmap adapters support:
-	 * a net_device_ops struct overrides ndo_select_queue(),
-	 * save_if_input saves the if_input hook (FreeBSD),
-	 * mit implements rx interrupt mitigation,
+	/* Emulated netmap adapters support:
+	 *  - mit implements rx interrupt mitigation;
 	 */
-	struct net_device_ops generic_ndo;
-	void (*save_if_input)(struct ifnet *, struct mbuf *);
-
 	struct nm_generic_mit *mit;
 #ifdef linux
-        netdev_tx_t (*save_start_xmit)(struct mbuf *, struct ifnet *);
+        netdev_tx_t (*save_start_xmit)(struct mbuf *, if_t);
 #endif
 	/* Is the adapter able to use multiple RX slots to scatter
 	 * each packet pushed up by the driver? */
@@ -929,13 +1057,22 @@ struct netmap_generic_adapter {	/* emulated device */
 };
 #endif  /* WITH_GENERIC */
 
-static __inline int
+static __inline u_int
 netmap_real_rings(struct netmap_adapter *na, enum txrx t)
 {
-	return nma_get_nrings(na, t) + !!(na->na_flags & NAF_HOST_RINGS);
+	return nma_get_nrings(na, t) +
+		!!(na->na_flags & NAF_HOST_RINGS) * nma_get_host_nrings(na, t);
 }
 
-#ifdef WITH_VALE
+/* account for fake rings */
+static __inline u_int
+netmap_all_rings(struct netmap_adapter *na, enum txrx t)
+{
+	return max(nma_get_nrings(na, t) + 1, netmap_real_rings(na, t));
+}
+
+int netmap_default_bdg_attach(const char *name, struct netmap_adapter *na,
+		struct nm_bridge *);
 struct nm_bdg_polling_state;
 /*
  * Bridge wrapper for non VALE ports attached to a VALE switch.
@@ -995,12 +1132,24 @@ struct netmap_bwrap_adapter {
 	 */
 	struct netmap_priv_d *na_kpriv;
 	struct nm_bdg_polling_state *na_polling_state;
+	/* we overwrite the hwna->na_vp pointer, so we save
+	 * here its original value, to be restored at detach
+	 */
+	struct netmap_vp_adapter *saved_na_vp;
+	int (*nm_intr_notify)(struct netmap_kring *kring, int flags);
 };
-int netmap_bwrap_attach(const char *name, struct netmap_adapter *);
-int netmap_vi_create(struct nmreq *, int);
+int nm_is_bwrap(struct netmap_adapter *na);
+int nm_bdg_polling(struct nmreq_header *hdr);
 
+int netmap_bdg_attach(struct nmreq_header *hdr, void *auth_token);
+int netmap_bdg_detach(struct nmreq_header *hdr, void *auth_token);
+#ifdef WITH_VALE
+int netmap_vale_list(struct nmreq_header *hdr);
+int netmap_vi_create(struct nmreq_header *hdr, int);
+int nm_vi_create(struct nmreq_header *);
+int nm_vi_destroy(const char *name);
 #else /* !WITH_VALE */
-#define netmap_vi_create(nmr, a) (EOPNOTSUPP)
+#define netmap_vi_create(hdr, a) (EOPNOTSUPP)
 #endif /* WITH_VALE */
 
 #ifdef WITH_PIPES
@@ -1008,21 +1157,29 @@ int netmap_vi_create(struct nmreq *, int);
 #define NM_MAXPIPES 	64	/* max number of pipes per adapter */
 
 struct netmap_pipe_adapter {
+	/* pipe identifier is up.name */
 	struct netmap_adapter up;
 
-	u_int id; 	/* pipe identifier */
-	int role;	/* either NR_REG_PIPE_MASTER or NR_REG_PIPE_SLAVE */
+#define NM_PIPE_ROLE_MASTER	0x1
+#define NM_PIPE_ROLE_SLAVE	0x2
+	int role;	/* either NM_PIPE_ROLE_MASTER or NM_PIPE_ROLE_SLAVE */
 
 	struct netmap_adapter *parent; /* adapter that owns the memory */
 	struct netmap_pipe_adapter *peer; /* the other end of the pipe */
 	int peer_ref;		/* 1 iff we are holding a ref to the peer */
-	struct ifnet *parent_ifp;	/* maybe null */
+	if_t parent_ifp;	/* maybe null */
 
 	u_int parent_slot; /* index in the parent pipe array */
 };
 
 #endif /* WITH_PIPES */
 
+#ifdef WITH_NMNULL
+struct netmap_null_adapter {
+	struct netmap_adapter up;
+};
+#endif /* WITH_NMNULL */
+
 
 /* return slots reserved to rx clients; used in drivers */
 static inline uint32_t
@@ -1031,7 +1188,7 @@ nm_kr_rxspace(struct netmap_kring *k)
 	int space = k->nr_hwtail - k->nr_hwcur;
 	if (space < 0)
 		space += k->nkr_num_slots;
-	ND("preserving %d rx slots %d -> %d", space, k->nr_hwcur, k->nr_hwtail);
+	nm_prdis("preserving %d rx slots %d -> %d", space, k->nr_hwcur, k->nr_hwtail);
 
 	return space;
 }
@@ -1044,13 +1201,22 @@ nm_kr_rxspace(struct netmap_kring *k)
 static inline int
 nm_kr_txempty(struct netmap_kring *kring)
 {
-	return kring->rcur == kring->nr_hwtail;
+	return kring->rhead == kring->nr_hwtail;
 }
 
 /* True if no more completed slots in the rx ring, only valid after
  * rxsync_prologue */
 #define nm_kr_rxempty(_k)	nm_kr_txempty(_k)
 
+/* True if the application needs to wait for more space on the ring
+ * (more received packets or more free tx slots).
+ * Only valid after *xsync_prologue. */
+static inline int
+nm_kr_wouldblock(struct netmap_kring *kring)
+{
+	return kring->rcur == kring->nr_hwtail;
+}
+
 /*
  * protect against multiple threads using the same ring.
  * also check that the ring has not been stopped or locked
@@ -1173,11 +1339,13 @@ static __inline void nm_kr_start(struct netmap_kring *kr)
  *	virtual ports (vale, pipes, monitor)
  */
 int netmap_attach(struct netmap_adapter *);
-void netmap_detach(struct ifnet *);
-int netmap_transmit(struct ifnet *, struct mbuf *);
+int netmap_attach_ext(struct netmap_adapter *, size_t size, int override_reg);
+void netmap_detach(if_t);
+int netmap_transmit(if_t, struct mbuf *);
 struct netmap_slot *netmap_reset(struct netmap_adapter *na,
 	enum txrx tx, u_int n, u_int new_cur);
 int netmap_ring_reinit(struct netmap_kring *);
+int netmap_rings_config_get(struct netmap_adapter *, struct nm_config_info *);
 
 /* Return codes for netmap_*x_irq. */
 enum {
@@ -1196,24 +1364,22 @@ enum {
 };
 
 /* default functions to handle rx/tx interrupts */
-int netmap_rx_irq(struct ifnet *, u_int, u_int *);
+int netmap_rx_irq(if_t, u_int, u_int *);
 #define netmap_tx_irq(_n, _q) netmap_rx_irq(_n, _q, NULL)
 int netmap_common_irq(struct netmap_adapter *, u_int, u_int *work_done);
 
-
+const char *netmap_bdg_name(struct netmap_vp_adapter *);
 #ifdef WITH_VALE
 /* functions used by external modules to interface with VALE */
 #define netmap_vp_to_ifp(_vp)	((_vp)->up.ifp)
 #define netmap_ifp_to_vp(_ifp)	(NA(_ifp)->na_vp)
 #define netmap_ifp_to_host_vp(_ifp) (NA(_ifp)->na_hostvp)
 #define netmap_bdg_idx(_vp)	((_vp)->bdg_port)
-const char *netmap_bdg_name(struct netmap_vp_adapter *);
 #else /* !WITH_VALE */
 #define netmap_vp_to_ifp(_vp)	NULL
 #define netmap_ifp_to_vp(_ifp)	NULL
 #define netmap_ifp_to_host_vp(_ifp) NULL
 #define netmap_bdg_idx(_vp)	-1
-#define netmap_bdg_name(_vp)	NULL
 #endif /* WITH_VALE */
 
 static inline int
@@ -1228,83 +1394,34 @@ nm_native_on(struct netmap_adapter *na)
 	return nm_netmap_on(na) && (na->na_flags & NAF_NATIVE);
 }
 
-static inline int
-nm_iszombie(struct netmap_adapter *na)
+static inline struct netmap_kring *
+netmap_kring_on(struct netmap_adapter *na, u_int q, enum txrx t)
 {
-	return na == NULL || (na->na_flags & NAF_ZOMBIE);
-}
-
-static inline void
-nm_update_hostrings_mode(struct netmap_adapter *na)
-{
-	/* Process nr_mode and nr_pending_mode for host rings. */
-	na->tx_rings[na->num_tx_rings].nr_mode =
-		na->tx_rings[na->num_tx_rings].nr_pending_mode;
-	na->rx_rings[na->num_rx_rings].nr_mode =
-		na->rx_rings[na->num_rx_rings].nr_pending_mode;
-}
+	struct netmap_kring *kring = NULL;
 
-/* set/clear native flags and if_transmit/netdev_ops */
-static inline void
-nm_set_native_flags(struct netmap_adapter *na)
-{
-	struct ifnet *ifp = na->ifp;
+	if (!nm_native_on(na))
+		return NULL;
 
-	/* We do the setup for intercepting packets only if we are the
-	 * first user of this adapapter. */
-	if (na->active_fds > 0) {
-		return;
-	}
+	if (t == NR_RX && q < na->num_rx_rings)
+		kring = na->rx_rings[q];
+	else if (t == NR_TX && q < na->num_tx_rings)
+		kring = na->tx_rings[q];
+	else
+		return NULL;
 
-	na->na_flags |= NAF_NETMAP_ON;
-#ifdef IFCAP_NETMAP /* or FreeBSD ? */
-	ifp->if_capenable |= IFCAP_NETMAP;
-#endif
-#if defined (__FreeBSD__)
-	na->if_transmit = ifp->if_transmit;
-	ifp->if_transmit = netmap_transmit;
-#elif defined (_WIN32)
-	(void)ifp; /* prevent a warning */
-	//XXX_ale can we just comment those?
-	//na->if_transmit = ifp->if_transmit;
-	//ifp->if_transmit = netmap_transmit;
-#else
-	na->if_transmit = (void *)ifp->netdev_ops;
-	ifp->netdev_ops = &((struct netmap_hw_adapter *)na)->nm_ndo;
-	((struct netmap_hw_adapter *)na)->save_ethtool = ifp->ethtool_ops;
-	ifp->ethtool_ops = &((struct netmap_hw_adapter*)na)->nm_eto;
-#endif
-	nm_update_hostrings_mode(na);
+	return (kring->nr_mode == NKR_NETMAP_ON) ? kring : NULL;
 }
 
-static inline void
-nm_clear_native_flags(struct netmap_adapter *na)
+static inline int
+nm_iszombie(struct netmap_adapter *na)
 {
-	struct ifnet *ifp = na->ifp;
-
-	/* We undo the setup for intercepting packets only if we are the
-	 * last user of this adapapter. */
-	if (na->active_fds > 0) {
-		return;
-	}
+	return na == NULL || (na->na_flags & NAF_ZOMBIE);
+}
 
-	nm_update_hostrings_mode(na);
+void nm_set_native_flags(struct netmap_adapter *);
+void nm_clear_native_flags(struct netmap_adapter *);
 
-#if defined(__FreeBSD__)
-	ifp->if_transmit = na->if_transmit;
-#elif defined(_WIN32)
-	(void)ifp; /* prevent a warning */
-	//XXX_ale can we just comment those?
-	//ifp->if_transmit = na->if_transmit;
-#else
-	ifp->netdev_ops = (void *)na->if_transmit;
-	ifp->ethtool_ops = ((struct netmap_hw_adapter*)na)->save_ethtool;
-#endif
-	na->na_flags &= ~NAF_NETMAP_ON;
-#ifdef IFCAP_NETMAP /* or FreeBSD ? */
-	ifp->if_capenable &= ~IFCAP_NETMAP;
-#endif
-}
+void netmap_krings_mode_commit(struct netmap_adapter *na, int onoff);
 
 /*
  * nm_*sync_prologue() functions are used in ioctl/poll and ptnetmap
@@ -1333,7 +1450,7 @@ uint32_t nm_rxsync_prologue(struct netmap_kring *, struct netmap_ring *);
 #if 1 /* debug version */
 #define	NM_CHECK_ADDR_LEN(_na, _a, _l)	do {				\
 	if (_a == NETMAP_BUF_BASE(_na) || _l > NETMAP_BUF_SIZE(_na)) {	\
-		RD(5, "bad addr/len ring %d slot %d idx %d len %d",	\
+		nm_prlim(5, "bad addr/len ring %d slot %d idx %d len %d",	\
 			kring->ring_id, nm_i, slot->buf_idx, len);	\
 		if (_l > NETMAP_BUF_SIZE(_na))				\
 			_l = NETMAP_BUF_SIZE(_na);			\
@@ -1345,6 +1462,12 @@ uint32_t nm_rxsync_prologue(struct netmap_kring *, struct netmap_ring *);
 	} while (0)
 #endif
 
+#define NM_CHECK_ADDR_LEN_OFF(na_, l_, o_) do {				\
+	if ((l_) + (o_) < (l_) || 					\
+	    (l_) + (o_) > NETMAP_BUF_SIZE(na_)) {			\
+		(l_) = NETMAP_BUF_SIZE(na_) - (o_);			\
+	} } while (0)
+
 
 /*---------------------------------------------------------------*/
 /*
@@ -1360,12 +1483,10 @@ uint32_t nm_rxsync_prologue(struct netmap_kring *, struct netmap_ring *);
  * - provide defaults for the setup callbacks and the memory allocator
  */
 int netmap_attach_common(struct netmap_adapter *);
-/* common actions to be performed on netmap adapter destruction */
-void netmap_detach_common(struct netmap_adapter *);
 /* fill priv->np_[tr]xq{first,last} using the ringid and flags information
- * coming from a struct nmreq
+ * coming from a struct nmreq_register
  */
-int netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags);
+int netmap_interp_ringid(struct netmap_priv_d *priv, struct nmreq_header *hdr);
 /* update the ring parameters (number and size of tx and rx rings).
  * It calls the nm_config callback, if available.
  */
@@ -1378,6 +1499,15 @@ int netmap_update_config(struct netmap_adapter *na);
  * leasing-related data structures
  */
 int netmap_krings_create(struct netmap_adapter *na, u_int tailroom);
+
+/*
+ * tailroom must be properly aligned with nm_tailroom_align().
+ */
+static inline uint64_t
+nm_tailroom_align(uint64_t tr) {
+	return (tr + (NM_KRING_ALIGNMENT - 1)) & ~((uint64_t)NM_KRING_ALIGNMENT - 1);
+}
+
 /* deletes the kring array of the adapter. The array must have
  * been created using netmap_krings_create
  */
@@ -1395,100 +1525,83 @@ void netmap_set_ring(struct netmap_adapter *, u_int ring_id, enum txrx, int stop
 /* set the stopped/enabled status of all rings of the adapter. */
 void netmap_set_all_rings(struct netmap_adapter *, int stopped);
 /* convenience wrappers for netmap_set_all_rings */
-void netmap_disable_all_rings(struct ifnet *);
-void netmap_enable_all_rings(struct ifnet *);
+void netmap_disable_all_rings(if_t);
+void netmap_enable_all_rings(if_t);
 
+int netmap_buf_size_validate(const struct netmap_adapter *na, unsigned mtu);
 int netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
-	uint16_t ringid, uint32_t flags);
+		struct nmreq_header *);
 void netmap_do_unregif(struct netmap_priv_d *priv);
 
 u_int nm_bound_var(u_int *v, u_int dflt, u_int lo, u_int hi, const char *msg);
-int netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na,
-		  struct ifnet **ifp, struct netmap_mem_d *nmd, int create);
-void netmap_unget_na(struct netmap_adapter *na, struct ifnet *ifp);
-int netmap_get_hw_na(struct ifnet *ifp,
+int netmap_get_na(struct nmreq_header *hdr, struct netmap_adapter **na,
+		if_t *ifp, struct netmap_mem_d *nmd, int create);
+void netmap_unget_na(struct netmap_adapter *na, if_t ifp);
+int netmap_get_hw_na(if_t ifp,
 		struct netmap_mem_d *nmd, struct netmap_adapter **na);
-
+void netmap_mem_restore(struct netmap_adapter *na);
 
 #ifdef WITH_VALE
-/*
- * The following bridge-related functions are used by other
- * kernel modules.
- *
- * VALE only supports unicast or broadcast. The lookup
- * function can return 0 .. NM_BDG_MAXPORTS-1 for regular ports,
- * NM_BDG_MAXPORTS for broadcast, NM_BDG_MAXPORTS+1 for unknown.
- * XXX in practice "unknown" might be handled same as broadcast.
- */
-typedef u_int (*bdg_lookup_fn_t)(struct nm_bdg_fwd *ft, uint8_t *ring_nr,
-		struct netmap_vp_adapter *);
-typedef int (*bdg_config_fn_t)(struct nm_ifreq *);
-typedef void (*bdg_dtor_fn_t)(const struct netmap_vp_adapter *);
-struct netmap_bdg_ops {
-	bdg_lookup_fn_t lookup;
-	bdg_config_fn_t config;
-	bdg_dtor_fn_t	dtor;
-};
-
-u_int netmap_bdg_learning(struct nm_bdg_fwd *ft, uint8_t *dst_ring,
-		struct netmap_vp_adapter *);
-
-#define	NM_BRIDGES		8	/* number of bridges */
-#define	NM_BDG_MAXPORTS		254	/* up to 254 */
-#define	NM_BDG_BROADCAST	NM_BDG_MAXPORTS
-#define	NM_BDG_NOPORT		(NM_BDG_MAXPORTS+1)
+uint32_t netmap_vale_learning(struct nm_bdg_fwd *ft, uint8_t *dst_ring,
+		struct netmap_vp_adapter *, void *private_data);
 
 /* these are redefined in case of no VALE support */
-int netmap_get_bdg_na(struct nmreq *nmr, struct netmap_adapter **na,
+int netmap_get_vale_na(struct nmreq_header *hdr, struct netmap_adapter **na,
 		struct netmap_mem_d *nmd, int create);
-struct nm_bridge *netmap_init_bridges2(u_int);
-void netmap_uninit_bridges2(struct nm_bridge *, u_int);
-int netmap_init_bridges(void);
-void netmap_uninit_bridges(void);
-int netmap_bdg_ctl(struct nmreq *nmr, struct netmap_bdg_ops *bdg_ops);
-int netmap_bdg_config(struct nmreq *nmr);
+void *netmap_vale_create(const char *bdg_name, int *return_status);
+int netmap_vale_destroy(const char *bdg_name, void *auth_token);
+
+extern unsigned int vale_max_bridges;
 
 #else /* !WITH_VALE */
-#define	netmap_get_bdg_na(_1, _2, _3, _4)	0
-#define netmap_init_bridges(_1) 0
-#define netmap_uninit_bridges()
-#define	netmap_bdg_ctl(_1, _2)	EINVAL
+#define netmap_bdg_learning(_1, _2, _3, _4)	0
+#define	netmap_get_vale_na(_1, _2, _3, _4)	0
+#define netmap_bdg_create(_1, _2)	NULL
+#define netmap_bdg_destroy(_1, _2)	0
+#define vale_max_bridges		1
 #endif /* !WITH_VALE */
 
 #ifdef WITH_PIPES
 /* max number of pipes per device */
-#define NM_MAXPIPES	64	/* XXX how many? */
+#define NM_MAXPIPES	64	/* XXX this should probably be a sysctl */
 void netmap_pipe_dealloc(struct netmap_adapter *);
-int netmap_get_pipe_na(struct nmreq *nmr, struct netmap_adapter **na,
-		struct netmap_mem_d *nmd, int create);
+int netmap_get_pipe_na(struct nmreq_header *hdr, struct netmap_adapter **na,
+			struct netmap_mem_d *nmd, int create);
 #else /* !WITH_PIPES */
 #define NM_MAXPIPES	0
 #define netmap_pipe_alloc(_1, _2) 	0
 #define netmap_pipe_dealloc(_1)
-#define netmap_get_pipe_na(nmr, _2, _3, _4)	\
-	({ int role__ = (nmr)->nr_flags & NR_REG_MASK; \
-	   (role__ == NR_REG_PIPE_MASTER || 	       \
-	    role__ == NR_REG_PIPE_SLAVE) ? EOPNOTSUPP : 0; })
+#define netmap_get_pipe_na(hdr, _2, _3, _4)	\
+	((strchr(hdr->nr_name, '{') != NULL || strchr(hdr->nr_name, '}') != NULL) ? EOPNOTSUPP : 0)
 #endif
 
 #ifdef WITH_MONITOR
-int netmap_get_monitor_na(struct nmreq *nmr, struct netmap_adapter **na,
+int netmap_get_monitor_na(struct nmreq_header *hdr, struct netmap_adapter **na,
 		struct netmap_mem_d *nmd, int create);
 void netmap_monitor_stop(struct netmap_adapter *na);
 #else
-#define netmap_get_monitor_na(nmr, _2, _3, _4) \
-	((nmr)->nr_flags & (NR_MONITOR_TX | NR_MONITOR_RX) ? EOPNOTSUPP : 0)
+#define netmap_get_monitor_na(hdr, _2, _3, _4) \
+	(((struct nmreq_register *)(uintptr_t)hdr->nr_body)->nr_flags & (NR_MONITOR_TX | NR_MONITOR_RX) ? EOPNOTSUPP : 0)
 #endif
 
+#ifdef WITH_NMNULL
+int netmap_get_null_na(struct nmreq_header *hdr, struct netmap_adapter **na,
+		struct netmap_mem_d *nmd, int create);
+#else /* !WITH_NMNULL */
+#define netmap_get_null_na(hdr, _2, _3, _4) \
+	(((struct nmreq_register *)(uintptr_t)hdr->nr_body)->nr_flags & (NR_MONITOR_TX | NR_MONITOR_RX) ? EOPNOTSUPP : 0)
+#endif /* WITH_NMNULL */
+
 #ifdef CONFIG_NET_NS
 struct net *netmap_bns_get(void);
 void netmap_bns_put(struct net *);
 void netmap_bns_getbridges(struct nm_bridge **, u_int *);
 #else
+extern struct nm_bridge *nm_bridges;
 #define netmap_bns_get()
 #define netmap_bns_put(_1)
 #define netmap_bns_getbridges(b, n) \
-	do { *b = nm_bridges; *n = NM_BRIDGES; } while (0)
+	do { *b = nm_bridges; *n = vale_max_bridges; } while (0)
 #endif
 
 /* Various prototypes */
@@ -1498,7 +1611,11 @@ void netmap_fini(void);
 int netmap_get_memory(struct netmap_priv_d* p);
 void netmap_dtor(void *data);
 
-int netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread *);
+int netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
+		struct thread *, int nr_body_is_user);
+int netmap_ioctl_legacy(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
+			struct thread *td);
+size_t nmreq_size_by_type(uint16_t nr_reqtype);
 
 /* netmap_adapter creation/destruction */
 
@@ -1513,8 +1630,8 @@ void __netmap_adapter_get(struct netmap_adapter *na);
 #define netmap_adapter_get(na) 				\
 	do {						\
 		struct netmap_adapter *__na = na;	\
-		D("getting %p:%s (%d)", __na, (__na)->name, (__na)->na_refcount);	\
 		__netmap_adapter_get(__na);		\
+		nm_prinf("getting %p:%s -> %d", __na, (__na)->name, (__na)->na_refcount);	\
 	} while (0)
 
 int __netmap_adapter_put(struct netmap_adapter *na);
@@ -1522,8 +1639,11 @@ int __netmap_adapter_put(struct netmap_adapter *na);
 #define netmap_adapter_put(na)				\
 	({						\
 		struct netmap_adapter *__na = na;	\
-		D("putting %p:%s (%d)", __na, (__na)->name, (__na)->na_refcount);	\
-		__netmap_adapter_put(__na);		\
+		if (__na == NULL)			\
+			nm_prinf("putting NULL");	\
+		else					\
+			nm_prinf("putting %p:%s -> %d", __na, (__na)->name, (__na)->na_refcount - 1);	\
+		__netmap_adapter_put(__na);	\
 	})
 
 #else /* !NM_DEBUG_PUTGET */
@@ -1541,32 +1661,48 @@ int netmap_adapter_put(struct netmap_adapter *na);
 #define NETMAP_BUF_BASE(_na)	((_na)->na_lut.lut[0].vaddr)
 #define NETMAP_BUF_SIZE(_na)	((_na)->na_lut.objsize)
 extern int netmap_no_pendintr;
-extern int netmap_mitigate;
-extern int netmap_verbose;		/* for debugging */
-enum {                                  /* verbose flags */
-	NM_VERB_ON = 1,                 /* generic verbose */
-	NM_VERB_HOST = 0x2,             /* verbose host stack */
-	NM_VERB_RXSYNC = 0x10,          /* verbose on rxsync/txsync */
-	NM_VERB_TXSYNC = 0x20,
-	NM_VERB_RXINTR = 0x100,         /* verbose on rx/tx intr (driver) */
-	NM_VERB_TXINTR = 0x200,
-	NM_VERB_NIC_RXSYNC = 0x1000,    /* verbose on rx/tx intr (driver) */
-	NM_VERB_NIC_TXSYNC = 0x2000,
+extern int netmap_verbose;
+#ifdef CONFIG_NETMAP_DEBUG
+extern int netmap_debug;		/* for debugging */
+#else /* !CONFIG_NETMAP_DEBUG */
+#define netmap_debug (0)
+#endif /* !CONFIG_NETMAP_DEBUG */
+enum {                                  /* debug flags */
+	NM_DEBUG_ON = 1,		/* generic debug messages */
+	NM_DEBUG_HOST = 0x2,            /* debug host stack */
+	NM_DEBUG_RXSYNC = 0x10,         /* debug on rxsync/txsync */
+	NM_DEBUG_TXSYNC = 0x20,
+	NM_DEBUG_RXINTR = 0x100,        /* debug on rx/tx intr (driver) */
+	NM_DEBUG_TXINTR = 0x200,
+	NM_DEBUG_NIC_RXSYNC = 0x1000,   /* debug on rx/tx intr (driver) */
+	NM_DEBUG_NIC_TXSYNC = 0x2000,
+	NM_DEBUG_MEM = 0x4000,		/* verbose memory allocations/deallocations */
+	NM_DEBUG_VALE = 0x8000,		/* debug messages from memory allocators */
+	NM_DEBUG_BDG = NM_DEBUG_VALE,
 };
 
 extern int netmap_txsync_retry;
-extern int netmap_flags;
+extern int netmap_generic_hwcsum;
 extern int netmap_generic_mit;
 extern int netmap_generic_ringsize;
 extern int netmap_generic_rings;
+#ifdef linux
 extern int netmap_generic_txqdisc;
+#endif
 
 /*
- * NA returns a pointer to the struct netmap adapter from the ifp,
- * WNA is used to write it.
+ * NA returns a pointer to the struct netmap adapter from the ifp.
+ * The if_getnetmapadapter() and if_setnetmapadapter() helpers are
+ * os-specific and must be defined in glue code.
  */
-#define	NA(_ifp)	((struct netmap_adapter *)WNA(_ifp))
+#define	NA(_ifp)	(if_getnetmapadapter(_ifp))
 
+/*
+ * we provide a default implementation of NM_ATTACH_NA/NM_DETACH_NA
+ * based on the if_setnetmapadapter() setter function.
+ * Glue code may override this by defining its own NM_ATTACH_NA
+ */
+#ifndef NM_ATTACH_NA
 /*
  * On old versions of FreeBSD, NA(ifp) is a pspare. On linux we
  * overload another pointer in the netdev.
@@ -1580,11 +1716,17 @@ extern int netmap_generic_txqdisc;
 	((uint32_t)(uintptr_t)NA(ifp) ^ NA(ifp)->magic) == NETMAP_MAGIC )
 
 #define	NM_ATTACH_NA(ifp, na) do {					\
-	WNA(ifp) = na;							\
+	if_setnetmapadapter(ifp, na);					\
 	if (NA(ifp))							\
 		NA(ifp)->magic = 					\
 			((uint32_t)(uintptr_t)NA(ifp)) ^ NETMAP_MAGIC;	\
 } while(0)
+#define NM_RESTORE_NA(ifp, na) 	if_setnetmapadapter(ifp, na);
+
+#define NM_DETACH_NA(ifp)	do { if_setnetmapadapter(ifp, NULL); } while (0)
+#define NM_NA_CLASH(ifp)	(NA(ifp) && !NM_NA_VALID(ifp))
+#endif /* !NM_ATTACH_NA */
+
 
 #define NM_IS_NATIVE(ifp)	(NM_NA_VALID(ifp) && NA(ifp)->nm_dtor == netmap_hw_dtor)
 
@@ -1592,7 +1734,7 @@ extern int netmap_generic_txqdisc;
 
 /* Assigns the device IOMMU domain to an allocator.
  * Returns -ENOMEM in case the domain is different */
-#define nm_iommu_group_id(dev) (0)
+#define nm_iommu_group_id(dev) (-1)
 
 /* Callback invoked by the dma machinery after a successful dmamap_load */
 static void netmap_dmamap_cb(__unused void *arg,
@@ -1603,13 +1745,14 @@ static void netmap_dmamap_cb(__unused void *arg,
 /* bus_dmamap_load wrapper: call aforementioned function if map != NULL.
  * XXX can we do it without a callback ?
  */
-static inline void
+static inline int
 netmap_load_map(struct netmap_adapter *na,
 	bus_dma_tag_t tag, bus_dmamap_t map, void *buf)
 {
 	if (map)
 		bus_dmamap_load(tag, map, buf, NETMAP_BUF_SIZE(na),
 		    netmap_dmamap_cb, NULL, BUS_DMA_NOWAIT);
+	return 0;
 }
 
 static inline void
@@ -1620,6 +1763,8 @@ netmap_unload_map(struct netmap_adapter *na,
 		bus_dmamap_unload(tag, map);
 }
 
+#define netmap_sync_map(na, tag, map, sz, t)
+
 /* update the map when a buffer changes. */
 static inline void
 netmap_reload_map(struct netmap_adapter *na,
@@ -1636,31 +1781,84 @@ netmap_reload_map(struct netmap_adapter *na,
 
 #else /* linux */
 
+int nm_os_config(struct netmap_adapter *na, struct nm_config_info *info);
+
 int nm_iommu_group_id(bus_dma_tag_t dev);
 #include 
 
-static inline void
+/*
+ * on linux we need
+ *	dma_map_single(&pdev->dev, virt_addr, len, direction)
+ *	dma_unmap_single(&adapter->pdev->dev, phys_addr, len, direction)
+ */
+#if 0
+	struct e1000_buffer *buffer_info =  &tx_ring->buffer_info[l];
+	/* set time_stamp *before* dma to help avoid a possible race */
+	buffer_info->time_stamp = jiffies;
+	buffer_info->mapped_as_page = false;
+	buffer_info->length = len;
+	//buffer_info->next_to_watch = l;
+	/* reload dma map */
+	dma_unmap_single(&adapter->pdev->dev, buffer_info->dma,
+			NETMAP_BUF_SIZE, DMA_TO_DEVICE);
+	buffer_info->dma = dma_map_single(&adapter->pdev->dev,
+			addr, NETMAP_BUF_SIZE, DMA_TO_DEVICE);
+
+	if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) {
+		nm_prerr("dma mapping error");
+		/* goto dma_error; See e1000_put_txbuf() */
+		/* XXX reset */
+	}
+	tx_desc->buffer_addr = htole64(buffer_info->dma); //XXX
+
+#endif
+
+static inline int
 netmap_load_map(struct netmap_adapter *na,
-	bus_dma_tag_t tag, bus_dmamap_t map, void *buf)
+	bus_dma_tag_t tag, bus_dmamap_t map, void *buf, u_int size)
 {
-	if (0 && map) {
-		*map = dma_map_single(na->pdev, buf, NETMAP_BUF_SIZE(na),
+	if (map) {
+		*map = dma_map_single(na->pdev, buf, size,
 				      DMA_BIDIRECTIONAL);
+		if (dma_mapping_error(na->pdev, *map)) {
+			*map = 0;
+			return ENOMEM;
+		}
 	}
+	return 0;
 }
 
 static inline void
 netmap_unload_map(struct netmap_adapter *na,
-	bus_dma_tag_t tag, bus_dmamap_t map)
+	bus_dma_tag_t tag, bus_dmamap_t map, u_int sz)
 {
-	u_int sz = NETMAP_BUF_SIZE(na);
-
 	if (*map) {
 		dma_unmap_single(na->pdev, *map, sz,
 				 DMA_BIDIRECTIONAL);
 	}
 }
 
+#ifdef NETMAP_LINUX_HAVE_DMASYNC
+static inline void
+netmap_sync_map_cpu(struct netmap_adapter *na,
+	bus_dma_tag_t tag, bus_dmamap_t map, u_int sz, enum txrx t)
+{
+	if (*map) {
+		dma_sync_single_for_cpu(na->pdev, *map, sz,
+			(t == NR_TX ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+	}
+}
+
+static inline void
+netmap_sync_map_dev(struct netmap_adapter *na,
+	bus_dma_tag_t tag, bus_dmamap_t map, u_int sz, enum txrx t)
+{
+	if (*map) {
+		dma_sync_single_for_device(na->pdev, *map, sz,
+			(t == NR_TX ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+	}
+}
+
 static inline void
 netmap_reload_map(struct netmap_adapter *na,
 	bus_dma_tag_t tag, bus_dmamap_t map, void *buf)
@@ -1675,44 +1873,10 @@ netmap_reload_map(struct netmap_adapter *na,
 	*map = dma_map_single(na->pdev, buf, sz,
 				DMA_BIDIRECTIONAL);
 }
-
-/*
- * XXX How do we redefine these functions:
- *
- * on linux we need
- *	dma_map_single(&pdev->dev, virt_addr, len, direction)
- *	dma_unmap_single(&adapter->pdev->dev, phys_addr, len, direction
- * The len can be implicit (on netmap it is NETMAP_BUF_SIZE)
- * unfortunately the direction is not, so we need to change
- * something to have a cross API
- */
-
-#if 0
-	struct e1000_buffer *buffer_info =  &tx_ring->buffer_info[l];
-	/* set time_stamp *before* dma to help avoid a possible race */
-	buffer_info->time_stamp = jiffies;
-	buffer_info->mapped_as_page = false;
-	buffer_info->length = len;
-	//buffer_info->next_to_watch = l;
-	/* reload dma map */
-	dma_unmap_single(&adapter->pdev->dev, buffer_info->dma,
-			NETMAP_BUF_SIZE, DMA_TO_DEVICE);
-	buffer_info->dma = dma_map_single(&adapter->pdev->dev,
-			addr, NETMAP_BUF_SIZE, DMA_TO_DEVICE);
-
-	if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) {
-		D("dma mapping error");
-		/* goto dma_error; See e1000_put_txbuf() */
-		/* XXX reset */
-	}
-	tx_desc->buffer_addr = htole64(buffer_info->dma); //XXX
-
-#endif
-
-/*
- * The bus_dmamap_sync() can be one of wmb() or rmb() depending on direction.
- */
-#define bus_dmamap_sync(_a, _b, _c)
+#else /* !NETMAP_LINUX_HAVE_DMASYNC */
+#define netmap_sync_map_cpu(na, tag, map, sz, t)
+#define netmap_sync_map_dev(na, tag, map, sz, t)
+#endif /* NETMAP_LINUX_HAVE_DMASYNC */
 
 #endif /* linux */
 
@@ -1724,6 +1888,11 @@ static inline int
 netmap_idx_n2k(struct netmap_kring *kr, int idx)
 {
 	int n = kr->nkr_num_slots;
+
+	if (likely(kr->nkr_hwofs == 0)) {
+		return idx;
+	}
+
 	idx += kr->nkr_hwofs;
 	if (idx < 0)
 		return idx + n;
@@ -1738,6 +1907,11 @@ static inline int
 netmap_idx_k2n(struct netmap_kring *kr, int idx)
 {
 	int n = kr->nkr_num_slots;
+
+	if (likely(kr->nkr_hwofs == 0)) {
+		return idx;
+	}
+
 	idx -= kr->nkr_hwofs;
 	if (idx < 0)
 		return idx + n;
@@ -1749,13 +1923,32 @@ netmap_idx_k2n(struct netmap_kring *kr, int idx)
 
 
 /* Entries of the look-up table. */
+#ifdef __FreeBSD__
+struct lut_entry {
+	void *vaddr;		/* virtual address. */
+	vm_paddr_t paddr;	/* physical address. */
+};
+#else /* linux & _WIN32 */
+/* dma-mapping in linux can assign a buffer a different address
+ * depending on the device, so we need to have a separate
+ * physical-address look-up table for each na.
+ * We can still share the vaddrs, though, therefore we split
+ * the lut_entry structure.
+ */
 struct lut_entry {
 	void *vaddr;		/* virtual address. */
+};
+
+struct plut_entry {
 	vm_paddr_t paddr;	/* physical address. */
 };
+#endif /* linux & _WIN32 */
 
 struct netmap_obj_pool;
 
+/* alignment for netmap buffers */
+#define NM_BUF_ALIGN	64
+
 /*
  * NMB return the virtual address of a buffer (buffer 0 on bad index)
  * PNMB also fills the physical address
@@ -1774,16 +1967,51 @@ PNMB(struct netmap_adapter *na, struct netmap_slot *slot, uint64_t *pp)
 {
 	uint32_t i = slot->buf_idx;
 	struct lut_entry *lut = na->na_lut.lut;
+	struct plut_entry *plut = na->na_lut.plut;
 	void *ret = (i >= na->na_lut.objtotal) ? lut[0].vaddr : lut[i].vaddr;
 
-#ifndef _WIN32
-	*pp = (i >= na->na_lut.objtotal) ? lut[0].paddr : lut[i].paddr;
+#ifdef _WIN32
+	*pp = (i >= na->na_lut.objtotal) ? (uint64_t)plut[0].paddr.QuadPart : (uint64_t)plut[i].paddr.QuadPart;
 #else
-	*pp = (i >= na->na_lut.objtotal) ? (uint64_t)lut[0].paddr.QuadPart : (uint64_t)lut[i].paddr.QuadPart;
+	*pp = (i >= na->na_lut.objtotal) ? plut[0].paddr : plut[i].paddr;
 #endif
 	return ret;
 }
 
+static inline void
+nm_write_offset(struct netmap_kring *kring,
+		struct netmap_slot *slot, uint64_t offset)
+{
+	slot->ptr = (slot->ptr & ~kring->offset_mask) |
+		(offset & kring->offset_mask);
+}
+
+static inline uint64_t
+nm_get_offset(struct netmap_kring *kring, struct netmap_slot *slot)
+{
+	uint64_t offset = (slot->ptr & kring->offset_mask);
+	if (unlikely(offset > kring->offset_max))
+		offset = kring->offset_max;
+	return offset;
+}
+
+static inline void *
+NMB_O(struct netmap_kring *kring, struct netmap_slot *slot)
+{
+	void *addr = NMB(kring->na, slot);
+	return (char *)addr + nm_get_offset(kring, slot);
+}
+
+static inline void *
+PNMB_O(struct netmap_kring *kring, struct netmap_slot *slot, uint64_t *pp)
+{
+	void *addr = PNMB(kring->na, slot, pp);
+	uint64_t offset = nm_get_offset(kring, slot);
+	addr = (char *)addr + offset;
+	*pp += offset;
+	return addr;
+}
+
 
 /*
  * Structure associated to each netmap file descriptor.
@@ -1804,11 +2032,14 @@ struct netmap_priv_d {
 	struct netmap_if * volatile np_nifp;	/* netmap if descriptor. */
 
 	struct netmap_adapter	*np_na;
-	struct ifnet	*np_ifp;
+	if_t		np_ifp;
 	uint32_t	np_flags;	/* from the ioctl */
 	u_int		np_qfirst[NR_TXRX],
 			np_qlast[NR_TXRX]; /* range of tx/rx rings to scan */
-	uint16_t	np_txpoll;	/* XXX and also np_rxpoll ? */
+	uint16_t	np_txpoll;
+	uint16_t        np_kloop_state;	/* use with NMG_LOCK held */
+#define NM_SYNC_KLOOP_RUNNING	(1 << 0)
+#define NM_SYNC_KLOOP_STOPPING	(1 << 1)
 	int             np_sync_flags; /* to be passed to nm_sync */
 
 	int		np_refs;	/* use with NMG_LOCK held */
@@ -1818,7 +2049,25 @@ struct netmap_priv_d {
 	 * number of rings.
 	 */
 	NM_SELINFO_T *np_si[NR_TXRX];
-	struct thread	*np_td;		/* kqueue, just debugging */
+
+	/* In the optional CSB mode, the user must specify the start address
+	 * of two arrays of Communication Status Block (CSB) entries, for the
+	 * two directions (kernel read application write, and kernel write
+	 * application read).
+	 * The number of entries must agree with the number of rings bound to
+	 * the netmap file descriptor. The entries corresponding to the TX
+	 * rings are laid out before the ones corresponding to the RX rings.
+	 *
+	 * Array of CSB entries for application --> kernel communication
+	 * (N entries). */
+	struct nm_csb_atok	*np_csb_atok_base;
+	/* Array of CSB entries for kernel --> application communication
+	 * (N entries). */
+	struct nm_csb_ktoa	*np_csb_ktoa_base;
+
+#ifdef linux
+	struct file	*np_filp;  /* used by sync kloop */
+#endif /* linux */
 };
 
 struct netmap_priv_d *netmap_priv_new(void);
@@ -1832,7 +2081,7 @@ static inline int nm_kring_pending(struct netmap_priv_d *np)
 
 	for_rx_tx(t) {
 		for (i = np->np_qfirst[t]; i < np->np_qlast[t]; i++) {
-			struct netmap_kring *kring = &NMR(na, t)[i];
+			struct netmap_kring *kring = NMR(na, t)[i];
 			if (kring->nr_mode != kring->nr_pending_mode) {
 				return 1;
 			}
@@ -1841,9 +2090,23 @@ static inline int nm_kring_pending(struct netmap_priv_d *np)
 	return 0;
 }
 
+/* call with NMG_LOCK held */
+static __inline int
+nm_si_user(struct netmap_priv_d *priv, enum txrx t)
+{
+	return (priv->np_na != NULL &&
+		(priv->np_qlast[t] - priv->np_qfirst[t] > 1));
+}
+
 #ifdef WITH_PIPES
 int netmap_pipe_txsync(struct netmap_kring *txkring, int flags);
 int netmap_pipe_rxsync(struct netmap_kring *rxkring, int flags);
+int netmap_pipe_krings_create_both(struct netmap_adapter *na,
+				  struct netmap_adapter *ona);
+void netmap_pipe_krings_delete_both(struct netmap_adapter *na,
+				    struct netmap_adapter *ona);
+int netmap_pipe_reg_both(struct netmap_adapter *na,
+			 struct netmap_adapter *ona);
 #endif /* WITH_PIPES */
 
 #ifdef WITH_MONITOR
@@ -1858,17 +2121,21 @@ struct netmap_monitor_adapter {
 #endif /* WITH_MONITOR */
 
 
+int nm_os_generic_find_num_desc(if_t ifp, u_int *tx, u_int *rx);
+void nm_os_generic_find_num_queues(if_t ifp, u_int *txq, u_int *rxq);
 #ifdef WITH_GENERIC
 /*
  * generic netmap emulation for devices that do not have
  * native netmap support.
  */
-int generic_netmap_attach(struct ifnet *ifp);
-int generic_rx_handler(struct ifnet *ifp, struct mbuf *m);;
+int generic_netmap_attach(if_t ifp);
+int generic_rx_handler(if_t ifp, struct mbuf *m);
 
 int nm_os_catch_rx(struct netmap_generic_adapter *gna, int intercept);
 int nm_os_catch_tx(struct netmap_generic_adapter *gna, int intercept);
 
+int na_is_generic(struct netmap_adapter *na);
+
 /*
  * the generic transmit routine is passed a structure to optionally
  * build a queue of descriptors, in an OS-specific way.
@@ -1880,21 +2147,19 @@ int nm_os_catch_tx(struct netmap_generic_adapter *gna, int intercept);
  * routine to send the queue and free any resources. Failure is ignored.
  */
 struct nm_os_gen_arg {
-	struct ifnet *ifp;
+	if_t ifp;
 	void *m;	/* os-specific mbuf-like object */
 	void *head, *tail; /* tailq, if the OS-specific routine needs to build one */
 	void *addr;	/* payload of current packet */
 	u_int len;	/* packet length */
-	u_int ring_nr;	/* packet length */
+	u_int ring_nr;	/* transmit ring index */
 	u_int qevent;   /* in txqdisc mode, place an event on this mbuf */
 };
 
 int nm_os_generic_xmit_frame(struct nm_os_gen_arg *);
-int nm_os_generic_find_num_desc(struct ifnet *ifp, u_int *tx, u_int *rx);
-void nm_os_generic_find_num_queues(struct ifnet *ifp, u_int *txq, u_int *rxq);
 void nm_os_generic_set_features(struct netmap_generic_adapter *gna);
 
-static inline struct ifnet*
+static inline if_t
 netmap_generic_getifp(struct netmap_generic_adapter *gna)
 {
         if (gna->prev)
@@ -1925,6 +2190,7 @@ int nm_os_mitigation_active(struct nm_generic_mit *mit);
 void nm_os_mitigation_cleanup(struct nm_generic_mit *mit);
 #else /* !WITH_GENERIC */
 #define generic_netmap_attach(ifp)	(EOPNOTSUPP)
+#define na_is_generic(na)		(0)
 #endif /* WITH_GENERIC */
 
 /* Shared declarations for the VALE switch. */
@@ -1938,7 +2204,7 @@ void nm_os_mitigation_cleanup(struct nm_generic_mit *mit);
 struct nm_bdg_fwd {	/* forwarding entry for a bridge */
 	void *ft_buf;		/* netmap or indirect buffer */
 	uint8_t ft_frags;	/* how many fragments (only on 1st frag) */
-	uint8_t _ft_port;	/* dst port (unused) */
+	uint16_t ft_offset;	/* dst port (unused) */
 	uint16_t ft_flags;	/* flags, e.g. indirect */
 	uint16_t ft_len;	/* src fragment len */
 	uint16_t ft_next;	/* next packet to same destination */
@@ -2030,64 +2296,47 @@ void bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 			   u_int *j, u_int lim, u_int *howmany);
 
 /* persistent virtual port routines */
-int nm_os_vi_persist(const char *, struct ifnet **);
-void nm_os_vi_detach(struct ifnet *);
+int nm_os_vi_persist(const char *, if_t *);
+void nm_os_vi_detach(if_t);
 void nm_os_vi_init_index(void);
 
 /*
  * kernel thread routines
  */
-struct nm_kthread; /* OS-specific kthread - opaque */
-typedef void (*nm_kthread_worker_fn_t)(void *data);
+struct nm_kctx; /* OS-specific kernel context - opaque */
+typedef void (*nm_kctx_worker_fn_t)(void *data);
 
 /* kthread configuration */
-struct nm_kthread_cfg {
-	long				type;		/* kthread type/identifier */
-	nm_kthread_worker_fn_t		worker_fn;	/* worker function */
-	void				*worker_private;/* worker parameter */
-	int				attach_user;	/* attach kthread to user process */
+struct nm_kctx_cfg {
+	long			type;		/* kthread type/identifier */
+	nm_kctx_worker_fn_t	worker_fn;	/* worker function */
+	void			*worker_private;/* worker parameter */
+	int			attach_user;	/* attach kthread to user process */
 };
 /* kthread configuration */
-struct nm_kthread *nm_os_kthread_create(struct nm_kthread_cfg *cfg,
-					unsigned int cfgtype,
+struct nm_kctx *nm_os_kctx_create(struct nm_kctx_cfg *cfg,
 					void *opaque);
-int nm_os_kthread_start(struct nm_kthread *);
-void nm_os_kthread_stop(struct nm_kthread *);
-void nm_os_kthread_delete(struct nm_kthread *);
-void nm_os_kthread_wakeup_worker(struct nm_kthread *nmk);
-void nm_os_kthread_send_irq(struct nm_kthread *);
-void nm_os_kthread_set_affinity(struct nm_kthread *, int);
+int nm_os_kctx_worker_start(struct nm_kctx *);
+void nm_os_kctx_worker_stop(struct nm_kctx *);
+void nm_os_kctx_destroy(struct nm_kctx *);
+void nm_os_kctx_worker_setaff(struct nm_kctx *, int);
 u_int nm_os_ncpus(void);
 
-#ifdef WITH_PTNETMAP_HOST
-/*
- * netmap adapter for host ptnetmap ports
- */
-struct netmap_pt_host_adapter {
-	struct netmap_adapter up;
+int netmap_sync_kloop(struct netmap_priv_d *priv,
+		      struct nmreq_header *hdr);
+int netmap_sync_kloop_stop(struct netmap_priv_d *priv);
 
-	struct netmap_adapter *parent;
-	int (*parent_nm_notify)(struct netmap_kring *kring, int flags);
-	void *ptns;
-};
-/* ptnetmap HOST routines */
-int netmap_get_pt_host_na(struct nmreq *nmr, struct netmap_adapter **na,
-		struct netmap_mem_d * nmd, int create);
-int ptnetmap_ctl(struct nmreq *nmr, struct netmap_adapter *na);
-static inline int
-nm_ptnetmap_host_on(struct netmap_adapter *na)
-{
-	return na && na->na_flags & NAF_PTNETMAP_HOST;
-}
-#else /* !WITH_PTNETMAP_HOST */
-#define netmap_get_pt_host_na(nmr, _2, _3, _4) \
-	((nmr)->nr_flags & (NR_PTNETMAP_HOST) ? EOPNOTSUPP : 0)
-#define ptnetmap_ctl(_1, _2)   EINVAL
-#define nm_ptnetmap_host_on(_1)   EINVAL
-#endif /* !WITH_PTNETMAP_HOST */
+#ifdef WITH_PTNETMAP
+/* ptnetmap guest routines */
 
-#ifdef WITH_PTNETMAP_GUEST
-/* ptnetmap GUEST routines */
+/*
+ * ptnetmap_memdev routines used to talk with ptnetmap_memdev device driver
+ */
+struct ptnetmap_memdev;
+int nm_os_pt_memdev_iomap(struct ptnetmap_memdev *, vm_paddr_t *, void **,
+                          uint64_t *);
+void nm_os_pt_memdev_iounmap(struct ptnetmap_memdev *);
+uint32_t nm_os_pt_memdev_ioread(struct ptnetmap_memdev *, unsigned int);
 
 /*
  * netmap adapter for guest ptnetmap ports
@@ -2100,26 +2349,107 @@ struct netmap_pt_guest_adapter {
         /* The netmap adapter to be used by the driver. */
         struct netmap_hw_adapter dr;
 
-	void *csb;
-
 	/* Reference counter to track users of backend netmap port: the
 	 * network stack and netmap clients.
 	 * Used to decide when we need (de)allocate krings/rings and
 	 * start (stop) ptnetmap kthreads. */
-	int backend_regifs;
+	int backend_users;
 
 };
 
-int netmap_pt_guest_attach(struct netmap_adapter *na, void *csb,
-			   unsigned int nifp_offset, unsigned int memid);
-struct ptnet_ring;
-bool netmap_pt_guest_txsync(struct ptnet_ring *ptring, struct netmap_kring *kring,
-			    int flags);
-bool netmap_pt_guest_rxsync(struct ptnet_ring *ptring, struct netmap_kring *kring,
-			    int flags);
+int netmap_pt_guest_attach(struct netmap_adapter *na,
+			unsigned int nifp_offset,
+			unsigned int memid);
+bool netmap_pt_guest_txsync(struct nm_csb_atok *atok,
+			struct nm_csb_ktoa *ktoa,
+			struct netmap_kring *kring, int flags);
+bool netmap_pt_guest_rxsync(struct nm_csb_atok *atok,
+			struct nm_csb_ktoa *ktoa,
+			struct netmap_kring *kring, int flags);
 int ptnet_nm_krings_create(struct netmap_adapter *na);
 void ptnet_nm_krings_delete(struct netmap_adapter *na);
 void ptnet_nm_dtor(struct netmap_adapter *na);
-#endif /* WITH_PTNETMAP_GUEST */
+
+/* Helper function wrapping nm_sync_kloop_appl_read(). */
+static inline void
+ptnet_sync_tail(struct nm_csb_ktoa *ktoa, struct netmap_kring *kring)
+{
+	struct netmap_ring *ring = kring->ring;
+
+	/* Update hwcur and hwtail as known by the host. */
+        nm_sync_kloop_appl_read(ktoa, &kring->nr_hwtail, &kring->nr_hwcur);
+
+	/* nm_sync_finalize */
+	ring->tail = kring->rtail = kring->nr_hwtail;
+}
+#endif /* WITH_PTNETMAP */
+
+#ifdef __FreeBSD__
+/*
+ * FreeBSD mbuf allocator/deallocator in emulation mode:
+ *
+ * We allocate mbufs with m_gethdr(), since the mbuf header is needed
+ * by the driver. We also attach a customly-provided external storage,
+ * which in this case is a netmap buffer. When calling m_extadd(), however
+ * we pass a NULL address, since the real address (and length) will be
+ * filled in by nm_os_generic_xmit_frame() right before calling
+ * if_transmit().
+ *
+ * The dtor function does nothing, however we need it since mb_free_ext()
+ * has a KASSERT(), checking that the mbuf dtor function is not NULL.
+ */
+
+static void void_mbuf_dtor(struct mbuf *m) { }
+
+#define SET_MBUF_DESTRUCTOR(m, fn)	do {		\
+	(m)->m_ext.ext_free = (fn != NULL) ?		\
+	    (void *)fn : (void *)void_mbuf_dtor;	\
+} while (0)
+
+static inline struct mbuf *
+nm_os_get_mbuf(if_t ifp, int len)
+{
+	struct mbuf *m;
+
+	(void)ifp;
+	(void)len;
+
+	m = m_gethdr(M_NOWAIT, MT_DATA);
+	if (m == NULL) {
+		return m;
+	}
+
+	m_extadd(m, NULL /* buf */, 0 /* size */, void_mbuf_dtor,
+		 NULL, NULL, 0, EXT_NET_DRV);
+
+	return m;
+}
+
+#endif /* __FreeBSD__ */
+
+struct nmreq_option * nmreq_getoption(struct nmreq_header *, uint16_t);
+
+int netmap_init_bridges(void);
+void netmap_uninit_bridges(void);
+
+/* Functions to read and write CSB fields from the kernel. */
+#if defined (linux)
+#define CSB_READ(csb, field, r) (get_user(r, &csb->field))
+#define CSB_WRITE(csb, field, v) (put_user(v, &csb->field))
+#else  /* ! linux */
+#define CSB_READ(csb, field, r) (r = fuword32(&csb->field))
+#define CSB_WRITE(csb, field, v) (suword32(&csb->field, v))
+#endif /* ! linux */
+
+/* some macros that may not be defined */
+#ifndef ETH_HLEN
+#define ETH_HLEN 6
+#endif
+#ifndef ETH_FCS_LEN
+#define ETH_FCS_LEN 4
+#endif
+#ifndef VLAN_HLEN
+#define VLAN_HLEN 4
+#endif
 
 #endif /* _NET_NETMAP_KERN_H_ */
diff --git a/sys/dev/netmap/netmap_kloop.c b/sys/dev/netmap/netmap_kloop.c
new file mode 100644
index 000000000..e9027249b
--- /dev/null
+++ b/sys/dev/netmap/netmap_kloop.c
@@ -0,0 +1,1191 @@
+/*
+ * Copyright (C) 2016-2018 Vincenzo Maffione
+ * Copyright (C) 2015 Stefano Garzarella
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * common headers
+ */
+#if defined(__FreeBSD__)
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define usleep_range(_1, _2) \
+        pause_sbt("sync-kloop-sleep", SBT_1US * _1, SBT_1US * 1, C_ABSOLUTE)
+
+#elif defined(linux)
+#include 
+#include 
+#include 
+#endif
+
+#include 
+#include 
+#include 
+#include 
+
+/* Support for eventfd-based notifications. */
+#if defined(linux)
+#define SYNC_KLOOP_POLL
+#endif
+
+/* Write kring pointers (hwcur, hwtail) to the CSB.
+ * This routine is coupled with ptnetmap_guest_read_kring_csb(). */
+static inline void
+sync_kloop_kernel_write(struct nm_csb_ktoa __user *ptr, uint32_t hwcur,
+			   uint32_t hwtail)
+{
+	/* Issue a first store-store barrier to make sure writes to the
+	 * netmap ring do not overcome updates on ktoa->hwcur and ktoa->hwtail. */
+	nm_stst_barrier();
+
+	/*
+	 * The same scheme used in nm_sync_kloop_appl_write() applies here.
+	 * We allow the application to read a value of hwcur more recent than the value
+	 * of hwtail, since this would anyway result in a consistent view of the
+	 * ring state (and hwcur can never wraparound hwtail, since hwcur must be
+	 * behind head).
+	 *
+	 * The following memory barrier scheme is used to make this happen:
+	 *
+	 *          Application            Kernel
+	 *
+	 *          STORE(hwcur)           LOAD(hwtail)
+	 *          wmb() <------------->  rmb()
+	 *          STORE(hwtail)          LOAD(hwcur)
+	 */
+	CSB_WRITE(ptr, hwcur, hwcur);
+	nm_stst_barrier();
+	CSB_WRITE(ptr, hwtail, hwtail);
+}
+
+/* Read kring pointers (head, cur, sync_flags) from the CSB.
+ * This routine is coupled with ptnetmap_guest_write_kring_csb(). */
+static inline void
+sync_kloop_kernel_read(struct nm_csb_atok __user *ptr,
+			  struct netmap_ring *shadow_ring,
+			  uint32_t num_slots)
+{
+	/*
+	 * We place a memory barrier to make sure that the update of head never
+	 * overtakes the update of cur.
+	 * (see explanation in sync_kloop_kernel_write).
+	 */
+	CSB_READ(ptr, head, shadow_ring->head);
+	nm_ldld_barrier();
+	CSB_READ(ptr, cur, shadow_ring->cur);
+	CSB_READ(ptr, sync_flags, shadow_ring->flags);
+
+	/* Make sure that loads from atok->head and atok->cur are not delayed
+	 * after the loads from the netmap ring. */
+	nm_ldld_barrier();
+}
+
+/* Enable or disable application --> kernel kicks. */
+static inline void
+csb_ktoa_kick_enable(struct nm_csb_ktoa __user *csb_ktoa, uint32_t val)
+{
+	CSB_WRITE(csb_ktoa, kern_need_kick, val);
+}
+
+#ifdef SYNC_KLOOP_POLL
+/* Are application interrupt enabled or disabled? */
+static inline uint32_t
+csb_atok_intr_enabled(struct nm_csb_atok __user *csb_atok)
+{
+	uint32_t v;
+
+	CSB_READ(csb_atok, appl_need_kick, v);
+
+	return v;
+}
+#endif  /* SYNC_KLOOP_POLL */
+
+static inline void
+sync_kloop_kring_dump(const char *title, const struct netmap_kring *kring)
+{
+	nm_prinf("%s, kring %s, hwcur %d, rhead %d, "
+		"rcur %d, rtail %d, hwtail %d",
+		title, kring->name, kring->nr_hwcur, kring->rhead,
+		kring->rcur, kring->rtail, kring->nr_hwtail);
+}
+
+/* Arguments for netmap_sync_kloop_tx_ring() and
+ * netmap_sync_kloop_rx_ring().
+ */
+struct sync_kloop_ring_args {
+	struct netmap_kring *kring;
+	struct nm_csb_atok *csb_atok;
+	struct nm_csb_ktoa *csb_ktoa;
+#ifdef SYNC_KLOOP_POLL
+	struct eventfd_ctx *irq_ctx;
+#endif /* SYNC_KLOOP_POLL */
+	/* Are we busy waiting rather than using a schedule() loop ? */
+	bool busy_wait;
+	/* Are we processing in the context of VM exit ? */
+	bool direct;
+};
+
+static void
+netmap_sync_kloop_tx_ring(const struct sync_kloop_ring_args *a)
+{
+	struct netmap_kring *kring = a->kring;
+	struct nm_csb_atok *csb_atok = a->csb_atok;
+	struct nm_csb_ktoa *csb_ktoa = a->csb_ktoa;
+	struct netmap_ring shadow_ring; /* shadow copy of the netmap_ring */
+#ifdef SYNC_KLOOP_POLL
+	bool more_txspace = false;
+#endif /* SYNC_KLOOP_POLL */
+	uint32_t num_slots;
+	int batch;
+
+	if (unlikely(nm_kr_tryget(kring, 1, NULL))) {
+		return;
+	}
+
+	num_slots = kring->nkr_num_slots;
+
+	/* Disable application --> kernel notifications. */
+	if (!a->direct) {
+		csb_ktoa_kick_enable(csb_ktoa, 0);
+	}
+	/* Copy the application kring pointers from the CSB */
+	sync_kloop_kernel_read(csb_atok, &shadow_ring, num_slots);
+
+	for (;;) {
+		batch = shadow_ring.head - kring->nr_hwcur;
+		if (batch < 0)
+			batch += num_slots;
+
+#ifdef PTN_TX_BATCH_LIM
+		if (batch > PTN_TX_BATCH_LIM(num_slots)) {
+			/* If application moves ahead too fast, let's cut the move so
+			 * that we don't exceed our batch limit. */
+			uint32_t head_lim = kring->nr_hwcur + PTN_TX_BATCH_LIM(num_slots);
+
+			if (head_lim >= num_slots)
+				head_lim -= num_slots;
+			nm_prdis(1, "batch: %d head: %d head_lim: %d", batch, shadow_ring.head,
+					head_lim);
+			shadow_ring.head = head_lim;
+			batch = PTN_TX_BATCH_LIM(num_slots);
+		}
+#endif /* PTN_TX_BATCH_LIM */
+
+		if (nm_kr_txspace(kring) <= (num_slots >> 1)) {
+			shadow_ring.flags |= NAF_FORCE_RECLAIM;
+		}
+
+		/* Netmap prologue */
+		shadow_ring.tail = kring->rtail;
+		if (unlikely(nm_txsync_prologue(kring, &shadow_ring) >= num_slots)) {
+			/* Reinit ring and enable notifications. */
+			netmap_ring_reinit(kring);
+			if (!a->busy_wait) {
+				csb_ktoa_kick_enable(csb_ktoa, 1);
+			}
+			break;
+		}
+
+		if (unlikely(netmap_debug & NM_DEBUG_TXSYNC)) {
+			sync_kloop_kring_dump("pre txsync", kring);
+		}
+
+		if (unlikely(kring->nm_sync(kring, shadow_ring.flags))) {
+			if (!a->busy_wait) {
+				/* Re-enable notifications. */
+				csb_ktoa_kick_enable(csb_ktoa, 1);
+			}
+			nm_prerr("txsync() failed");
+			break;
+		}
+
+		/*
+		 * Finalize
+		 * Copy kernel hwcur and hwtail into the CSB for the application sync(), and
+		 * do the nm_sync_finalize.
+		 */
+		sync_kloop_kernel_write(csb_ktoa, kring->nr_hwcur,
+				kring->nr_hwtail);
+		if (kring->rtail != kring->nr_hwtail) {
+			/* Some more room available in the parent adapter. */
+			kring->rtail = kring->nr_hwtail;
+#ifdef SYNC_KLOOP_POLL
+			more_txspace = true;
+#endif /* SYNC_KLOOP_POLL */
+		}
+
+		if (unlikely(netmap_debug & NM_DEBUG_TXSYNC)) {
+			sync_kloop_kring_dump("post txsync", kring);
+		}
+
+		/* Interrupt the application if needed. */
+#ifdef SYNC_KLOOP_POLL
+		if (a->irq_ctx && more_txspace && csb_atok_intr_enabled(csb_atok)) {
+			/* We could disable kernel --> application kicks here,
+			 * to avoid spurious interrupts. */
+			NM_EVENTFD_SIGNAL(a->irq_ctx);
+			more_txspace = false;
+		}
+#endif /* SYNC_KLOOP_POLL */
+
+		/* Read CSB to see if there is more work to do. */
+		sync_kloop_kernel_read(csb_atok, &shadow_ring, num_slots);
+		if (shadow_ring.head == kring->rhead) {
+			if (a->busy_wait) {
+				break;
+			}
+			/*
+			 * No more packets to transmit. We enable notifications and
+			 * go to sleep, waiting for a kick from the application when new
+			 * new slots are ready for transmission.
+			 */
+			/* Re-enable notifications. */
+			csb_ktoa_kick_enable(csb_ktoa, 1);
+			/* Double check, with store-load memory barrier. */
+			nm_stld_barrier();
+			sync_kloop_kernel_read(csb_atok, &shadow_ring, num_slots);
+			if (shadow_ring.head != kring->rhead) {
+				/* We won the race condition, there are more packets to
+				 * transmit. Disable notifications and do another cycle */
+				csb_ktoa_kick_enable(csb_ktoa, 0);
+				continue;
+			}
+			break;
+		}
+
+		if (nm_kr_txempty(kring)) {
+			/* No more available TX slots. We stop waiting for a notification
+			 * from the backend (netmap_tx_irq). */
+			nm_prdis(1, "TX ring");
+			break;
+		}
+	}
+
+	nm_kr_put(kring);
+
+#ifdef SYNC_KLOOP_POLL
+	if (a->irq_ctx && more_txspace && csb_atok_intr_enabled(csb_atok)) {
+		NM_EVENTFD_SIGNAL(a->irq_ctx);
+	}
+#endif /* SYNC_KLOOP_POLL */
+}
+
+/* RX cycle without receive any packets */
+#define SYNC_LOOP_RX_DRY_CYCLES_MAX	2
+
+static inline int
+sync_kloop_norxslots(struct netmap_kring *kring, uint32_t g_head)
+{
+	return (NM_ACCESS_ONCE(kring->nr_hwtail) == nm_prev(g_head,
+				kring->nkr_num_slots - 1));
+}
+
+static void
+netmap_sync_kloop_rx_ring(const struct sync_kloop_ring_args *a)
+{
+
+	struct netmap_kring *kring = a->kring;
+	struct nm_csb_atok *csb_atok = a->csb_atok;
+	struct nm_csb_ktoa *csb_ktoa = a->csb_ktoa;
+	struct netmap_ring shadow_ring; /* shadow copy of the netmap_ring */
+	int dry_cycles = 0;
+#ifdef SYNC_KLOOP_POLL
+	bool some_recvd = false;
+#endif /* SYNC_KLOOP_POLL */
+	uint32_t num_slots;
+
+	if (unlikely(nm_kr_tryget(kring, 1, NULL))) {
+		return;
+	}
+
+	num_slots = kring->nkr_num_slots;
+
+	/* Get RX csb_atok and csb_ktoa pointers from the CSB. */
+	num_slots = kring->nkr_num_slots;
+
+	/* Disable notifications. */
+	if (!a->direct) {
+		csb_ktoa_kick_enable(csb_ktoa, 0);
+	}
+	/* Copy the application kring pointers from the CSB */
+	sync_kloop_kernel_read(csb_atok, &shadow_ring, num_slots);
+
+	for (;;) {
+		uint32_t hwtail;
+
+		/* Netmap prologue */
+		shadow_ring.tail = kring->rtail;
+		if (unlikely(nm_rxsync_prologue(kring, &shadow_ring) >= num_slots)) {
+			/* Reinit ring and enable notifications. */
+			netmap_ring_reinit(kring);
+			if (!a->busy_wait) {
+				csb_ktoa_kick_enable(csb_ktoa, 1);
+			}
+			break;
+		}
+
+		if (unlikely(netmap_debug & NM_DEBUG_RXSYNC)) {
+			sync_kloop_kring_dump("pre rxsync", kring);
+		}
+
+		if (unlikely(kring->nm_sync(kring, shadow_ring.flags))) {
+			if (!a->busy_wait) {
+				/* Re-enable notifications. */
+				csb_ktoa_kick_enable(csb_ktoa, 1);
+			}
+			nm_prerr("rxsync() failed");
+			break;
+		}
+
+		/*
+		 * Finalize
+		 * Copy kernel hwcur and hwtail into the CSB for the application sync()
+		 */
+		hwtail = NM_ACCESS_ONCE(kring->nr_hwtail);
+		sync_kloop_kernel_write(csb_ktoa, kring->nr_hwcur, hwtail);
+		if (kring->rtail != hwtail) {
+			kring->rtail = hwtail;
+#ifdef SYNC_KLOOP_POLL
+			some_recvd = true;
+#endif /* SYNC_KLOOP_POLL */
+			dry_cycles = 0;
+		} else {
+			dry_cycles++;
+		}
+
+		if (unlikely(netmap_debug & NM_DEBUG_RXSYNC)) {
+			sync_kloop_kring_dump("post rxsync", kring);
+		}
+
+#ifdef SYNC_KLOOP_POLL
+		/* Interrupt the application if needed. */
+		if (a->irq_ctx && some_recvd && csb_atok_intr_enabled(csb_atok)) {
+			/* We could disable kernel --> application kicks here,
+			 * to avoid spurious interrupts. */
+			NM_EVENTFD_SIGNAL(a->irq_ctx);
+			some_recvd = false;
+		}
+#endif /* SYNC_KLOOP_POLL */
+
+		/* Read CSB to see if there is more work to do. */
+		sync_kloop_kernel_read(csb_atok, &shadow_ring, num_slots);
+		if (sync_kloop_norxslots(kring, shadow_ring.head)) {
+			if (a->busy_wait) {
+				break;
+			}
+			/*
+			 * No more slots available for reception. We enable notification and
+			 * go to sleep, waiting for a kick from the application when new receive
+			 * slots are available.
+			 */
+			/* Re-enable notifications. */
+			csb_ktoa_kick_enable(csb_ktoa, 1);
+			/* Double check, with store-load memory barrier. */
+			nm_stld_barrier();
+			sync_kloop_kernel_read(csb_atok, &shadow_ring, num_slots);
+			if (!sync_kloop_norxslots(kring, shadow_ring.head)) {
+				/* We won the race condition, more slots are available. Disable
+				 * notifications and do another cycle. */
+				csb_ktoa_kick_enable(csb_ktoa, 0);
+				continue;
+			}
+			break;
+		}
+
+		hwtail = NM_ACCESS_ONCE(kring->nr_hwtail);
+		if (unlikely(hwtail == kring->rhead ||
+					dry_cycles >= SYNC_LOOP_RX_DRY_CYCLES_MAX)) {
+			/* No more packets to be read from the backend. We stop and
+			 * wait for a notification from the backend (netmap_rx_irq). */
+			nm_prdis(1, "nr_hwtail: %d rhead: %d dry_cycles: %d",
+					hwtail, kring->rhead, dry_cycles);
+			break;
+		}
+	}
+
+	nm_kr_put(kring);
+
+#ifdef SYNC_KLOOP_POLL
+	/* Interrupt the application if needed. */
+	if (a->irq_ctx && some_recvd && csb_atok_intr_enabled(csb_atok)) {
+		NM_EVENTFD_SIGNAL(a->irq_ctx);
+	}
+#endif /* SYNC_KLOOP_POLL */
+}
+
+#ifdef SYNC_KLOOP_POLL
+struct sync_kloop_poll_ctx;
+struct sync_kloop_poll_entry {
+	/* Support for receiving notifications from
+	 * a netmap ring or from the application. */
+	struct file *filp;
+	wait_queue_t wait;
+	wait_queue_head_t *wqh;
+
+	/* Support for sending notifications to the application. */
+	struct eventfd_ctx *irq_ctx;
+	struct file *irq_filp;
+
+	/* Arguments for the ring processing function. Useful
+	 * in case of custom wake-up function. */
+	struct sync_kloop_ring_args *args;
+	struct sync_kloop_poll_ctx *parent;
+
+};
+
+struct sync_kloop_poll_ctx {
+	poll_table wait_table;
+	unsigned int next_entry;
+	int (*next_wake_fun)(wait_queue_t *, unsigned, int, void *);
+	unsigned int num_entries;
+	unsigned int num_tx_rings;
+	unsigned int num_rings;
+	/* First num_tx_rings entries are for the TX kicks.
+	 * Then the RX kicks entries follow. The last two
+	 * entries are for TX irq, and RX irq. */
+	struct sync_kloop_poll_entry entries[0];
+};
+
+static void
+sync_kloop_poll_table_queue_proc(struct file *file, wait_queue_head_t *wqh,
+				poll_table *pt
+#ifdef NETMAP_LINUX_HAVE_POLLWAIT_5ARGS
+				, unsigned long unused
+#endif
+				)
+{
+	struct sync_kloop_poll_ctx *poll_ctx =
+		container_of(pt, struct sync_kloop_poll_ctx, wait_table);
+	struct sync_kloop_poll_entry *entry = poll_ctx->entries +
+						poll_ctx->next_entry;
+
+	BUG_ON(poll_ctx->next_entry >= poll_ctx->num_entries);
+	entry->wqh = wqh;
+	entry->filp = file;
+	/* Use the default wake up function. */
+	if (poll_ctx->next_wake_fun == NULL) {
+		init_waitqueue_entry(&entry->wait, current);
+	} else {
+		init_waitqueue_func_entry(&entry->wait,
+		    poll_ctx->next_wake_fun);
+	}
+	add_wait_queue(wqh, &entry->wait);
+}
+
+static int
+sync_kloop_tx_kick_wake_fun(wait_queue_t *wait, unsigned mode,
+    int wake_flags, void *key)
+{
+	struct sync_kloop_poll_entry *entry =
+	    container_of(wait, struct sync_kloop_poll_entry, wait);
+
+	netmap_sync_kloop_tx_ring(entry->args);
+
+	return 0;
+}
+
+static int
+sync_kloop_tx_irq_wake_fun(wait_queue_t *wait, unsigned mode,
+    int wake_flags, void *key)
+{
+	struct sync_kloop_poll_entry *entry =
+	    container_of(wait, struct sync_kloop_poll_entry, wait);
+	struct sync_kloop_poll_ctx *poll_ctx = entry->parent;
+	int i;
+
+	for (i = 0; i < poll_ctx->num_tx_rings; i++) {
+		struct eventfd_ctx *irq_ctx = poll_ctx->entries[i].irq_ctx;
+
+		if (irq_ctx) {
+			NM_EVENTFD_SIGNAL(irq_ctx);
+		}
+	}
+
+	return 0;
+}
+
+static int
+sync_kloop_rx_kick_wake_fun(wait_queue_t *wait, unsigned mode,
+    int wake_flags, void *key)
+{
+	struct sync_kloop_poll_entry *entry =
+	    container_of(wait, struct sync_kloop_poll_entry, wait);
+
+	netmap_sync_kloop_rx_ring(entry->args);
+
+	return 0;
+}
+
+static int
+sync_kloop_rx_irq_wake_fun(wait_queue_t *wait, unsigned mode,
+    int wake_flags, void *key)
+{
+	struct sync_kloop_poll_entry *entry =
+	    container_of(wait, struct sync_kloop_poll_entry, wait);
+	struct sync_kloop_poll_ctx *poll_ctx = entry->parent;
+	int i;
+
+	for (i = poll_ctx->num_tx_rings; i < poll_ctx->num_rings; i++) {
+		struct eventfd_ctx *irq_ctx = poll_ctx->entries[i].irq_ctx;
+
+		if (irq_ctx) {
+			NM_EVENTFD_SIGNAL(irq_ctx);
+		}
+	}
+
+	return 0;
+}
+#endif  /* SYNC_KLOOP_POLL */
+
+int
+netmap_sync_kloop(struct netmap_priv_d *priv, struct nmreq_header *hdr)
+{
+	struct nmreq_sync_kloop_start *req =
+		(struct nmreq_sync_kloop_start *)(uintptr_t)hdr->nr_body;
+	struct nmreq_opt_sync_kloop_eventfds *eventfds_opt = NULL;
+#ifdef SYNC_KLOOP_POLL
+	struct sync_kloop_poll_ctx *poll_ctx = NULL;
+#endif  /* SYNC_KLOOP_POLL */
+	int num_rx_rings, num_tx_rings, num_rings;
+	struct sync_kloop_ring_args *args = NULL;
+	uint32_t sleep_us = req->sleep_us;
+	struct nm_csb_atok* csb_atok_base;
+	struct nm_csb_ktoa* csb_ktoa_base;
+	struct netmap_adapter *na;
+	struct nmreq_option *opt;
+	bool na_could_sleep = false;
+	bool busy_wait = true;
+	bool direct_tx = false;
+	bool direct_rx = false;
+	int err = 0;
+	int i;
+
+	if (sleep_us > 1000000) {
+		/* We do not accept sleeping for more than a second. */
+		return EINVAL;
+	}
+
+	if (priv->np_nifp == NULL) {
+		return ENXIO;
+	}
+	mb(); /* make sure following reads are not from cache */
+
+	na = priv->np_na;
+	if (!nm_netmap_on(na)) {
+		return ENXIO;
+	}
+
+	NMG_LOCK();
+	/* Make sure the application is working in CSB mode. */
+	if (!priv->np_csb_atok_base || !priv->np_csb_ktoa_base) {
+		NMG_UNLOCK();
+		nm_prerr("sync-kloop on %s requires "
+				"NETMAP_REQ_OPT_CSB option", na->name);
+		return EINVAL;
+	}
+
+	csb_atok_base = priv->np_csb_atok_base;
+	csb_ktoa_base = priv->np_csb_ktoa_base;
+
+	/* Make sure that no kloop is currently running. */
+	if (priv->np_kloop_state & NM_SYNC_KLOOP_RUNNING) {
+		err = EBUSY;
+	}
+	priv->np_kloop_state |= NM_SYNC_KLOOP_RUNNING;
+	NMG_UNLOCK();
+	if (err) {
+		return err;
+	}
+
+	num_rx_rings = priv->np_qlast[NR_RX] - priv->np_qfirst[NR_RX];
+	num_tx_rings = priv->np_qlast[NR_TX] - priv->np_qfirst[NR_TX];
+	num_rings = num_tx_rings + num_rx_rings;
+
+	args = nm_os_malloc(num_rings * sizeof(args[0]));
+	if (!args) {
+		err = ENOMEM;
+		goto out;
+	}
+
+	/* Prepare the arguments for netmap_sync_kloop_tx_ring()
+	 * and netmap_sync_kloop_rx_ring(). */
+	for (i = 0; i < num_tx_rings; i++) {
+		struct sync_kloop_ring_args *a = args + i;
+
+		a->kring = NMR(na, NR_TX)[i + priv->np_qfirst[NR_TX]];
+		a->csb_atok = csb_atok_base + i;
+		a->csb_ktoa = csb_ktoa_base + i;
+		a->busy_wait = busy_wait;
+		a->direct = direct_tx;
+	}
+	for (i = 0; i < num_rx_rings; i++) {
+		struct sync_kloop_ring_args *a = args + num_tx_rings + i;
+
+		a->kring = NMR(na, NR_RX)[i + priv->np_qfirst[NR_RX]];
+		a->csb_atok = csb_atok_base + num_tx_rings + i;
+		a->csb_ktoa = csb_ktoa_base + num_tx_rings + i;
+		a->busy_wait = busy_wait;
+		a->direct = direct_rx;
+	}
+
+	/* Validate notification options. */
+	opt = nmreq_getoption(hdr, NETMAP_REQ_OPT_SYNC_KLOOP_MODE);
+	if (opt != NULL) {
+		struct nmreq_opt_sync_kloop_mode *mode_opt =
+		    (struct nmreq_opt_sync_kloop_mode *)opt;
+
+		direct_tx = !!(mode_opt->mode & NM_OPT_SYNC_KLOOP_DIRECT_TX);
+		direct_rx = !!(mode_opt->mode & NM_OPT_SYNC_KLOOP_DIRECT_RX);
+		if (mode_opt->mode & ~(NM_OPT_SYNC_KLOOP_DIRECT_TX |
+		    NM_OPT_SYNC_KLOOP_DIRECT_RX)) {
+			opt->nro_status = err = EINVAL;
+			goto out;
+		}
+		opt->nro_status = 0;
+	}
+	opt = nmreq_getoption(hdr, NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS);
+	if (opt != NULL) {
+		if (opt->nro_size != sizeof(*eventfds_opt) +
+			sizeof(eventfds_opt->eventfds[0]) * num_rings) {
+			/* Option size not consistent with the number of
+			 * entries. */
+			opt->nro_status = err = EINVAL;
+			goto out;
+		}
+#ifdef SYNC_KLOOP_POLL
+		eventfds_opt = (struct nmreq_opt_sync_kloop_eventfds *)opt;
+		opt->nro_status = 0;
+
+		/* Check if some ioeventfd entry is not defined, and force sleep
+		 * synchronization in that case. */
+		busy_wait = false;
+		for (i = 0; i < num_rings; i++) {
+			if (eventfds_opt->eventfds[i].ioeventfd < 0) {
+				busy_wait = true;
+				break;
+			}
+		}
+
+		if (busy_wait && (direct_tx || direct_rx)) {
+			/* For direct processing we need all the
+			 * ioeventfds to be valid. */
+			opt->nro_status = err = EINVAL;
+			goto out;
+		}
+
+		/* We need 2 poll entries for TX and RX notifications coming
+		 * from the netmap adapter, plus one entries per ring for the
+		 * notifications coming from the application. */
+		poll_ctx = nm_os_malloc(sizeof(*poll_ctx) +
+				(num_rings + 2) * sizeof(poll_ctx->entries[0]));
+		init_poll_funcptr(&poll_ctx->wait_table,
+					sync_kloop_poll_table_queue_proc);
+		poll_ctx->num_entries = 2 + num_rings;
+		poll_ctx->num_tx_rings = num_tx_rings;
+		poll_ctx->num_rings = num_rings;
+		poll_ctx->next_entry = 0;
+		poll_ctx->next_wake_fun = NULL;
+
+		if (direct_tx && (na->na_flags & NAF_BDG_MAYSLEEP)) {
+			/* In direct mode, VALE txsync is called from
+			 * wake-up context, where it is not possible
+			 * to sleep.
+			 */
+			na->na_flags &= ~NAF_BDG_MAYSLEEP;
+			na_could_sleep = true;
+		}
+
+		for (i = 0; i < num_rings + 2; i++) {
+			poll_ctx->entries[i].args = args + i;
+			poll_ctx->entries[i].parent = poll_ctx;
+		}
+
+		/* Poll for notifications coming from the applications through
+		 * eventfds. */
+		for (i = 0; i < num_rings; i++, poll_ctx->next_entry++) {
+			struct eventfd_ctx *irq = NULL;
+			struct file *filp = NULL;
+			unsigned long mask;
+			bool tx_ring = (i < num_tx_rings);
+
+			if (eventfds_opt->eventfds[i].irqfd >= 0) {
+				filp = eventfd_fget(
+				    eventfds_opt->eventfds[i].irqfd);
+				if (IS_ERR(filp)) {
+					err = PTR_ERR(filp);
+					goto out;
+				}
+				irq = eventfd_ctx_fileget(filp);
+				if (IS_ERR(irq)) {
+					err = PTR_ERR(irq);
+					goto out;
+				}
+			}
+			poll_ctx->entries[i].irq_filp = filp;
+			poll_ctx->entries[i].irq_ctx = irq;
+			poll_ctx->entries[i].args->busy_wait = busy_wait;
+			/* Don't let netmap_sync_kloop_*x_ring() use
+			 * IRQs in direct mode. */
+			poll_ctx->entries[i].args->irq_ctx =
+			    ((tx_ring && direct_tx) ||
+			    (!tx_ring && direct_rx)) ? NULL :
+			    poll_ctx->entries[i].irq_ctx;
+			poll_ctx->entries[i].args->direct =
+			    (tx_ring ? direct_tx : direct_rx);
+
+			if (!busy_wait) {
+				filp = eventfd_fget(
+				    eventfds_opt->eventfds[i].ioeventfd);
+				if (IS_ERR(filp)) {
+					err = PTR_ERR(filp);
+					goto out;
+				}
+				if (tx_ring && direct_tx) {
+					/* Override the wake up function
+					 * so that it can directly call
+					 * netmap_sync_kloop_tx_ring().
+					 */
+					poll_ctx->next_wake_fun =
+					    sync_kloop_tx_kick_wake_fun;
+				} else if (!tx_ring && direct_rx) {
+					/* Same for direct RX. */
+					poll_ctx->next_wake_fun =
+					    sync_kloop_rx_kick_wake_fun;
+				} else {
+					poll_ctx->next_wake_fun = NULL;
+				}
+				mask = filp->f_op->poll(filp,
+				    &poll_ctx->wait_table);
+				if (mask & POLLERR) {
+					err = EINVAL;
+					goto out;
+				}
+			}
+		}
+
+		/* Poll for notifications coming from the netmap rings bound to
+		 * this file descriptor. */
+		if (!busy_wait) {
+			NMG_LOCK();
+			/* In direct mode, override the wake up function so
+			 * that it can forward the netmap_tx_irq() to the
+			 * guest. */
+			poll_ctx->next_wake_fun = direct_tx ?
+			    sync_kloop_tx_irq_wake_fun : NULL;
+			poll_wait(priv->np_filp, priv->np_si[NR_TX],
+			    &poll_ctx->wait_table);
+			poll_ctx->next_entry++;
+
+			poll_ctx->next_wake_fun = direct_rx ?
+			    sync_kloop_rx_irq_wake_fun : NULL;
+			poll_wait(priv->np_filp, priv->np_si[NR_RX],
+			    &poll_ctx->wait_table);
+			poll_ctx->next_entry++;
+			NMG_UNLOCK();
+		}
+#else   /* SYNC_KLOOP_POLL */
+		opt->nro_status = EOPNOTSUPP;
+		goto out;
+#endif  /* SYNC_KLOOP_POLL */
+	}
+
+	nm_prinf("kloop busy_wait %u, direct_tx %u, direct_rx %u, "
+	    "na_could_sleep %u", busy_wait, direct_tx, direct_rx,
+	    na_could_sleep);
+
+	/* Main loop. */
+	for (;;) {
+		if (unlikely(NM_ACCESS_ONCE(priv->np_kloop_state) & NM_SYNC_KLOOP_STOPPING)) {
+			break;
+		}
+
+#ifdef SYNC_KLOOP_POLL
+		if (!busy_wait) {
+			/* It is important to set the task state as
+			 * interruptible before processing any TX/RX ring,
+			 * so that if a notification on ring Y comes after
+			 * we have processed ring Y, but before we call
+			 * schedule(), we don't miss it. This is true because
+			 * the wake up function will change the task state,
+			 * and therefore the schedule_timeout() call below
+			 * will observe the change).
+			 */
+			set_current_state(TASK_INTERRUPTIBLE);
+		}
+#endif  /* SYNC_KLOOP_POLL */
+
+		/* Process all the TX rings bound to this file descriptor. */
+		for (i = 0; !direct_tx && i < num_tx_rings; i++) {
+			struct sync_kloop_ring_args *a = args + i;
+			netmap_sync_kloop_tx_ring(a);
+		}
+
+		/* Process all the RX rings bound to this file descriptor. */
+		for (i = 0; !direct_rx && i < num_rx_rings; i++) {
+			struct sync_kloop_ring_args *a = args + num_tx_rings + i;
+			netmap_sync_kloop_rx_ring(a);
+		}
+
+		if (busy_wait) {
+			/* Default synchronization method: sleep for a while. */
+			usleep_range(sleep_us, sleep_us);
+		}
+#ifdef SYNC_KLOOP_POLL
+		else {
+			/* Yield to the scheduler waiting for a notification
+			 * to come either from netmap or the application. */
+			schedule_timeout(msecs_to_jiffies(3000));
+		}
+#endif /* SYNC_KLOOP_POLL */
+	}
+out:
+#ifdef SYNC_KLOOP_POLL
+	if (poll_ctx) {
+		/* Stop polling from netmap and the eventfds, and deallocate
+		 * the poll context. */
+		if (!busy_wait) {
+			__set_current_state(TASK_RUNNING);
+		}
+		for (i = 0; i < poll_ctx->next_entry; i++) {
+			struct sync_kloop_poll_entry *entry =
+						poll_ctx->entries + i;
+
+			if (entry->wqh)
+				remove_wait_queue(entry->wqh, &entry->wait);
+			/* We did not get a reference to the eventfds, but
+			 * don't do that on netmap file descriptors (since
+			 * a reference was not taken. */
+			if (entry->filp && entry->filp != priv->np_filp)
+				fput(entry->filp);
+			if (entry->irq_ctx)
+				eventfd_ctx_put(entry->irq_ctx);
+			if (entry->irq_filp)
+				fput(entry->irq_filp);
+		}
+		nm_os_free(poll_ctx);
+		poll_ctx = NULL;
+	}
+#endif /* SYNC_KLOOP_POLL */
+
+	if (args) {
+		nm_os_free(args);
+		args = NULL;
+	}
+
+	/* Reset the kloop state. */
+	NMG_LOCK();
+	priv->np_kloop_state = 0;
+	if (na_could_sleep) {
+		na->na_flags |= NAF_BDG_MAYSLEEP;
+	}
+	NMG_UNLOCK();
+
+	return err;
+}
+
+int
+netmap_sync_kloop_stop(struct netmap_priv_d *priv)
+{
+	struct netmap_adapter *na;
+	bool running = true;
+	int err = 0;
+
+	if (priv->np_nifp == NULL) {
+		return ENXIO;
+	}
+	mb(); /* make sure following reads are not from cache */
+
+	na = priv->np_na;
+	if (!nm_netmap_on(na)) {
+		return ENXIO;
+	}
+
+	/* Set the kloop stopping flag. */
+	NMG_LOCK();
+	priv->np_kloop_state |= NM_SYNC_KLOOP_STOPPING;
+	NMG_UNLOCK();
+
+	/* Send a notification to the kloop, in case it is blocked in
+	 * schedule_timeout(). We can use either RX or TX, because the
+	 * kloop is waiting on both. */
+	nm_os_selwakeup(priv->np_si[NR_RX]);
+
+	/* Wait for the kloop to actually terminate. */
+	while (running) {
+		usleep_range(1000, 1500);
+		NMG_LOCK();
+		running = (NM_ACCESS_ONCE(priv->np_kloop_state)
+				& NM_SYNC_KLOOP_RUNNING);
+		NMG_UNLOCK();
+	}
+
+	return err;
+}
+
+#ifdef WITH_PTNETMAP
+/*
+ * Guest ptnetmap txsync()/rxsync() routines, used in ptnet device drivers.
+ * These routines are reused across the different operating systems supported
+ * by netmap.
+ */
+
+/*
+ * Reconcile host and guest views of the transmit ring.
+ *
+ * Guest user wants to transmit packets up to the one before ring->head,
+ * and guest kernel knows tx_ring->hwcur is the first packet unsent
+ * by the host kernel.
+ *
+ * We push out as many packets as possible, and possibly
+ * reclaim buffers from previously completed transmission.
+ *
+ * Notifications from the host are enabled only if the user guest would
+ * block (no space in the ring).
+ */
+bool
+netmap_pt_guest_txsync(struct nm_csb_atok *atok, struct nm_csb_ktoa *ktoa,
+			struct netmap_kring *kring, int flags)
+{
+	bool notify = false;
+
+	/* Disable notifications */
+	atok->appl_need_kick = 0;
+
+	/*
+	 * First part: tell the host to process the new packets,
+	 * updating the CSB.
+	 */
+	kring->nr_hwcur = ktoa->hwcur;
+	nm_sync_kloop_appl_write(atok, kring->rcur, kring->rhead);
+
+        /* Ask for a kick from a guest to the host if needed. */
+	if (((kring->rhead != kring->nr_hwcur || nm_kr_wouldblock(kring))
+		&& NM_ACCESS_ONCE(ktoa->kern_need_kick)) ||
+			(flags & NAF_FORCE_RECLAIM)) {
+		atok->sync_flags = flags;
+		notify = true;
+	}
+
+	/*
+	 * Second part: reclaim buffers for completed transmissions.
+	 */
+	if (nm_kr_wouldblock(kring) || (flags & NAF_FORCE_RECLAIM)) {
+		nm_sync_kloop_appl_read(ktoa, &kring->nr_hwtail,
+					&kring->nr_hwcur);
+	}
+
+        /*
+         * No more room in the ring for new transmissions. The user thread will
+	 * go to sleep and we need to be notified by the host when more free
+	 * space is available.
+         */
+	if (nm_kr_wouldblock(kring) && !(kring->nr_kflags & NKR_NOINTR)) {
+		/* Re-enable notifications. */
+		atok->appl_need_kick = 1;
+                /* Double check, with store-load memory barrier. */
+		nm_stld_barrier();
+		nm_sync_kloop_appl_read(ktoa, &kring->nr_hwtail,
+					&kring->nr_hwcur);
+                /* If there is new free space, disable notifications */
+		if (unlikely(!nm_kr_wouldblock(kring))) {
+			atok->appl_need_kick = 0;
+		}
+	}
+
+	nm_prdis(1, "%s CSB(head:%u cur:%u hwtail:%u) KRING(head:%u cur:%u tail:%u)",
+		kring->name, atok->head, atok->cur, ktoa->hwtail,
+		kring->rhead, kring->rcur, kring->nr_hwtail);
+
+	return notify;
+}
+
+/*
+ * Reconcile host and guest view of the receive ring.
+ *
+ * Update hwcur/hwtail from host (reading from CSB).
+ *
+ * If guest user has released buffers up to the one before ring->head, we
+ * also give them to the host.
+ *
+ * Notifications from the host are enabled only if the user guest would
+ * block (no more completed slots in the ring).
+ */
+bool
+netmap_pt_guest_rxsync(struct nm_csb_atok *atok, struct nm_csb_ktoa *ktoa,
+			struct netmap_kring *kring, int flags)
+{
+	bool notify = false;
+
+        /* Disable notifications */
+	atok->appl_need_kick = 0;
+
+	/*
+	 * First part: import newly received packets, by updating the kring
+	 * hwtail to the hwtail known from the host (read from the CSB).
+	 * This also updates the kring hwcur.
+	 */
+	nm_sync_kloop_appl_read(ktoa, &kring->nr_hwtail, &kring->nr_hwcur);
+	kring->nr_kflags &= ~NKR_PENDINTR;
+
+	/*
+	 * Second part: tell the host about the slots that guest user has
+	 * released, by updating cur and head in the CSB.
+	 */
+	if (kring->rhead != kring->nr_hwcur) {
+		nm_sync_kloop_appl_write(atok, kring->rcur, kring->rhead);
+	}
+
+        /*
+         * No more completed RX slots. The user thread will go to sleep and
+	 * we need to be notified by the host when more RX slots have been
+	 * completed.
+         */
+	if (nm_kr_wouldblock(kring) && !(kring->nr_kflags & NKR_NOINTR)) {
+		/* Re-enable notifications. */
+                atok->appl_need_kick = 1;
+                /* Double check, with store-load memory barrier. */
+		nm_stld_barrier();
+		nm_sync_kloop_appl_read(ktoa, &kring->nr_hwtail,
+					&kring->nr_hwcur);
+                /* If there are new slots, disable notifications. */
+		if (!nm_kr_wouldblock(kring)) {
+                        atok->appl_need_kick = 0;
+                }
+        }
+
+	/* Ask for a kick from the guest to the host if needed. */
+	if ((kring->rhead != kring->nr_hwcur || nm_kr_wouldblock(kring))
+		&& NM_ACCESS_ONCE(ktoa->kern_need_kick)) {
+		atok->sync_flags = flags;
+		notify = true;
+	}
+
+	nm_prdis(1, "%s CSB(head:%u cur:%u hwtail:%u) KRING(head:%u cur:%u tail:%u)",
+		kring->name, atok->head, atok->cur, ktoa->hwtail,
+		kring->rhead, kring->rcur, kring->nr_hwtail);
+
+	return notify;
+}
+
+/*
+ * Callbacks for ptnet drivers: nm_krings_create, nm_krings_delete, nm_dtor.
+ */
+int
+ptnet_nm_krings_create(struct netmap_adapter *na)
+{
+	struct netmap_pt_guest_adapter *ptna =
+			(struct netmap_pt_guest_adapter *)na; /* Upcast. */
+	struct netmap_adapter *na_nm = &ptna->hwup.up;
+	struct netmap_adapter *na_dr = &ptna->dr.up;
+	int ret;
+
+	if (ptna->backend_users) {
+		return 0;
+	}
+
+	/* Create krings on the public netmap adapter. */
+	ret = netmap_hw_krings_create(na_nm);
+	if (ret) {
+		return ret;
+	}
+
+	/* Copy krings into the netmap adapter private to the driver. */
+	na_dr->tx_rings = na_nm->tx_rings;
+	na_dr->rx_rings = na_nm->rx_rings;
+
+	return 0;
+}
+
+void
+ptnet_nm_krings_delete(struct netmap_adapter *na)
+{
+	struct netmap_pt_guest_adapter *ptna =
+			(struct netmap_pt_guest_adapter *)na; /* Upcast. */
+	struct netmap_adapter *na_nm = &ptna->hwup.up;
+	struct netmap_adapter *na_dr = &ptna->dr.up;
+
+	if (ptna->backend_users) {
+		return;
+	}
+
+	na_dr->tx_rings = NULL;
+	na_dr->rx_rings = NULL;
+
+	netmap_hw_krings_delete(na_nm);
+}
+
+void
+ptnet_nm_dtor(struct netmap_adapter *na)
+{
+	struct netmap_pt_guest_adapter *ptna =
+			(struct netmap_pt_guest_adapter *)na;
+
+	netmap_mem_put(ptna->dr.up.nm_mem);
+	memset(&ptna->dr, 0, sizeof(ptna->dr));
+	netmap_mem_pt_guest_ifp_del(na->nm_mem, na->ifp);
+}
+
+int
+netmap_pt_guest_attach(struct netmap_adapter *arg,
+		       unsigned int nifp_offset, unsigned int memid)
+{
+	struct netmap_pt_guest_adapter *ptna;
+	if_t ifp = arg ? arg->ifp : NULL;
+	int error;
+
+	/* get allocator */
+	arg->nm_mem = netmap_mem_pt_guest_new(ifp, nifp_offset, memid);
+	if (arg->nm_mem == NULL)
+		return ENOMEM;
+	arg->na_flags |= NAF_MEM_OWNER;
+	error = netmap_attach_ext(arg, sizeof(struct netmap_pt_guest_adapter), 1);
+	if (error)
+		return error;
+
+	/* get the netmap_pt_guest_adapter */
+	ptna = (struct netmap_pt_guest_adapter *) NA(ifp);
+
+	/* Initialize a separate pass-through netmap adapter that is going to
+	 * be used by the ptnet driver only, and so never exposed to netmap
+         * applications. We only need a subset of the available fields. */
+	memset(&ptna->dr, 0, sizeof(ptna->dr));
+	ptna->dr.up.ifp = ifp;
+	ptna->dr.up.nm_mem = netmap_mem_get(ptna->hwup.up.nm_mem);
+        ptna->dr.up.nm_config = ptna->hwup.up.nm_config;
+
+	ptna->backend_users = 0;
+
+	return 0;
+}
+
+#endif /* WITH_PTNETMAP */
diff --git a/sys/dev/netmap/netmap_legacy.c b/sys/dev/netmap/netmap_legacy.c
new file mode 100644
index 000000000..b52bf24e1
--- /dev/null
+++ b/sys/dev/netmap/netmap_legacy.c
@@ -0,0 +1,437 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018 Vincenzo Maffione
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(__FreeBSD__)
+#include  /* prerequisite */
+#include 
+#include 	/* defines used in kernel.h */
+#include 	/* FIONBIO */
+#include 
+#include 	/* struct socket */
+#include  /* sockaddrs */
+#include 
+#include 
+#include 
+#include 		/* BIOCIMMEDIATE */
+#include 	/* bus_dmamap_* */
+#include 
+#elif defined(linux)
+#include "bsd_glue.h"
+#elif defined(__APPLE__)
+#warning OSX support is only partial
+#include "osx_glue.h"
+#elif defined (_WIN32)
+#include "win_glue.h"
+#endif
+
+/*
+ * common headers
+ */
+#include 
+#include 
+#include 
+
+static int
+nmreq_register_from_legacy(struct nmreq *nmr, struct nmreq_header *hdr,
+				struct nmreq_register *req)
+{
+	req->nr_offset = nmr->nr_offset;
+	req->nr_memsize = nmr->nr_memsize;
+	req->nr_tx_slots = nmr->nr_tx_slots;
+	req->nr_rx_slots = nmr->nr_rx_slots;
+	req->nr_tx_rings = nmr->nr_tx_rings;
+	req->nr_rx_rings = nmr->nr_rx_rings;
+	req->nr_host_tx_rings = 0;
+	req->nr_host_rx_rings = 0;
+	req->nr_mem_id = nmr->nr_arg2;
+	req->nr_ringid = nmr->nr_ringid & NETMAP_RING_MASK;
+	if ((nmr->nr_flags & NR_REG_MASK) == NR_REG_DEFAULT) {
+		/* Convert the older nmr->nr_ringid (original
+		 * netmap control API) to nmr->nr_flags. */
+		u_int regmode = NR_REG_DEFAULT;
+		if (nmr->nr_ringid & NETMAP_SW_RING) {
+			regmode = NR_REG_SW;
+		} else if (nmr->nr_ringid & NETMAP_HW_RING) {
+			regmode = NR_REG_ONE_NIC;
+		} else {
+			regmode = NR_REG_ALL_NIC;
+		}
+		req->nr_mode = regmode;
+	} else {
+		req->nr_mode = nmr->nr_flags & NR_REG_MASK;
+	}
+
+	/* Fix nr_name, nr_mode and nr_ringid to handle pipe requests. */
+	if (req->nr_mode == NR_REG_PIPE_MASTER ||
+			req->nr_mode == NR_REG_PIPE_SLAVE) {
+		char suffix[10];
+		snprintf(suffix, sizeof(suffix), "%c%d",
+			(req->nr_mode == NR_REG_PIPE_MASTER ? '{' : '}'),
+			req->nr_ringid);
+		if (strlen(hdr->nr_name) + strlen(suffix)
+					>= sizeof(hdr->nr_name)) {
+			/* No space for the pipe suffix. */
+			return ENOBUFS;
+		}
+		strlcat(hdr->nr_name, suffix, sizeof(hdr->nr_name));
+		req->nr_mode = NR_REG_ALL_NIC;
+		req->nr_ringid = 0;
+	}
+	req->nr_flags = nmr->nr_flags & (~NR_REG_MASK);
+	if (nmr->nr_ringid & NETMAP_NO_TX_POLL) {
+		req->nr_flags |= NR_NO_TX_POLL;
+	}
+	if (nmr->nr_ringid & NETMAP_DO_RX_POLL) {
+		req->nr_flags |= NR_DO_RX_POLL;
+	}
+	/* nmr->nr_arg1 (nr_pipes) ignored */
+	req->nr_extra_bufs = nmr->nr_arg3;
+
+	return 0;
+}
+
+/* Convert the legacy 'nmr' struct into one of the nmreq_xyz structs
+ * (new API). The new struct is dynamically allocated. */
+static struct nmreq_header *
+nmreq_from_legacy(struct nmreq *nmr, u_long ioctl_cmd)
+{
+	struct nmreq_header *hdr = nm_os_malloc(sizeof(*hdr));
+
+	if (hdr == NULL) {
+		goto oom;
+	}
+
+	/* Sanitize nmr->nr_name by adding the string terminator. */
+	if (ioctl_cmd == NIOCGINFO || ioctl_cmd == NIOCREGIF) {
+		nmr->nr_name[sizeof(nmr->nr_name) - 1] = '\0';
+	}
+
+	/* First prepare the request header. */
+	hdr->nr_version = NETMAP_API; /* new API */
+	strlcpy(hdr->nr_name, nmr->nr_name, sizeof(nmr->nr_name));
+	hdr->nr_options = (uintptr_t)NULL;
+	hdr->nr_body = (uintptr_t)NULL;
+
+	switch (ioctl_cmd) {
+	case NIOCREGIF: {
+		switch (nmr->nr_cmd) {
+		case 0: {
+			/* Regular NIOCREGIF operation. */
+			struct nmreq_register *req = nm_os_malloc(sizeof(*req));
+			if (!req) { goto oom; }
+			hdr->nr_body = (uintptr_t)req;
+			hdr->nr_reqtype = NETMAP_REQ_REGISTER;
+			if (nmreq_register_from_legacy(nmr, hdr, req)) {
+				goto oom;
+			}
+			break;
+		}
+		case NETMAP_BDG_ATTACH: {
+			struct nmreq_vale_attach *req = nm_os_malloc(sizeof(*req));
+			if (!req) { goto oom; }
+			hdr->nr_body = (uintptr_t)req;
+			hdr->nr_reqtype = NETMAP_REQ_VALE_ATTACH;
+			if (nmreq_register_from_legacy(nmr, hdr, &req->reg)) {
+				goto oom;
+			}
+			/* Fix nr_mode, starting from nr_arg1. */
+			if (nmr->nr_arg1 & NETMAP_BDG_HOST) {
+				req->reg.nr_mode = NR_REG_NIC_SW;
+			} else {
+				req->reg.nr_mode = NR_REG_ALL_NIC;
+			}
+			break;
+		}
+		case NETMAP_BDG_DETACH: {
+			hdr->nr_reqtype = NETMAP_REQ_VALE_DETACH;
+			hdr->nr_body = (uintptr_t)nm_os_malloc(sizeof(struct nmreq_vale_detach));
+			break;
+		}
+		case NETMAP_BDG_VNET_HDR:
+		case NETMAP_VNET_HDR_GET: {
+			struct nmreq_port_hdr *req = nm_os_malloc(sizeof(*req));
+			if (!req) { goto oom; }
+			hdr->nr_body = (uintptr_t)req;
+			hdr->nr_reqtype = (nmr->nr_cmd == NETMAP_BDG_VNET_HDR) ?
+				NETMAP_REQ_PORT_HDR_SET : NETMAP_REQ_PORT_HDR_GET;
+			req->nr_hdr_len = nmr->nr_arg1;
+			break;
+		}
+		case NETMAP_BDG_NEWIF : {
+			struct nmreq_vale_newif *req = nm_os_malloc(sizeof(*req));
+			if (!req) { goto oom; }
+			hdr->nr_body = (uintptr_t)req;
+			hdr->nr_reqtype = NETMAP_REQ_VALE_NEWIF;
+			req->nr_tx_slots = nmr->nr_tx_slots;
+			req->nr_rx_slots = nmr->nr_rx_slots;
+			req->nr_tx_rings = nmr->nr_tx_rings;
+			req->nr_rx_rings = nmr->nr_rx_rings;
+			req->nr_mem_id = nmr->nr_arg2;
+			break;
+		}
+		case NETMAP_BDG_DELIF: {
+			hdr->nr_reqtype = NETMAP_REQ_VALE_DELIF;
+			break;
+		}
+		case NETMAP_BDG_POLLING_ON:
+		case NETMAP_BDG_POLLING_OFF: {
+			struct nmreq_vale_polling *req = nm_os_malloc(sizeof(*req));
+			if (!req) { goto oom; }
+			hdr->nr_body = (uintptr_t)req;
+			hdr->nr_reqtype = (nmr->nr_cmd == NETMAP_BDG_POLLING_ON) ?
+				NETMAP_REQ_VALE_POLLING_ENABLE :
+				NETMAP_REQ_VALE_POLLING_DISABLE;
+			switch (nmr->nr_flags & NR_REG_MASK) {
+			default:
+				req->nr_mode = 0; /* invalid */
+				break;
+			case NR_REG_ONE_NIC:
+				req->nr_mode = NETMAP_POLLING_MODE_MULTI_CPU;
+				break;
+			case NR_REG_ALL_NIC:
+				req->nr_mode = NETMAP_POLLING_MODE_SINGLE_CPU;
+				break;
+			}
+			req->nr_first_cpu_id = nmr->nr_ringid & NETMAP_RING_MASK;
+			req->nr_num_polling_cpus = nmr->nr_arg1;
+			break;
+		}
+		case NETMAP_PT_HOST_CREATE:
+		case NETMAP_PT_HOST_DELETE: {
+			nm_prerr("Netmap passthrough not supported yet");
+			return NULL;
+			break;
+		}
+		}
+		break;
+	}
+	case NIOCGINFO: {
+		if (nmr->nr_cmd == NETMAP_BDG_LIST) {
+			struct nmreq_vale_list *req = nm_os_malloc(sizeof(*req));
+			if (!req) { goto oom; }
+			hdr->nr_body = (uintptr_t)req;
+			hdr->nr_reqtype = NETMAP_REQ_VALE_LIST;
+			req->nr_bridge_idx = nmr->nr_arg1;
+			req->nr_port_idx = nmr->nr_arg2;
+		} else {
+			/* Regular NIOCGINFO. */
+			struct nmreq_port_info_get *req = nm_os_malloc(sizeof(*req));
+			if (!req) { goto oom; }
+			hdr->nr_body = (uintptr_t)req;
+			hdr->nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
+			req->nr_memsize = nmr->nr_memsize;
+			req->nr_tx_slots = nmr->nr_tx_slots;
+			req->nr_rx_slots = nmr->nr_rx_slots;
+			req->nr_tx_rings = nmr->nr_tx_rings;
+			req->nr_rx_rings = nmr->nr_rx_rings;
+			req->nr_host_tx_rings = 0;
+			req->nr_host_rx_rings = 0;
+			req->nr_mem_id = nmr->nr_arg2;
+		}
+		break;
+	}
+	}
+
+	return hdr;
+oom:
+	if (hdr) {
+		if (hdr->nr_body) {
+			nm_os_free((void *)(uintptr_t)hdr->nr_body);
+		}
+		nm_os_free(hdr);
+	}
+	nm_prerr("Failed to allocate memory for nmreq_xyz struct");
+
+	return NULL;
+}
+
+static void
+nmreq_register_to_legacy(const struct nmreq_register *req, struct nmreq *nmr)
+{
+	nmr->nr_offset = req->nr_offset;
+	nmr->nr_memsize = req->nr_memsize;
+	nmr->nr_tx_slots = req->nr_tx_slots;
+	nmr->nr_rx_slots = req->nr_rx_slots;
+	nmr->nr_tx_rings = req->nr_tx_rings;
+	nmr->nr_rx_rings = req->nr_rx_rings;
+	nmr->nr_arg2 = req->nr_mem_id;
+	nmr->nr_arg3 = req->nr_extra_bufs;
+}
+
+/* Convert a nmreq_xyz struct (new API) to the legacy 'nmr' struct.
+ * It also frees the nmreq_xyz struct, as it was allocated by
+ * nmreq_from_legacy(). */
+static int
+nmreq_to_legacy(struct nmreq_header *hdr, struct nmreq *nmr)
+{
+	int ret = 0;
+
+	/* We only write-back the fields that the user expects to be
+	 * written back. */
+	switch (hdr->nr_reqtype) {
+	case NETMAP_REQ_REGISTER: {
+		struct nmreq_register *req =
+			(struct nmreq_register *)(uintptr_t)hdr->nr_body;
+		nmreq_register_to_legacy(req, nmr);
+		break;
+	}
+	case NETMAP_REQ_PORT_INFO_GET: {
+		struct nmreq_port_info_get *req =
+			(struct nmreq_port_info_get *)(uintptr_t)hdr->nr_body;
+		nmr->nr_memsize = req->nr_memsize;
+		nmr->nr_tx_slots = req->nr_tx_slots;
+		nmr->nr_rx_slots = req->nr_rx_slots;
+		nmr->nr_tx_rings = req->nr_tx_rings;
+		nmr->nr_rx_rings = req->nr_rx_rings;
+		nmr->nr_arg2 = req->nr_mem_id;
+		break;
+	}
+	case NETMAP_REQ_VALE_ATTACH: {
+		struct nmreq_vale_attach *req =
+			(struct nmreq_vale_attach *)(uintptr_t)hdr->nr_body;
+		nmreq_register_to_legacy(&req->reg, nmr);
+		break;
+	}
+	case NETMAP_REQ_VALE_DETACH: {
+		break;
+	}
+	case NETMAP_REQ_VALE_LIST: {
+		struct nmreq_vale_list *req =
+			(struct nmreq_vale_list *)(uintptr_t)hdr->nr_body;
+		strlcpy(nmr->nr_name, hdr->nr_name, sizeof(nmr->nr_name));
+		nmr->nr_arg1 = req->nr_bridge_idx;
+		nmr->nr_arg2 = req->nr_port_idx;
+		break;
+	}
+	case NETMAP_REQ_PORT_HDR_SET:
+	case NETMAP_REQ_PORT_HDR_GET: {
+		struct nmreq_port_hdr *req =
+			(struct nmreq_port_hdr *)(uintptr_t)hdr->nr_body;
+		nmr->nr_arg1 = req->nr_hdr_len;
+		break;
+	}
+	case NETMAP_REQ_VALE_NEWIF: {
+		struct nmreq_vale_newif *req =
+			(struct nmreq_vale_newif *)(uintptr_t)hdr->nr_body;
+		nmr->nr_tx_slots = req->nr_tx_slots;
+		nmr->nr_rx_slots = req->nr_rx_slots;
+		nmr->nr_tx_rings = req->nr_tx_rings;
+		nmr->nr_rx_rings = req->nr_rx_rings;
+		nmr->nr_arg2 = req->nr_mem_id;
+		break;
+	}
+	case NETMAP_REQ_VALE_DELIF:
+	case NETMAP_REQ_VALE_POLLING_ENABLE:
+	case NETMAP_REQ_VALE_POLLING_DISABLE: {
+		break;
+	}
+	}
+
+	return ret;
+}
+
+int
+netmap_ioctl_legacy(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
+			struct thread *td)
+{
+	int error = 0;
+
+	switch (cmd) {
+	case NIOCGINFO:
+	case NIOCREGIF: {
+		/* Request for the legacy control API. Convert it to a
+		 * NIOCCTRL request. */
+		struct nmreq *nmr = (struct nmreq *) data;
+		struct nmreq_header *hdr;
+
+		if (nmr->nr_version < 14) {
+			nm_prerr("Minimum supported API is 14 (requested %u)",
+			    nmr->nr_version);
+			return EINVAL;
+		}
+		hdr = nmreq_from_legacy(nmr, cmd);
+		if (hdr == NULL) { /* out of memory */
+			return ENOMEM;
+		}
+		error = netmap_ioctl(priv, NIOCCTRL, (caddr_t)hdr, td,
+					/*nr_body_is_user=*/0);
+		if (error == 0) {
+			nmreq_to_legacy(hdr, nmr);
+		}
+		if (hdr->nr_body) {
+			nm_os_free((void *)(uintptr_t)hdr->nr_body);
+		}
+		nm_os_free(hdr);
+		break;
+	}
+#ifdef WITH_VALE
+	case NIOCCONFIG: {
+		struct nm_ifreq *nr = (struct nm_ifreq *)data;
+		error = netmap_bdg_config(nr);
+		break;
+	}
+#endif
+#ifdef __FreeBSD__
+	case FIONBIO:
+	case FIOASYNC:
+		/* FIONBIO/FIOASYNC are no-ops. */
+		break;
+
+	case BIOCIMMEDIATE:
+	case BIOCGHDRCMPLT:
+	case BIOCSHDRCMPLT:
+	case BIOCSSEESENT:
+		/* Ignore these commands. */
+		break;
+
+	default:	/* allow device-specific ioctls */
+	    {
+		struct nmreq *nmr = (struct nmreq *)data;
+		if_t ifp = ifunit_ref(nmr->nr_name);
+		if (ifp == NULL) {
+			error = ENXIO;
+		} else {
+			struct socket so;
+
+			bzero(&so, sizeof(so));
+			so.so_vnet = if_getvnet(ifp);
+			// so->so_proto not null.
+			error = ifioctl(&so, cmd, data, td);
+			if_rele(ifp);
+		}
+		break;
+	    }
+
+#else /* linux */
+	default:
+		error = EOPNOTSUPP;
+#endif /* linux */
+	}
+
+	return error;
+}
diff --git a/sys/dev/netmap/netmap_mbq.c b/sys/dev/netmap/netmap_mbq.c
index 3eb971b74..6b1e0da07 100644
--- a/sys/dev/netmap/netmap_mbq.c
+++ b/sys/dev/netmap/netmap_mbq.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2013-2014 Vincenzo Maffione
  * All rights reserved.
  *
@@ -24,11 +26,6 @@
  * SUCH DAMAGE.
  */
 
-/*
- * $FreeBSD$
- */
-
-
 #ifdef linux
 #include "bsd_glue.h"
 #elif defined (_WIN32)
diff --git a/sys/dev/netmap/netmap_mbq.h b/sys/dev/netmap/netmap_mbq.h
index 8ba0947b5..b8b811c64 100644
--- a/sys/dev/netmap/netmap_mbq.h
+++ b/sys/dev/netmap/netmap_mbq.h
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2013-2014 Vincenzo Maffione
  * All rights reserved.
  *
@@ -24,11 +26,6 @@
  * SUCH DAMAGE.
  */
 
-/*
- * $FreeBSD$
- */
-
-
 #ifndef _NET_NETMAP_MBQ_H__
 #define _NET_NETMAP_MBQ_H__
 
diff --git a/sys/dev/netmap/netmap_mem2.c b/sys/dev/netmap/netmap_mem2.c
index b4c0d90df..9a3e5a7b0 100644
--- a/sys/dev/netmap/netmap_mem2.c
+++ b/sys/dev/netmap/netmap_mem2.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2012-2014 Matteo Landi
  * Copyright (C) 2012-2016 Luigi Rizzo
  * Copyright (C) 2012-2016 Giuseppe Lettieri
@@ -36,8 +38,6 @@
 
 #ifdef __FreeBSD__
 #include  /* prerequisite */
-__FBSDID("$FreeBSD: head/sys/dev/netmap/netmap.c 241723 2012-10-19 09:41:45Z glebius $");
-
 #include 
 #include 
 #include 		/* MALLOC_DEFINE */
@@ -98,15 +98,18 @@ struct netmap_obj_pool {
 	/* ---------------------------------------------------*/
 	/* these are only meaningful if the pool is finalized */
 	/* (see 'finalized' field in netmap_mem_d)            */
-	u_int objtotal;         /* actual total number of objects. */
-	u_int memtotal;		/* actual total memory space */
-	u_int numclusters;	/* actual number of clusters */
-
-	u_int objfree;          /* number of free objects. */
+	size_t memtotal;	/* actual total memory space */
 
 	struct lut_entry *lut;  /* virt,phys addresses, objtotal entries */
 	uint32_t *bitmap;       /* one bit per buffer, 1 means free */
+	uint32_t *invalid_bitmap;/* one bit per buffer, 1 means invalid */
 	uint32_t bitmap_slots;	/* number of uint32 entries in bitmap */
+
+	u_int objtotal;         /* actual total number of objects. */
+	u_int numclusters;	/* actual number of clusters */
+	u_int objfree;          /* number of free objects. */
+
+	int	alloc_done;	/* we have allocated the memory */
 	/* ---------------------------------------------------*/
 
 	/* limits */
@@ -128,34 +131,42 @@ struct netmap_obj_pool {
 };
 
 #define NMA_LOCK_T		NM_MTX_T
-
+#define NMA_LOCK_INIT(n)	NM_MTX_INIT((n)->nm_mtx)
+#define NMA_LOCK_DESTROY(n)	NM_MTX_DESTROY((n)->nm_mtx)
+#define NMA_LOCK(n)		NM_MTX_LOCK((n)->nm_mtx)
+#define NMA_SPINLOCK(n)         NM_MTX_SPINLOCK((n)->nm_mtx)
+#define NMA_UNLOCK(n)		NM_MTX_UNLOCK((n)->nm_mtx)
 
 struct netmap_mem_ops {
 	int (*nmd_get_lut)(struct netmap_mem_d *, struct netmap_lut*);
-	int  (*nmd_get_info)(struct netmap_mem_d *, u_int *size,
+	int  (*nmd_get_info)(struct netmap_mem_d *, uint64_t *size,
 			u_int *memflags, uint16_t *id);
 
 	vm_paddr_t (*nmd_ofstophys)(struct netmap_mem_d *, vm_ooffset_t);
 	int (*nmd_config)(struct netmap_mem_d *);
-	int (*nmd_finalize)(struct netmap_mem_d *);
-	void (*nmd_deref)(struct netmap_mem_d *);
+	int (*nmd_finalize)(struct netmap_mem_d *, struct netmap_adapter *);
+	void (*nmd_deref)(struct netmap_mem_d *, struct netmap_adapter *);
 	ssize_t  (*nmd_if_offset)(struct netmap_mem_d *, const void *vaddr);
 	void (*nmd_delete)(struct netmap_mem_d *);
 
-	struct netmap_if * (*nmd_if_new)(struct netmap_adapter *,
-					 struct netmap_priv_d *);
-	void (*nmd_if_delete)(struct netmap_adapter *, struct netmap_if *);
-	int  (*nmd_rings_create)(struct netmap_adapter *);
-	void (*nmd_rings_delete)(struct netmap_adapter *);
+	struct netmap_if * (*nmd_if_new)(struct netmap_mem_d *,
+			struct netmap_adapter *, struct netmap_priv_d *);
+	void (*nmd_if_delete)(struct netmap_mem_d *,
+			struct netmap_adapter *, struct netmap_if *);
+	int  (*nmd_rings_create)(struct netmap_mem_d *,
+			struct netmap_adapter *);
+	void (*nmd_rings_delete)(struct netmap_mem_d *,
+			struct netmap_adapter *);
 };
 
 struct netmap_mem_d {
 	NMA_LOCK_T nm_mtx;  /* protect the allocator */
-	u_int nm_totalsize; /* shorthand */
+	size_t nm_totalsize; /* shorthand */
 
 	u_int flags;
 #define NETMAP_MEM_FINALIZED	0x1	/* preallocation done */
-#define NETMAP_MEM_HIDDEN	0x8	/* beeing prepared */
+#define NETMAP_MEM_HIDDEN	0x8	/* being prepared */
+#define NETMAP_MEM_NOMAP	0x10	/* do not map/unmap pdevs */
 	int lasterr;		/* last error for curr config */
 	int active;		/* active users */
 	int refcount;
@@ -163,7 +174,7 @@ struct netmap_mem_d {
 	struct netmap_obj_pool pools[NETMAP_POOLS_NR];
 
 	nm_memid_t nm_id;	/* allocator identifier */
-	int nm_grp;	/* iommu groupd id */
+	int nm_grp;	/* iommu group id */
 
 	/* list of all existing allocators, sorted by nm_id */
 	struct netmap_mem_d *prev, *next;
@@ -176,60 +187,130 @@ struct netmap_mem_d {
 	char name[NM_MEM_NAMESZ];
 };
 
-/*
- * XXX need to fix the case of t0 == void
- */
-#define NMD_DEFCB(t0, name) \
-t0 \
-netmap_mem_##name(struct netmap_mem_d *nmd) \
-{ \
-	return nmd->ops->nmd_##name(nmd); \
+int
+netmap_mem_get_lut(struct netmap_mem_d *nmd, struct netmap_lut *lut)
+{
+	int rv;
+
+	NMA_LOCK(nmd);
+	rv = nmd->ops->nmd_get_lut(nmd, lut);
+	NMA_UNLOCK(nmd);
+
+	return rv;
+}
+
+int
+netmap_mem_get_info(struct netmap_mem_d *nmd, uint64_t *size,
+		u_int *memflags, nm_memid_t *memid)
+{
+	int rv;
+
+	NMA_LOCK(nmd);
+	rv = nmd->ops->nmd_get_info(nmd, size, memflags, memid);
+	NMA_UNLOCK(nmd);
+
+	return rv;
+}
+
+vm_paddr_t
+netmap_mem_ofstophys(struct netmap_mem_d *nmd, vm_ooffset_t off)
+{
+	vm_paddr_t pa;
+
+#if defined(__FreeBSD__)
+	/* This function is called by netmap_dev_pager_fault(), which holds a
+	 * non-sleepable lock since FreeBSD 12. Since we cannot sleep, we
+	 * spin on the trylock. */
+	NMA_SPINLOCK(nmd);
+#else
+	NMA_LOCK(nmd);
+#endif
+	pa = nmd->ops->nmd_ofstophys(nmd, off);
+	NMA_UNLOCK(nmd);
+
+	return pa;
+}
+
+static int
+netmap_mem_config(struct netmap_mem_d *nmd)
+{
+	if (nmd->active) {
+		/* already in use. Not fatal, but we
+		 * cannot change the configuration
+		 */
+		return 0;
+	}
+
+	return nmd->ops->nmd_config(nmd);
+}
+
+ssize_t
+netmap_mem_if_offset(struct netmap_mem_d *nmd, const void *off)
+{
+	ssize_t rv;
+
+	NMA_LOCK(nmd);
+	rv = nmd->ops->nmd_if_offset(nmd, off);
+	NMA_UNLOCK(nmd);
+
+	return rv;
 }
 
-#define NMD_DEFCB1(t0, name, t1) \
-t0 \
-netmap_mem_##name(struct netmap_mem_d *nmd, t1 a1) \
-{ \
-	return nmd->ops->nmd_##name(nmd, a1); \
+static void
+netmap_mem_delete(struct netmap_mem_d *nmd)
+{
+	nmd->ops->nmd_delete(nmd);
 }
 
-#define NMD_DEFCB3(t0, name, t1, t2, t3) \
-t0 \
-netmap_mem_##name(struct netmap_mem_d *nmd, t1 a1, t2 a2, t3 a3) \
-{ \
-	return nmd->ops->nmd_##name(nmd, a1, a2, a3); \
+struct netmap_if *
+netmap_mem_if_new(struct netmap_adapter *na, struct netmap_priv_d *priv)
+{
+	struct netmap_if *nifp;
+	struct netmap_mem_d *nmd = na->nm_mem;
+
+	NMA_LOCK(nmd);
+	nifp = nmd->ops->nmd_if_new(nmd, na, priv);
+	NMA_UNLOCK(nmd);
+
+	return nifp;
 }
 
-#define NMD_DEFNACB(t0, name) \
-t0 \
-netmap_mem_##name(struct netmap_adapter *na) \
-{ \
-	return na->nm_mem->ops->nmd_##name(na); \
+void
+netmap_mem_if_delete(struct netmap_adapter *na, struct netmap_if *nif)
+{
+	struct netmap_mem_d *nmd = na->nm_mem;
+
+	NMA_LOCK(nmd);
+	nmd->ops->nmd_if_delete(nmd, na, nif);
+	NMA_UNLOCK(nmd);
 }
 
-#define NMD_DEFNACB1(t0, name, t1) \
-t0 \
-netmap_mem_##name(struct netmap_adapter *na, t1 a1) \
-{ \
-	return na->nm_mem->ops->nmd_##name(na, a1); \
+int
+netmap_mem_rings_create(struct netmap_adapter *na)
+{
+	int rv;
+	struct netmap_mem_d *nmd = na->nm_mem;
+
+	NMA_LOCK(nmd);
+	rv = nmd->ops->nmd_rings_create(nmd, na);
+	NMA_UNLOCK(nmd);
+
+	return rv;
 }
 
-NMD_DEFCB1(int, get_lut, struct netmap_lut *);
-NMD_DEFCB3(int, get_info, u_int *, u_int *, uint16_t *);
-NMD_DEFCB1(vm_paddr_t, ofstophys, vm_ooffset_t);
-static int netmap_mem_config(struct netmap_mem_d *);
-NMD_DEFCB(int, config);
-NMD_DEFCB1(ssize_t, if_offset, const void *);
-NMD_DEFCB(void, delete);
+void
+netmap_mem_rings_delete(struct netmap_adapter *na)
+{
+	struct netmap_mem_d *nmd = na->nm_mem;
 
-NMD_DEFNACB1(struct netmap_if *, if_new, struct netmap_priv_d *);
-NMD_DEFNACB1(void, if_delete, struct netmap_if *);
-NMD_DEFNACB(int, rings_create);
-NMD_DEFNACB(void, rings_delete);
+	NMA_LOCK(nmd);
+	nmd->ops->nmd_rings_delete(nmd, na);
+	NMA_UNLOCK(nmd);
+}
 
 static int netmap_mem_map(struct netmap_obj_pool *, struct netmap_adapter *);
 static int netmap_mem_unmap(struct netmap_obj_pool *, struct netmap_adapter *);
-static int nm_mem_assign_group(struct netmap_mem_d *, struct device *);
+static int nm_mem_check_group(struct netmap_mem_d *, bus_dma_tag_t);
 static void nm_mem_release_id(struct netmap_mem_d *);
 
 nm_memid_t
@@ -238,24 +319,19 @@ netmap_mem_get_id(struct netmap_mem_d *nmd)
 	return nmd->nm_id;
 }
 
-#define NMA_LOCK_INIT(n)	NM_MTX_INIT((n)->nm_mtx)
-#define NMA_LOCK_DESTROY(n)	NM_MTX_DESTROY((n)->nm_mtx)
-#define NMA_LOCK(n)		NM_MTX_LOCK((n)->nm_mtx)
-#define NMA_UNLOCK(n)		NM_MTX_UNLOCK((n)->nm_mtx)
-
 #ifdef NM_DEBUG_MEM_PUTGET
 #define NM_DBG_REFC(nmd, func, line)	\
-	printf("%s:%d mem[%d] -> %d\n", func, line, (nmd)->nm_id, (nmd)->refcount);
+	nm_prinf("%s:%d mem[%d:%d] -> %d", func, line, (nmd)->nm_id, (nmd)->nm_grp, (nmd)->refcount);
 #else
 #define NM_DBG_REFC(nmd, func, line)
 #endif
 
 /* circular list of all existing allocators */
 static struct netmap_mem_d *netmap_last_mem_d = &nm_mem;
-NM_MTX_T nm_mem_list_lock;
+static NM_MTX_T nm_mem_list_lock;
 
 struct netmap_mem_d *
-netmap_mem_get(struct netmap_mem_d *nmd)
+__netmap_mem_get(struct netmap_mem_d *nmd, const char *func, int line)
 {
 	NM_MTX_LOCK(nm_mem_list_lock);
 	nmd->refcount++;
@@ -265,7 +341,7 @@ netmap_mem_get(struct netmap_mem_d *nmd)
 }
 
 void
-netmap_mem_put(struct netmap_mem_d *nmd)
+__netmap_mem_put(struct netmap_mem_d *nmd, const char *func, int line)
 {
 	int last;
 	NM_MTX_LOCK(nm_mem_list_lock);
@@ -281,76 +357,144 @@ netmap_mem_put(struct netmap_mem_d *nmd)
 int
 netmap_mem_finalize(struct netmap_mem_d *nmd, struct netmap_adapter *na)
 {
-	if (nm_mem_assign_group(nmd, na->pdev) < 0) {
+	int lasterr = 0;
+	if (nm_mem_check_group(nmd, na->pdev) < 0) {
 		return ENOMEM;
+	}
+
+	NMA_LOCK(nmd);
+
+	if (netmap_mem_config(nmd))
+		goto out;
+
+	nmd->active++;
+
+	nmd->lasterr = nmd->ops->nmd_finalize(nmd, na);
+
+	if (!nmd->lasterr && !(nmd->flags & NETMAP_MEM_NOMAP)) {
+		nmd->lasterr = netmap_mem_map(&nmd->pools[NETMAP_BUF_POOL], na);
+	}
+
+out:
+	lasterr = nmd->lasterr;
+	NMA_UNLOCK(nmd);
+
+	if (lasterr)
+		netmap_mem_deref(nmd, na);
+
+	return lasterr;
+}
+
+static int
+nm_isset(uint32_t *bitmap, u_int i)
+{
+	return bitmap[ (i>>5) ] & ( 1U << (i & 31U) );
+}
+
+
+static int
+netmap_init_obj_allocator_bitmap(struct netmap_obj_pool *p)
+{
+	u_int n, j;
+
+	if (p->bitmap == NULL) {
+		/* Allocate the bitmap */
+		n = (p->objtotal + 31) / 32;
+		p->bitmap = nm_os_malloc(sizeof(p->bitmap[0]) * n);
+		if (p->bitmap == NULL) {
+			nm_prerr("Unable to create bitmap (%d entries) for allocator '%s'", (int)n,
+			    p->name);
+			return ENOMEM;
+		}
+		p->bitmap_slots = n;
 	} else {
-		NMA_LOCK(nmd);
-		nmd->lasterr = nmd->ops->nmd_finalize(nmd);
-		NMA_UNLOCK(nmd);
+		memset(p->bitmap, 0, p->bitmap_slots * sizeof(p->bitmap[0]));
+	}
+
+	p->objfree = 0;
+	/*
+	 * Set all the bits in the bitmap that have
+	 * corresponding buffers to 1 to indicate they are
+	 * free.
+	 */
+	for (j = 0; j < p->objtotal; j++) {
+		if (p->invalid_bitmap && nm_isset(p->invalid_bitmap, j)) {
+			if (netmap_debug & NM_DEBUG_MEM)
+				nm_prinf("skipping %s %d", p->name, j);
+			continue;
+		}
+		p->bitmap[ (j>>5) ] |=  ( 1U << (j & 31U) );
+		p->objfree++;
 	}
 
-	if (!nmd->lasterr && na->pdev)
-		netmap_mem_map(&nmd->pools[NETMAP_BUF_POOL], na);
+	if (netmap_verbose)
+		nm_prinf("%s free %u", p->name, p->objfree);
+	if (p->objfree == 0) {
+		if (netmap_verbose)
+			nm_prerr("%s: no objects available", p->name);
+		return ENOMEM;
+	}
 
-	return nmd->lasterr;
+	return 0;
 }
 
-void
+static int
+netmap_mem_init_bitmaps(struct netmap_mem_d *nmd)
+{
+	int i, error = 0;
+
+	for (i = 0; i < NETMAP_POOLS_NR; i++) {
+		struct netmap_obj_pool *p = &nmd->pools[i];
+
+		error = netmap_init_obj_allocator_bitmap(p);
+		if (error)
+			return error;
+	}
+
+	/*
+	 * buffers 0 and 1 are reserved
+	 */
+	if (nmd->pools[NETMAP_BUF_POOL].objfree < 2) {
+		nm_prerr("%s: not enough buffers", nmd->pools[NETMAP_BUF_POOL].name);
+		return ENOMEM;
+	}
+
+	nmd->pools[NETMAP_BUF_POOL].objfree -= 2;
+	if (nmd->pools[NETMAP_BUF_POOL].bitmap) {
+		/* XXX This check is a workaround that prevents a
+		 * NULL pointer crash which currently happens only
+		 * with ptnetmap guests.
+		 * Removed shared-info --> is the bug still there? */
+		nmd->pools[NETMAP_BUF_POOL].bitmap[0] = ~3U;
+	}
+	return 0;
+}
+
+int
 netmap_mem_deref(struct netmap_mem_d *nmd, struct netmap_adapter *na)
 {
+	int last_user = 0;
 	NMA_LOCK(nmd);
-	netmap_mem_unmap(&nmd->pools[NETMAP_BUF_POOL], na);
+	if (na->active_fds <= 0 && !(nmd->flags & NETMAP_MEM_NOMAP))
+		netmap_mem_unmap(&nmd->pools[NETMAP_BUF_POOL], na);
 	if (nmd->active == 1) {
-		u_int i;
-
+		last_user = 1;
 		/*
 		 * Reset the allocator when it falls out of use so that any
 		 * pool resources leaked by unclean application exits are
 		 * reclaimed.
 		 */
-		for (i = 0; i < NETMAP_POOLS_NR; i++) {
-			struct netmap_obj_pool *p;
-			u_int j;
-
-			p = &nmd->pools[i];
-			p->objfree = p->objtotal;
-			/*
-			 * Reproduce the net effect of the M_ZERO malloc()
-			 * and marking of free entries in the bitmap that
-			 * occur in finalize_obj_allocator()
-			 */
-			memset(p->bitmap,
-			    '\0',
-			    sizeof(uint32_t) * ((p->objtotal + 31) / 32));
-
-			/*
-			 * Set all the bits in the bitmap that have
-			 * corresponding buffers to 1 to indicate they are
-			 * free.
-			 */
-			for (j = 0; j < p->objtotal; j++) {
-				if (p->lut[j].vaddr != NULL) {
-					p->bitmap[ (j>>5) ] |=  ( 1 << (j & 31) );
-				}
-			}
-		}
+		netmap_mem_init_bitmaps(nmd);
+	}
+	nmd->ops->nmd_deref(nmd, na);
 
-		/*
-		 * Per netmap_mem_finalize_all(),
-		 * buffers 0 and 1 are reserved
-		 */
-		nmd->pools[NETMAP_BUF_POOL].objfree -= 2;
-		if (nmd->pools[NETMAP_BUF_POOL].bitmap) {
-			/* XXX This check is a workaround that prevents a
-			 * NULL pointer crash which currently happens only
-			 * with ptnetmap guests.
-			 * Removed shared-info --> is the bug still there? */
-			nmd->pools[NETMAP_BUF_POOL].bitmap[0] = ~3;
-		}
+	nmd->active--;
+	if (last_user) {
+		nmd->lasterr = 0;
 	}
-	nmd->ops->nmd_deref(nmd);
 
 	NMA_UNLOCK(nmd);
+	return last_user;
 }
 
 
@@ -359,6 +503,9 @@ static int
 netmap_mem2_get_lut(struct netmap_mem_d *nmd, struct netmap_lut *lut)
 {
 	lut->lut = nmd->pools[NETMAP_BUF_POOL].lut;
+#ifdef __FreeBSD__
+	lut->plut = lut->lut;
+#endif
 	lut->objtotal = nmd->pools[NETMAP_BUF_POOL].objtotal;
 	lut->objsize = nmd->pools[NETMAP_BUF_POOL]._objsize;
 
@@ -438,9 +585,9 @@ struct netmap_mem_d nm_mem = {	/* Our memory allocator. */
 	.name = "1"
 };
 
+static struct netmap_mem_d nm_mem_blueprint;
 
 /* blueprint for the private memory allocators */
-extern struct netmap_mem_ops netmap_mem_private_ops; /* forward */
 /* XXX clang is not happy about using name as a print format */
 static const struct netmap_mem_d nm_blueprint = {
 	.pools = {
@@ -504,7 +651,7 @@ DECLARE_SYSCTLS(NETMAP_BUF_POOL, buf);
 
 /* call with nm_mem_list_lock held */
 static int
-nm_mem_assign_id_locked(struct netmap_mem_d *nmd)
+nm_mem_assign_id_locked(struct netmap_mem_d *nmd, int grp_id)
 {
 	nm_memid_t id;
 	struct netmap_mem_d *scan = netmap_last_mem_d;
@@ -518,12 +665,14 @@ nm_mem_assign_id_locked(struct netmap_mem_d *nmd)
 		scan = scan->next;
 		if (id != scan->nm_id) {
 			nmd->nm_id = id;
+			nmd->nm_grp = grp_id;
 			nmd->prev = scan->prev;
 			nmd->next = scan;
 			scan->prev->next = nmd;
 			scan->prev = nmd;
 			netmap_last_mem_d = nmd;
 			nmd->refcount = 1;
+			NM_DBG_REFC(nmd, __FUNCTION__, __LINE__);
 			error = 0;
 			break;
 		}
@@ -534,12 +683,12 @@ nm_mem_assign_id_locked(struct netmap_mem_d *nmd)
 
 /* call with nm_mem_list_lock *not* held */
 static int
-nm_mem_assign_id(struct netmap_mem_d *nmd)
+nm_mem_assign_id(struct netmap_mem_d *nmd, int grp_id)
 {
-        int ret;
+	int ret;
 
 	NM_MTX_LOCK(nm_mem_list_lock);
-        ret = nm_mem_assign_id_locked(nmd);
+	ret = nm_mem_assign_id_locked(nmd, grp_id);
 	NM_MTX_UNLOCK(nm_mem_list_lock);
 
 	return ret;
@@ -568,6 +717,7 @@ netmap_mem_find(nm_memid_t id)
 	do {
 		if (!(nmd->flags & NETMAP_MEM_HIDDEN) && nmd->nm_id == id) {
 			nmd->refcount++;
+			NM_DBG_REFC(nmd, __FUNCTION__, __LINE__);
 			NM_MTX_UNLOCK(nm_mem_list_lock);
 			return nmd;
 		}
@@ -578,25 +728,74 @@ netmap_mem_find(nm_memid_t id)
 }
 
 static int
-nm_mem_assign_group(struct netmap_mem_d *nmd, struct device *dev)
+nm_mem_check_group(struct netmap_mem_d *nmd, bus_dma_tag_t dev)
 {
 	int err = 0, id;
+
+	/* Skip not hw adapters.
+	 * Vale port can use particular allocator through vale-ctl -m option
+	 */
+	if (!dev)
+		return 0;
 	id = nm_iommu_group_id(dev);
-	if (netmap_verbose)
-		D("iommu_group %d", id);
+	if (netmap_debug & NM_DEBUG_MEM)
+		nm_prinf("iommu_group %d", id);
 
 	NMA_LOCK(nmd);
 
-	if (nmd->nm_grp < 0)
-		nmd->nm_grp = id;
-
-	if (nmd->nm_grp != id)
+	if (nmd->nm_grp != id) {
+		if (netmap_verbose)
+			nm_prerr("iommu group mismatch: %d vs %d",
+					nmd->nm_grp, id);
 		nmd->lasterr = err = ENOMEM;
+	}
 
 	NMA_UNLOCK(nmd);
 	return err;
 }
 
+static struct lut_entry *
+nm_alloc_lut(u_int nobj)
+{
+	size_t n = sizeof(struct lut_entry) * nobj;
+	struct lut_entry *lut;
+#ifdef linux
+	lut = vmalloc(n);
+#else
+	lut = nm_os_malloc(n);
+#endif
+	return lut;
+}
+
+static void
+nm_free_lut(struct lut_entry *lut, u_int objtotal)
+{
+	bzero(lut, sizeof(struct lut_entry) * objtotal);
+#ifdef linux
+	vfree(lut);
+#else
+	nm_os_free(lut);
+#endif
+}
+
+#if defined(linux) || defined(_WIN32)
+static struct plut_entry *
+nm_alloc_plut(u_int nobj)
+{
+	size_t n = sizeof(struct plut_entry) * nobj;
+	struct plut_entry *lut;
+	lut = vmalloc(n);
+	return lut;
+}
+
+static void
+nm_free_plut(struct plut_entry * lut)
+{
+	vfree(lut);
+}
+#endif /* linux or _WIN32 */
+
+
 /*
  * First, find the allocator that contains the requested offset,
  * then locate the cluster through a lookup table.
@@ -609,7 +808,6 @@ netmap_mem2_ofstophys(struct netmap_mem_d* nmd, vm_ooffset_t offset)
 	vm_paddr_t pa;
 	struct netmap_obj_pool *p;
 
-	NMA_LOCK(nmd);
 	p = nmd->pools;
 
 	for (i = 0; i < NETMAP_POOLS_NR; offset -= p[i].memtotal, i++) {
@@ -623,20 +821,18 @@ netmap_mem2_ofstophys(struct netmap_mem_d* nmd, vm_ooffset_t offset)
 		pa = vtophys(p[i].lut[offset / p[i]._objsize].vaddr);
 		pa.QuadPart += offset % p[i]._objsize;
 #endif
-		NMA_UNLOCK(nmd);
 		return pa;
 	}
 	/* this is only in case of errors */
-	D("invalid ofs 0x%x out of 0x%x 0x%x 0x%x", (u_int)o,
+	nm_prerr("invalid ofs 0x%x out of 0x%zx 0x%zx 0x%zx", (u_int)o,
 		p[NETMAP_IF_POOL].memtotal,
 		p[NETMAP_IF_POOL].memtotal
 			+ p[NETMAP_RING_POOL].memtotal,
 		p[NETMAP_IF_POOL].memtotal
 			+ p[NETMAP_RING_POOL].memtotal
 			+ p[NETMAP_BUF_POOL].memtotal);
-	NMA_UNLOCK(nmd);
 #ifndef _WIN32
-	return 0;	// XXX bad address
+	return 0; /* bad address */
 #else
 	vm_paddr_t res;
 	res.QuadPart = 0;
@@ -658,7 +854,7 @@ netmap_mem2_ofstophys(struct netmap_mem_d* nmd, vm_ooffset_t offset)
  *
  *		2a - cycle all the objects in every pool, get the list
  *				of the physical address descriptors
- *		2b - calculate the offset in the array of pages desciptor in the
+ *		2b - calculate the offset in the array of pages descriptor in the
  *				main MDL
  *		2c - copy the descriptors of the object in the main MDL
  *
@@ -671,18 +867,19 @@ netmap_mem2_ofstophys(struct netmap_mem_d* nmd, vm_ooffset_t offset)
 PMDL
 win32_build_user_vm_map(struct netmap_mem_d* nmd)
 {
-	int i, j;
-	u_int memsize, memflags, ofs = 0;
+	u_int memflags, ofs = 0;
 	PMDL mainMdl, tempMdl;
+	uint64_t memsize;
+	int i, j;
 
 	if (netmap_mem_get_info(nmd, &memsize, &memflags, NULL)) {
-		D("memory not finalised yet");
+		nm_prerr("memory not finalised yet");
 		return NULL;
 	}
 
 	mainMdl = IoAllocateMdl(NULL, memsize, FALSE, FALSE, NULL);
 	if (mainMdl == NULL) {
-		D("failed to allocate mdl");
+		nm_prerr("failed to allocate mdl");
 		return NULL;
 	}
 
@@ -698,7 +895,7 @@ win32_build_user_vm_map(struct netmap_mem_d* nmd)
 		tempMdl = IoAllocateMdl(p->lut[0].vaddr, clsz, FALSE, FALSE, NULL);
 		if (tempMdl == NULL) {
 			NMA_UNLOCK(nmd);
-			D("fail to allocate tempMdl");
+			nm_prerr("fail to allocate tempMdl");
 			IoFreeMdl(mainMdl);
 			return NULL;
 		}
@@ -742,11 +939,10 @@ netmap_mem2_get_pool_info(struct netmap_mem_d* nmd, u_int pool, u_int *clustsize
 }
 
 static int
-netmap_mem2_get_info(struct netmap_mem_d* nmd, u_int* size, u_int *memflags,
-	nm_memid_t *id)
+netmap_mem2_get_info(struct netmap_mem_d* nmd, uint64_t* size,
+			u_int *memflags, nm_memid_t *id)
 {
 	int error = 0;
-	NMA_LOCK(nmd);
 	error = netmap_mem_config(nmd);
 	if (error)
 		goto out;
@@ -758,7 +954,7 @@ netmap_mem2_get_info(struct netmap_mem_d* nmd, u_int* size, u_int *memflags,
 			*size = 0;
 			for (i = 0; i < NETMAP_POOLS_NR; i++) {
 				struct netmap_obj_pool *p = nmd->pools + i;
-				*size += (p->_numclusters * p->_clustsize);
+				*size += ((size_t)p->_numclusters * (size_t)p->_clustsize);
 			}
 		}
 	}
@@ -767,7 +963,6 @@ netmap_mem2_get_info(struct netmap_mem_d* nmd, u_int* size, u_int *memflags,
 	if (id)
 		*id = nmd->nm_id;
 out:
-	NMA_UNLOCK(nmd);
 	return error;
 }
 
@@ -791,11 +986,11 @@ netmap_obj_offset(struct netmap_obj_pool *p, const void *vaddr)
 			continue;
 
 		ofs = ofs + relofs;
-		ND("%s: return offset %d (cluster %d) for pointer %p",
+		nm_prdis("%s: return offset %d (cluster %d) for pointer %p",
 		    p->name, ofs, i, vaddr);
 		return ofs;
 	}
-	D("address %p is not contained inside any cluster (%s)",
+	nm_prerr("address %p is not contained inside any cluster (%s)",
 	    vaddr, p->name);
 	return 0; /* An error occurred */
 }
@@ -811,11 +1006,7 @@ netmap_obj_offset(struct netmap_obj_pool *p, const void *vaddr)
 static ssize_t
 netmap_mem2_if_offset(struct netmap_mem_d *nmd, const void *addr)
 {
-	ssize_t v;
-	NMA_LOCK(nmd);
-	v = netmap_if_offset(nmd, addr);
-	NMA_UNLOCK(nmd);
-	return v;
+	return netmap_if_offset(nmd, addr);
 }
 
 /*
@@ -830,13 +1021,12 @@ netmap_obj_malloc(struct netmap_obj_pool *p, u_int len, uint32_t *start, uint32_
 	void *vaddr = NULL;
 
 	if (len > p->_objsize) {
-		D("%s request size %d too large", p->name, len);
-		// XXX cannot reduce the size
+		nm_prerr("%s request size %d too large", p->name, len);
 		return NULL;
 	}
 
 	if (p->objfree == 0) {
-		D("no more %s objects", p->name);
+		nm_prerr("no more %s objects", p->name);
 		return NULL;
 	}
 	if (start)
@@ -860,7 +1050,7 @@ netmap_obj_malloc(struct netmap_obj_pool *p, u_int len, uint32_t *start, uint32_
 		if (index)
 			*index = i * 32 + j;
 	}
-	ND("%s allocator: allocated object @ [%d][%d]: vaddr %p",p->name, i, j, vaddr);
+	nm_prdis("%s allocator: allocated object @ [%d][%d]: vaddr %p",p->name, i, j, vaddr);
 
 	if (start)
 		*start = i;
@@ -878,13 +1068,13 @@ netmap_obj_free(struct netmap_obj_pool *p, uint32_t j)
 	uint32_t *ptr, mask;
 
 	if (j >= p->objtotal) {
-		D("invalid index %u, max %u", j, p->objtotal);
+		nm_prerr("invalid index %u, max %u", j, p->objtotal);
 		return 1;
 	}
 	ptr = &p->bitmap[j / 32];
 	mask = (1 << (j % 32));
 	if (*ptr & mask) {
-		D("ouch, double free on buffer %d", j);
+		nm_prerr("ouch, double free on buffer %d", j);
 		return 1;
 	} else {
 		*ptr |= mask;
@@ -907,7 +1097,7 @@ netmap_obj_free_va(struct netmap_obj_pool *p, void *vaddr)
 		ssize_t relofs = (ssize_t) vaddr - (ssize_t) base;
 
 		/* Given address, is out of the scope of the current cluster.*/
-		if (vaddr < base || relofs >= p->_clustsize)
+		if (base == NULL || vaddr < base || relofs >= p->_clustsize)
 			continue;
 
 		j = j + relofs / p->_objsize;
@@ -915,12 +1105,15 @@ netmap_obj_free_va(struct netmap_obj_pool *p, void *vaddr)
 		netmap_obj_free(p, j);
 		return;
 	}
-	D("address %p is not contained inside any cluster (%s)",
+	nm_prerr("address %p is not contained inside any cluster (%s)",
 	    vaddr, p->name);
 }
 
-#define netmap_mem_bufsize(n)	\
-	((n)->pools[NETMAP_BUF_POOL]._objsize)
+unsigned
+netmap_mem_bufsize(struct netmap_mem_d *nmd)
+{
+	return nmd->pools[NETMAP_BUF_POOL]._objsize;
+}
 
 #define netmap_if_malloc(n, len)	netmap_obj_malloc(&(n)->pools[NETMAP_IF_POOL], len, NULL, NULL)
 #define netmap_if_free(n, v)		netmap_obj_free_va(&(n)->pools[NETMAP_IF_POOL], (v))
@@ -930,7 +1123,7 @@ netmap_obj_free_va(struct netmap_obj_pool *p, void *vaddr)
 	netmap_obj_malloc(&(n)->pools[NETMAP_BUF_POOL], netmap_mem_bufsize(n), _pos, _index)
 
 
-#if 0 // XXX unused
+#if 0 /* currently unused */
 /* Return the index associated to the given packet buffer */
 #define netmap_buf_index(n, v)						\
     (netmap_obj_offset(&(n)->pools[NETMAP_BUF_POOL], (v)) / NETMAP_BDG_BUF_SIZE(n))
@@ -953,11 +1146,11 @@ netmap_extra_alloc(struct netmap_adapter *na, uint32_t *head, uint32_t n)
 		uint32_t cur = *head;	/* save current head */
 		uint32_t *p = netmap_buf_malloc(nmd, &pos, head);
 		if (p == NULL) {
-			D("no more buffers after %d of %d", i, n);
+			nm_prerr("no more buffers after %d of %d", i, n);
 			*head = cur; /* restore */
 			break;
 		}
-		ND(5, "allocate buffer %d -> %d", *head, cur);
+		nm_prdis(5, "allocate buffer %d -> %d", *head, cur);
 		*p = cur; /* link to previous head */
 	}
 
@@ -969,12 +1162,12 @@ netmap_extra_alloc(struct netmap_adapter *na, uint32_t *head, uint32_t n)
 static void
 netmap_extra_free(struct netmap_adapter *na, uint32_t head)
 {
-        struct lut_entry *lut = na->na_lut.lut;
+	struct lut_entry *lut = na->na_lut.lut;
 	struct netmap_mem_d *nmd = na->nm_mem;
 	struct netmap_obj_pool *p = &nmd->pools[NETMAP_BUF_POOL];
 	uint32_t i, cur, *buf;
 
-	ND("freeing the extra list");
+	nm_prdis("freeing the extra list");
 	for (i = 0; head >=2 && head < p->objtotal; i++) {
 		cur = head;
 		buf = lut[head].vaddr;
@@ -984,9 +1177,9 @@ netmap_extra_free(struct netmap_adapter *na, uint32_t head)
 			break;
 	}
 	if (head != 0)
-		D("breaking with head %d", head);
-	if (netmap_verbose)
-		D("freed %d buffers", i);
+		nm_prerr("breaking with head %d", head);
+	if (netmap_debug & NM_DEBUG_MEM)
+		nm_prinf("freed %d buffers", i);
 }
 
 
@@ -1002,15 +1195,16 @@ netmap_new_bufs(struct netmap_mem_d *nmd, struct netmap_slot *slot, u_int n)
 	for (i = 0; i < n; i++) {
 		void *vaddr = netmap_buf_malloc(nmd, &pos, &index);
 		if (vaddr == NULL) {
-			D("no more buffers after %d of %d", i, n);
+			nm_prerr("no more buffers after %d of %d", i, n);
 			goto cleanup;
 		}
 		slot[i].buf_idx = index;
 		slot[i].len = p->_objsize;
 		slot[i].flags = 0;
+		slot[i].ptr = 0;
 	}
 
-	ND("allocated %d buffers, %d available, first at %d", n, p->objfree, pos);
+	nm_prdis("%s: allocated %d buffers, %d available, first at %d", p->name, n, p->objfree, pos);
 	return (0);
 
 cleanup:
@@ -1042,7 +1236,7 @@ netmap_free_buf(struct netmap_mem_d *nmd, uint32_t i)
 	struct netmap_obj_pool *p = &nmd->pools[NETMAP_BUF_POOL];
 
 	if (i < 2 || i >= p->objtotal) {
-		D("Cannot free buf#%d: should be in [2, %d[", i, p->objtotal);
+		nm_prerr("Cannot free buf#%d: should be in [2, %d[", i, p->objtotal);
 		return;
 	}
 	netmap_obj_free(p, i);
@@ -1055,9 +1249,11 @@ netmap_free_bufs(struct netmap_mem_d *nmd, struct netmap_slot *slot, u_int n)
 	u_int i;
 
 	for (i = 0; i < n; i++) {
-		if (slot[i].buf_idx > 2)
+		if (slot[i].buf_idx > 1)
 			netmap_free_buf(nmd, slot[i].buf_idx);
 	}
+	nm_prdis("%s: released some buffers, available: %u",
+			p->name, p->objfree);
 }
 
 static void
@@ -1069,6 +1265,15 @@ netmap_reset_obj_allocator(struct netmap_obj_pool *p)
 	if (p->bitmap)
 		nm_os_free(p->bitmap);
 	p->bitmap = NULL;
+	if (p->invalid_bitmap)
+		nm_os_free(p->invalid_bitmap);
+	p->invalid_bitmap = NULL;
+	if (!p->alloc_done) {
+		/* allocation was done by somebody else.
+		 * Let them clean up after themselves.
+		 */
+		return;
+	}
 	if (p->lut) {
 		u_int i;
 
@@ -1079,21 +1284,16 @@ netmap_reset_obj_allocator(struct netmap_obj_pool *p)
 		 * in the lut.
 		 */
 		for (i = 0; i < p->objtotal; i += p->_clustentries) {
-			if (p->lut[i].vaddr)
-				contigfree(p->lut[i].vaddr, p->_clustsize, M_NETMAP);
+			contigfree(p->lut[i].vaddr, p->_clustsize, M_NETMAP);
 		}
-		bzero(p->lut, sizeof(struct lut_entry) * p->objtotal);
-#ifdef linux
-		vfree(p->lut);
-#else
-		nm_os_free(p->lut);
-#endif
+		nm_free_lut(p->lut, p->objtotal);
 	}
 	p->lut = NULL;
 	p->objtotal = 0;
 	p->memtotal = 0;
 	p->numclusters = 0;
 	p->objfree = 0;
+	p->alloc_done = 0;
 }
 
 /*
@@ -1133,25 +1333,25 @@ netmap_config_obj_allocator(struct netmap_obj_pool *p, u_int objtotal, u_int obj
 	p->r_objsize = objsize;
 
 #define MAX_CLUSTSIZE	(1<<22)		// 4 MB
-#define LINE_ROUND	NM_CACHE_ALIGN	// 64
+#define LINE_ROUND	NM_BUF_ALIGN	// 64
 	if (objsize >= MAX_CLUSTSIZE) {
 		/* we could do it but there is no point */
-		D("unsupported allocation for %d bytes", objsize);
+		nm_prerr("unsupported allocation for %d bytes", objsize);
 		return EINVAL;
 	}
 	/* make sure objsize is a multiple of LINE_ROUND */
 	i = (objsize & (LINE_ROUND - 1));
 	if (i) {
-		D("XXX aligning object by %d bytes", LINE_ROUND - i);
+		nm_prinf("aligning object by %d bytes", LINE_ROUND - i);
 		objsize += LINE_ROUND - i;
 	}
 	if (objsize < p->objminsize || objsize > p->objmaxsize) {
-		D("requested objsize %d out of range [%d, %d]",
+		nm_prerr("requested objsize %d out of range [%d, %d]",
 			objsize, p->objminsize, p->objmaxsize);
 		return EINVAL;
 	}
 	if (objtotal < p->nummin || objtotal > p->nummax) {
-		D("requested objtotal %d out of range [%d, %d]",
+		nm_prerr("requested objtotal %d out of range [%d, %d]",
 			objtotal, p->nummin, p->nummax);
 		return EINVAL;
 	}
@@ -1173,13 +1373,13 @@ netmap_config_obj_allocator(struct netmap_obj_pool *p, u_int objtotal, u_int obj
 	}
 	/* exact solution not found */
 	if (clustentries == 0) {
-		D("unsupported allocation for %d bytes", objsize);
+		nm_prerr("unsupported allocation for %d bytes", objsize);
 		return EINVAL;
 	}
 	/* compute clustsize */
 	clustsize = clustentries * objsize;
-	if (netmap_verbose)
-		D("objsize %d clustsize %d objects %d",
+	if (netmap_debug & NM_DEBUG_MEM)
+		nm_prinf("objsize %d clustsize %d objects %d",
 			objsize, clustsize, clustentries);
 
 	/*
@@ -1197,19 +1397,6 @@ netmap_config_obj_allocator(struct netmap_obj_pool *p, u_int objtotal, u_int obj
 	return 0;
 }
 
-static struct lut_entry *
-nm_alloc_lut(u_int nobj)
-{
-	size_t n = sizeof(struct lut_entry) * nobj;
-	struct lut_entry *lut;
-#ifdef linux
-	lut = vmalloc(n);
-#else
-	lut = nm_os_malloc(n);
-#endif
-	return lut;
-}
-
 /* call with NMA_LOCK held */
 static int
 netmap_finalize_obj_allocator(struct netmap_obj_pool *p)
@@ -1217,28 +1404,30 @@ netmap_finalize_obj_allocator(struct netmap_obj_pool *p)
 	int i; /* must be signed */
 	size_t n;
 
+	if (p->lut) {
+		/* if the lut is already there we assume that also all the
+		 * clusters have already been allocated, possibly by somebody
+		 * else (e.g., extmem). In the latter case, the alloc_done flag
+		 * will remain at zero, so that we will not attempt to
+		 * deallocate the clusters by ourselves in
+		 * netmap_reset_obj_allocator.
+		 */
+		return 0;
+	}
+
 	/* optimistically assume we have enough memory */
 	p->numclusters = p->_numclusters;
 	p->objtotal = p->_objtotal;
+	p->alloc_done = 1;
 
 	p->lut = nm_alloc_lut(p->objtotal);
 	if (p->lut == NULL) {
-		D("Unable to create lookup table for '%s'", p->name);
-		goto clean;
-	}
-
-	/* Allocate the bitmap */
-	n = (p->objtotal + 31) / 32;
-	p->bitmap = nm_os_malloc(sizeof(uint32_t) * n);
-	if (p->bitmap == NULL) {
-		D("Unable to create bitmap (%d entries) for allocator '%s'", (int)n,
-		    p->name);
+		nm_prerr("Unable to create lookup table for '%s'", p->name);
 		goto clean;
 	}
-	p->bitmap_slots = n;
 
 	/*
-	 * Allocate clusters, init pointers and bitmap
+	 * Allocate clusters, init pointers
 	 */
 
 	n = p->_clustsize;
@@ -1260,13 +1449,12 @@ netmap_finalize_obj_allocator(struct netmap_obj_pool *p)
 			 * If we get here, there is a severe memory shortage,
 			 * so halve the allocated memory to reclaim some.
 			 */
-			D("Unable to create cluster at %d for '%s' allocator",
+			nm_prerr("Unable to create cluster at %d for '%s' allocator",
 			    i, p->name);
 			if (i < 2) /* nothing to halve */
 				goto out;
 			lim = i / 2;
 			for (i--; i >= lim; i--) {
-				p->bitmap[ (i>>5) ] &=  ~( 1 << (i & 31) );
 				if (i % p->_clustentries == 0 && p->lut[i].vaddr)
 					contigfree(p->lut[i].vaddr,
 						n, M_NETMAP);
@@ -1279,8 +1467,7 @@ netmap_finalize_obj_allocator(struct netmap_obj_pool *p)
 			break;
 		}
 		/*
-		 * Set bitmap and lut state for all buffers in the current
-		 * cluster.
+		 * Set lut state for all buffers in the current cluster.
 		 *
 		 * [i, lim) is the set of buffer indexes that cover the
 		 * current cluster.
@@ -1290,17 +1477,15 @@ netmap_finalize_obj_allocator(struct netmap_obj_pool *p)
 		 * of p->_objsize.
 		 */
 		for (; i < lim; i++, clust += p->_objsize) {
-			p->bitmap[ (i>>5) ] |=  ( 1 << (i & 31) );
 			p->lut[i].vaddr = clust;
+#if !defined(linux) && !defined(_WIN32)
 			p->lut[i].paddr = vtophys(clust);
+#endif
 		}
 	}
-	p->objfree = p->objtotal;
-	p->memtotal = p->numclusters * p->_clustsize;
-	if (p->objfree == 0)
-		goto clean;
+	p->memtotal = (size_t)p->numclusters * (size_t)p->_clustsize;
 	if (netmap_verbose)
-		D("Pre-allocated %d clusters (%d/%dKB) for '%s'",
+		nm_prinf("Pre-allocated %d clusters (%d/%zuKB) for '%s'",
 		    p->numclusters, p->_clustsize >> 10,
 		    p->memtotal >> 10, p->name);
 
@@ -1332,8 +1517,8 @@ netmap_mem_reset_all(struct netmap_mem_d *nmd)
 {
 	int i;
 
-	if (netmap_verbose)
-		D("resetting %p", nmd);
+	if (netmap_debug & NM_DEBUG_MEM)
+		nm_prinf("resetting %p", nmd);
 	for (i = 0; i < NETMAP_POOLS_NR; i++) {
 		netmap_reset_obj_allocator(&nmd->pools[i]);
 	}
@@ -1343,24 +1528,36 @@ netmap_mem_reset_all(struct netmap_mem_d *nmd)
 static int
 netmap_mem_unmap(struct netmap_obj_pool *p, struct netmap_adapter *na)
 {
-	int i, lim = p->_objtotal;
-
+	int i, lim = p->objtotal;
+	struct netmap_lut *lut;
 	if (na == NULL || na->pdev == NULL)
 		return 0;
 
+	lut = &na->na_lut;
+
+
+
 #if defined(__FreeBSD__)
+	/* On FreeBSD mapping and unmapping is performed by the txsync
+	 * and rxsync routine, packet by packet. */
 	(void)i;
 	(void)lim;
-	D("unsupported on FreeBSD");
-
+	(void)lut;
 #elif defined(_WIN32)
 	(void)i;
 	(void)lim;
-	D("unsupported on Windows");	//XXX_ale, really?
+	(void)lut;
+	nm_prerr("unsupported on Windows");
 #else /* linux */
-	for (i = 2; i < lim; i++) {
-		netmap_unload_map(na, (bus_dma_tag_t) na->pdev, &p->lut[i].paddr);
+	nm_prdis("unmapping and freeing plut for %s", na->name);
+	if (lut->plut == NULL || na->pdev == NULL)
+		return 0;
+	for (i = 0; i < lim; i += p->_clustentries) {
+		if (lut->plut[i].paddr)
+			netmap_unload_map(na, (bus_dma_tag_t) na->pdev, &lut->plut[i].paddr, p->_clustsize);
 	}
+	nm_free_plut(lut->plut);
+	lut->plut = NULL;
 #endif /* linux */
 
 	return 0;
@@ -1369,23 +1566,66 @@ netmap_mem_unmap(struct netmap_obj_pool *p, struct netmap_adapter *na)
 static int
 netmap_mem_map(struct netmap_obj_pool *p, struct netmap_adapter *na)
 {
+	int error = 0;
+	int i, lim = p->objtotal;
+	struct netmap_lut *lut = &na->na_lut;
+
+	if (na->pdev == NULL)
+		return 0;
+
 #if defined(__FreeBSD__)
-	D("unsupported on FreeBSD");
+	/* On FreeBSD mapping and unmapping is performed by the txsync
+	 * and rxsync routine, packet by packet. */
+	(void)i;
+	(void)lim;
+	(void)lut;
 #elif defined(_WIN32)
-	D("unsupported on Windows");	//XXX_ale, really?
+	(void)i;
+	(void)lim;
+	(void)lut;
+	nm_prerr("unsupported on Windows");
 #else /* linux */
-	int i, lim = p->_objtotal;
 
-	if (na->pdev == NULL)
+	if (lut->plut != NULL) {
+		nm_prdis("plut already allocated for %s", na->name);
 		return 0;
+	}
 
-	for (i = 2; i < lim; i++) {
-		netmap_load_map(na, (bus_dma_tag_t) na->pdev, &p->lut[i].paddr,
-				p->lut[i].vaddr);
+	nm_prdis("allocating physical lut for %s", na->name);
+	lut->plut = nm_alloc_plut(lim);
+	if (lut->plut == NULL) {
+		nm_prerr("Failed to allocate physical lut for %s", na->name);
+		return ENOMEM;
 	}
+
+	for (i = 0; i < lim; i += p->_clustentries) {
+		lut->plut[i].paddr = 0;
+	}
+
+	for (i = 0; i < lim; i += p->_clustentries) {
+		int j;
+
+		if (p->lut[i].vaddr == NULL)
+			continue;
+
+		error = netmap_load_map(na, (bus_dma_tag_t) na->pdev, &lut->plut[i].paddr,
+				p->lut[i].vaddr, p->_clustsize);
+		if (error) {
+			nm_prerr("Failed to map cluster #%d from the %s pool", i, p->name);
+			break;
+		}
+
+		for (j = 1; j < p->_clustentries; j++) {
+			lut->plut[i + j].paddr = lut->plut[i + j - 1].paddr + p->_objsize;
+		}
+	}
+
+	if (error)
+		netmap_mem_unmap(p, na);
+
 #endif /* linux */
 
-	return 0;
+	return error;
 }
 
 static int
@@ -1402,19 +1642,21 @@ netmap_mem_finalize_all(struct netmap_mem_d *nmd)
 			goto error;
 		nmd->nm_totalsize += nmd->pools[i].memtotal;
 	}
-	/* buffers 0 and 1 are reserved */
-	nmd->pools[NETMAP_BUF_POOL].objfree -= 2;
-	nmd->pools[NETMAP_BUF_POOL].bitmap[0] = ~3;
+	nmd->nm_totalsize = (nmd->nm_totalsize + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+	nmd->lasterr = netmap_mem_init_bitmaps(nmd);
+	if (nmd->lasterr)
+		goto error;
+
 	nmd->flags |= NETMAP_MEM_FINALIZED;
 
 	if (netmap_verbose)
-		D("interfaces %d KB, rings %d KB, buffers %d MB",
+		nm_prinf("interfaces %zd KB, rings %zd KB, buffers %zd MB",
 		    nmd->pools[NETMAP_IF_POOL].memtotal >> 10,
 		    nmd->pools[NETMAP_RING_POOL].memtotal >> 10,
 		    nmd->pools[NETMAP_BUF_POOL].memtotal >> 20);
 
 	if (netmap_verbose)
-		D("Free buffers: %d", nmd->pools[NETMAP_BUF_POOL].objfree);
+		nm_prinf("Free buffers: %d", nmd->pools[NETMAP_BUF_POOL].objfree);
 
 
 	return 0;
@@ -1426,45 +1668,80 @@ netmap_mem_finalize_all(struct netmap_mem_d *nmd)
 /*
  * allocator for private memory
  */
-static struct netmap_mem_d *
-_netmap_mem_private_new(struct netmap_obj_params *p, int *perr)
+static void *
+_netmap_mem_private_new(size_t size, struct netmap_obj_params *p, int grp_id,
+		struct netmap_mem_ops *ops, uint64_t memtotal, int *perr)
 {
 	struct netmap_mem_d *d = NULL;
 	int i, err = 0;
+	int checksz = 0;
+
+	/* if memtotal is !=0 we check that the request fits the available
+	 * memory. Moreover, any surprlus memory is assigned to buffers.
+	 */
+	checksz = (memtotal > 0);
 
-	d = nm_os_malloc(sizeof(struct netmap_mem_d));
+	d = nm_os_malloc(size);
 	if (d == NULL) {
 		err = ENOMEM;
 		goto error;
 	}
 
 	*d = nm_blueprint;
+	d->ops = ops;
 
-	err = nm_mem_assign_id(d);
+	err = nm_mem_assign_id(d, grp_id);
 	if (err)
-		goto error;
+		goto error_free;
 	snprintf(d->name, NM_MEM_NAMESZ, "%d", d->nm_id);
 
 	for (i = 0; i < NETMAP_POOLS_NR; i++) {
 		snprintf(d->pools[i].name, NETMAP_POOL_MAX_NAMSZ,
 				nm_blueprint.pools[i].name,
 				d->name);
+		if (checksz) {
+			uint64_t poolsz = (uint64_t)p[i].num * p[i].size;
+			if (memtotal < poolsz) {
+				nm_prerr("%s: request too large", d->pools[i].name);
+				err = ENOMEM;
+				goto error_rel_id;
+			}
+			memtotal -= poolsz;
+		}
 		d->params[i].num = p[i].num;
 		d->params[i].size = p[i].size;
 	}
+	if (checksz && memtotal > 0) {
+		uint64_t sz = d->params[NETMAP_BUF_POOL].size;
+		uint64_t n = (memtotal + sz - 1) / sz;
+
+		if (n) {
+			if (netmap_verbose) {
+				nm_prinf("%s: adding %llu more buffers",
+				    d->pools[NETMAP_BUF_POOL].name,
+				    (unsigned long long)n);
+			}
+			d->params[NETMAP_BUF_POOL].num += n;
+		}
+	}
 
 	NMA_LOCK_INIT(d);
 
 	err = netmap_mem_config(d);
 	if (err)
-		goto error;
+		goto error_destroy_lock;
 
 	d->flags &= ~NETMAP_MEM_FINALIZED;
 
 	return d;
 
+error_destroy_lock:
+	NMA_LOCK_DESTROY(d);
+error_rel_id:
+	nm_mem_release_id(d);
+error_free:
+	nm_os_free(d);
 error:
-	netmap_mem_delete(d);
 	if (perr)
 		*perr = err;
 	return NULL;
@@ -1476,7 +1753,7 @@ netmap_mem_private_new(u_int txr, u_int txd, u_int rxr, u_int rxd,
 {
 	struct netmap_mem_d *d = NULL;
 	struct netmap_obj_params p[NETMAP_POOLS_NR];
-	int i, err = 0;
+	int i;
 	u_int v, maxd;
 	/* account for the fake host rings */
 	txr++;
@@ -1499,23 +1776,23 @@ netmap_mem_private_new(u_int txr, u_int txd, u_int rxr, u_int rxd,
 	if (p[NETMAP_RING_POOL].size < v)
 		p[NETMAP_RING_POOL].size = v;
 	/* each pipe endpoint needs two tx rings (1 normal + 1 host, fake)
-         * and two rx rings (again, 1 normal and 1 fake host)
-         */
+	 * and two rx rings (again, 1 normal and 1 fake host)
+	 */
 	v = txr + rxr + 8 * npipes;
 	if (p[NETMAP_RING_POOL].num < v)
 		p[NETMAP_RING_POOL].num = v;
 	/* for each pipe we only need the buffers for the 4 "real" rings.
-         * On the other end, the pipe ring dimension may be different from
-         * the parent port ring dimension. As a compromise, we allocate twice the
-         * space actually needed if the pipe rings were the same size as the parent rings
-         */
+	 * On the other end, the pipe ring dimension may be different from
+	 * the parent port ring dimension. As a compromise, we allocate twice the
+	 * space actually needed if the pipe rings were the same size as the parent rings
+	 */
 	v = (4 * npipes + rxr) * rxd + (4 * npipes + txr) * txd + 2 + extra_bufs;
 		/* the +2 is for the tx and rx fake buffers (indices 0 and 1) */
 	if (p[NETMAP_BUF_POOL].num < v)
 		p[NETMAP_BUF_POOL].num = v;
 
 	if (netmap_verbose)
-		D("req if %d*%d ring %d*%d buf %d*%d",
+		nm_prinf("req if %d*%d ring %d*%d buf %d*%d",
 			p[NETMAP_IF_POOL].num,
 			p[NETMAP_IF_POOL].size,
 			p[NETMAP_RING_POOL].num,
@@ -1523,33 +1800,76 @@ netmap_mem_private_new(u_int txr, u_int txd, u_int rxr, u_int rxd,
 			p[NETMAP_BUF_POOL].num,
 			p[NETMAP_BUF_POOL].size);
 
-	d = _netmap_mem_private_new(p, perr);
-	if (d == NULL)
-		goto error;
+	d = _netmap_mem_private_new(sizeof(*d), p, -1, &netmap_mem_global_ops, 0, perr);
 
 	return d;
+}
+
+/* Reference iommu allocator - find existing or create new,
+ * for not hw addapeters fallback to global allocator.
+ */
+struct netmap_mem_d *
+netmap_mem_get_iommu(struct netmap_adapter *na)
+{
+	int i, err, grp_id;
+	struct netmap_mem_d *nmd;
+
+	if (na == NULL || na->pdev == NULL)
+		return netmap_mem_get(&nm_mem);
+
+	grp_id = nm_iommu_group_id(na->pdev);
+
+	NM_MTX_LOCK(nm_mem_list_lock);
+	nmd = netmap_last_mem_d;
+	do {
+		if (!(nmd->flags & NETMAP_MEM_HIDDEN) && nmd->nm_grp == grp_id) {
+			nmd->refcount++;
+			NM_DBG_REFC(nmd, __FUNCTION__, __LINE__);
+			NM_MTX_UNLOCK(nm_mem_list_lock);
+			return nmd;
+		}
+		nmd = nmd->next;
+	} while (nmd != netmap_last_mem_d);
+
+	nmd = nm_os_malloc(sizeof(*nmd));
+	if (nmd == NULL)
+		goto error;
+
+	*nmd = nm_mem_blueprint;
+
+	err = nm_mem_assign_id_locked(nmd, grp_id);
+	if (err)
+		goto error_free;
+
+	snprintf(nmd->name, sizeof(nmd->name), "%d", nmd->nm_id);
+
+	for (i = 0; i < NETMAP_POOLS_NR; i++) {
+		snprintf(nmd->pools[i].name, NETMAP_POOL_MAX_NAMSZ, "%s-%s",
+			nm_mem_blueprint.pools[i].name, nmd->name);
+	}
+
+	NMA_LOCK_INIT(nmd);
+
+	NM_MTX_UNLOCK(nm_mem_list_lock);
+	return nmd;
+
+error_free:
+	nm_os_free(nmd);
 error:
-	netmap_mem_delete(d);
-	if (perr)
-		*perr = err;
+	NM_MTX_UNLOCK(nm_mem_list_lock);
 	return NULL;
 }
 
-
 /* call with lock held */
 static int
 netmap_mem2_config(struct netmap_mem_d *nmd)
 {
 	int i;
 
-	if (nmd->active)
-		/* already in use, we cannot change the configuration */
-		goto out;
-
 	if (!netmap_mem_params_changed(nmd->params))
 		goto out;
 
-	ND("reconfiguring");
+	nm_prdis("reconfiguring");
 
 	if (nmd->flags & NETMAP_MEM_FINALIZED) {
 		/* reset previous allocation */
@@ -1572,21 +1892,10 @@ netmap_mem2_config(struct netmap_mem_d *nmd)
 }
 
 static int
-netmap_mem2_finalize(struct netmap_mem_d *nmd)
+netmap_mem2_finalize(struct netmap_mem_d *nmd, struct netmap_adapter *na)
 {
-	int err;
-
-	/* update configuration if changed */
-	if (netmap_mem2_config(nmd))
-		goto out1;
-
-	nmd->active++;
-
-	if (nmd->flags & NETMAP_MEM_FINALIZED) {
-		/* may happen if config is not changed */
-		ND("nothing to do");
+	if (nmd->flags & NETMAP_MEM_FINALIZED)
 		goto out;
-	}
 
 	if (netmap_mem_finalize_all(nmd))
 		goto out;
@@ -1594,13 +1903,7 @@ netmap_mem2_finalize(struct netmap_mem_d *nmd)
 	nmd->lasterr = 0;
 
 out:
-	if (nmd->lasterr)
-		nmd->active--;
-out1:
-	err = nmd->lasterr;
-
-	return err;
-
+	return nmd->lasterr;
 }
 
 static void
@@ -1617,12 +1920,22 @@ netmap_mem2_delete(struct netmap_mem_d *nmd)
 		nm_os_free(nmd);
 }
 
+#ifdef WITH_EXTMEM
+/* doubly linekd list of all existing external allocators */
+static struct netmap_mem_ext *netmap_mem_ext_list = NULL;
+NM_MTX_T nm_mem_ext_list_lock;
+#endif /* WITH_EXTMEM */
+
 int
 netmap_mem_init(void)
 {
+	nm_mem_blueprint = nm_mem;
 	NM_MTX_INIT(nm_mem_list_lock);
 	NMA_LOCK_INIT(&nm_mem);
 	netmap_mem_get(&nm_mem);
+#ifdef WITH_EXTMEM
+	NM_MTX_INIT(nm_mem_ext_list_lock);
+#endif /* WITH_EXTMEM */
 	return (0);
 }
 
@@ -1632,30 +1945,23 @@ netmap_mem_fini(void)
 	netmap_mem_put(&nm_mem);
 }
 
-static void
-netmap_free_rings(struct netmap_adapter *na)
+static int
+netmap_mem_ring_needed(struct netmap_kring *kring)
 {
-	enum txrx t;
-
-	for_rx_tx(t) {
-		u_int i;
-		for (i = 0; i < nma_get_nrings(na, t) + 1; i++) {
-			struct netmap_kring *kring = &NMR(na, t)[i];
-			struct netmap_ring *ring = kring->ring;
+	return kring->ring == NULL &&
+		(kring->users > 0 ||
+		 (kring->nr_kflags & NKR_NEEDRING));
+}
 
-			if (ring == NULL || kring->users > 0 || (kring->nr_kflags & NKR_NEEDRING)) {
-				ND("skipping ring %s (ring %p, users %d)",
-						kring->name, ring, kring->users);
-				continue;
-			}
-			if (i != nma_get_nrings(na, t) || na->na_flags & NAF_HOST_RINGS)
-				netmap_free_bufs(na->nm_mem, ring->slot, kring->nkr_num_slots);
-			netmap_ring_free(na->nm_mem, ring);
-			kring->ring = NULL;
-		}
-	}
+static int
+netmap_mem_ring_todelete(struct netmap_kring *kring)
+{
+	return kring->ring != NULL &&
+		kring->users == 0 &&
+		!(kring->nr_kflags & NKR_NEEDRING);
 }
 
+
 /* call with NMA_LOCK held *
  *
  * Allocate netmap rings and buffers for this card
@@ -1664,59 +1970,65 @@ netmap_free_rings(struct netmap_adapter *na)
  * in netmap_krings_create().
  */
 static int
-netmap_mem2_rings_create(struct netmap_adapter *na)
+netmap_mem2_rings_create(struct netmap_mem_d *nmd, struct netmap_adapter *na)
 {
 	enum txrx t;
 
-	NMA_LOCK(na->nm_mem);
-
 	for_rx_tx(t) {
 		u_int i;
 
-		for (i = 0; i <= nma_get_nrings(na, t); i++) {
-			struct netmap_kring *kring = &NMR(na, t)[i];
+		for (i = 0; i < netmap_all_rings(na, t); i++) {
+			struct netmap_kring *kring = NMR(na, t)[i];
 			struct netmap_ring *ring = kring->ring;
 			u_int len, ndesc;
 
-			if (ring || (!kring->users && !(kring->nr_kflags & NKR_NEEDRING))) {
-				/* uneeded, or already created by somebody else */
-				ND("skipping ring %s", kring->name);
+			if (!netmap_mem_ring_needed(kring)) {
+				/* unneeded, or already created by somebody else */
+				if (netmap_debug & NM_DEBUG_MEM)
+					nm_prinf("NOT creating ring %s (ring %p, users %d neekring %d)",
+						kring->name, ring, kring->users, kring->nr_kflags & NKR_NEEDRING);
 				continue;
 			}
+			if (netmap_debug & NM_DEBUG_MEM)
+				nm_prinf("creating %s", kring->name);
 			ndesc = kring->nkr_num_slots;
 			len = sizeof(struct netmap_ring) +
 				  ndesc * sizeof(struct netmap_slot);
-			ring = netmap_ring_malloc(na->nm_mem, len);
+			ring = netmap_ring_malloc(nmd, len);
 			if (ring == NULL) {
-				D("Cannot allocate %s_ring", nm_txrx2str(t));
+				nm_prerr("Cannot allocate %s_ring", nm_txrx2str(t));
 				goto cleanup;
 			}
-			ND("txring at %p", ring);
+			nm_prdis("txring at %p", ring);
 			kring->ring = ring;
 			*(uint32_t *)(uintptr_t)&ring->num_slots = ndesc;
 			*(int64_t *)(uintptr_t)&ring->buf_ofs =
-			    (na->nm_mem->pools[NETMAP_IF_POOL].memtotal +
-				na->nm_mem->pools[NETMAP_RING_POOL].memtotal) -
-				netmap_ring_offset(na->nm_mem, ring);
+			    (nmd->pools[NETMAP_IF_POOL].memtotal +
+				nmd->pools[NETMAP_RING_POOL].memtotal) -
+				netmap_ring_offset(nmd, ring);
 
 			/* copy values from kring */
 			ring->head = kring->rhead;
 			ring->cur = kring->rcur;
 			ring->tail = kring->rtail;
-			*(uint16_t *)(uintptr_t)&ring->nr_buf_size =
-				netmap_mem_bufsize(na->nm_mem);
-			ND("%s h %d c %d t %d", kring->name,
+			*(uint32_t *)(uintptr_t)&ring->nr_buf_size =
+				netmap_mem_bufsize(nmd);
+			nm_prdis("%s h %d c %d t %d", kring->name,
 				ring->head, ring->cur, ring->tail);
-			ND("initializing slots for %s_ring", nm_txrx2str(txrx));
-			if (i != nma_get_nrings(na, t) || (na->na_flags & NAF_HOST_RINGS)) {
+			nm_prdis("initializing slots for %s_ring", nm_txrx2str(t));
+			if (!(kring->nr_kflags & NKR_FAKERING)) {
 				/* this is a real ring */
-				if (netmap_new_bufs(na->nm_mem, ring->slot, ndesc)) {
-					D("Cannot allocate buffers for %s_ring", nm_txrx2str(t));
+				if (netmap_debug & NM_DEBUG_MEM)
+					nm_prinf("allocating buffers for %s", kring->name);
+				if (netmap_new_bufs(nmd, ring->slot, ndesc)) {
+					nm_prerr("Cannot allocate buffers for %s_ring", nm_txrx2str(t));
 					goto cleanup;
 				}
 			} else {
 				/* this is a fake ring, set all indices to 0 */
-				netmap_mem_set_ring(na->nm_mem, ring->slot, ndesc, 0);
+				if (netmap_debug & NM_DEBUG_MEM)
+					nm_prinf("NOT allocating buffers for %s", kring->name);
+				netmap_mem_set_ring(nmd, ring->slot, ndesc, 0);
 			}
 		        /* ring info */
 		        *(uint16_t *)(uintptr_t)&ring->ringid = kring->ring_id;
@@ -1724,30 +2036,49 @@ netmap_mem2_rings_create(struct netmap_adapter *na)
 		}
 	}
 
-	NMA_UNLOCK(na->nm_mem);
-
 	return 0;
 
 cleanup:
-	netmap_free_rings(na);
-
-	NMA_UNLOCK(na->nm_mem);
+	/* we cannot actually cleanup here, since we don't own kring->users
+	 * and kring->nr_klags & NKR_NEEDRING. The caller must decrement
+	 * the first or zero-out the second, then call netmap_free_rings()
+	 * to do the cleanup
+	 */
 
 	return ENOMEM;
 }
 
 static void
-netmap_mem2_rings_delete(struct netmap_adapter *na)
+netmap_mem2_rings_delete(struct netmap_mem_d *nmd, struct netmap_adapter *na)
 {
-	/* last instance, release bufs and rings */
-	NMA_LOCK(na->nm_mem);
+	enum txrx t;
 
-	netmap_free_rings(na);
+	for_rx_tx(t) {
+		u_int i;
+		for (i = 0; i < netmap_all_rings(na, t); i++) {
+			struct netmap_kring *kring = NMR(na, t)[i];
+			struct netmap_ring *ring = kring->ring;
 
-	NMA_UNLOCK(na->nm_mem);
+			if (!netmap_mem_ring_todelete(kring)) {
+				if (netmap_debug & NM_DEBUG_MEM)
+					nm_prinf("NOT deleting ring %s (ring %p, users %d neekring %d)",
+						kring->name, ring, kring->users, kring->nr_kflags & NKR_NEEDRING);
+				continue;
+			}
+			if (netmap_debug & NM_DEBUG_MEM)
+				nm_prinf("deleting ring %s", kring->name);
+			if (!(kring->nr_kflags & NKR_FAKERING)) {
+				nm_prdis("freeing bufs for %s", kring->name);
+				netmap_free_bufs(nmd, ring->slot, kring->nkr_num_slots);
+			} else {
+				nm_prdis("NOT freeing bufs for %s", kring->name);
+			}
+			netmap_ring_free(nmd, ring);
+			kring->ring = NULL;
+		}
+	}
 }
 
-
 /* call with NMA_LOCK held */
 /*
  * Allocate the per-fd structure netmap_if.
@@ -1757,7 +2088,8 @@ netmap_mem2_rings_delete(struct netmap_adapter *na)
  * the interface is in netmap mode.
  */
 static struct netmap_if *
-netmap_mem2_if_new(struct netmap_adapter *na, struct netmap_priv_d *priv)
+netmap_mem2_if_new(struct netmap_mem_d *nmd,
+		struct netmap_adapter *na, struct netmap_priv_d *priv)
 {
 	struct netmap_if *nifp;
 	ssize_t base; /* handy for relative offsets between rings and nifp */
@@ -1767,7 +2099,7 @@ netmap_mem2_if_new(struct netmap_adapter *na, struct netmap_priv_d *priv)
 	ntot = 0;
 	for_rx_tx(t) {
 		/* account for the (eventually fake) host rings */
-		n[t] = nma_get_nrings(na, t) + 1;
+		n[t] = netmap_all_rings(na, t);
 		ntot += n[t];
 	}
 	/*
@@ -1775,35 +2107,36 @@ netmap_mem2_if_new(struct netmap_adapter *na, struct netmap_priv_d *priv)
 	 * to the tx and rx rings in the shared memory region.
 	 */
 
-	NMA_LOCK(na->nm_mem);
-
 	len = sizeof(struct netmap_if) + (ntot * sizeof(ssize_t));
-	nifp = netmap_if_malloc(na->nm_mem, len);
+	nifp = netmap_if_malloc(nmd, len);
 	if (nifp == NULL) {
-		NMA_UNLOCK(na->nm_mem);
 		return NULL;
 	}
 
 	/* initialize base fields -- override const */
 	*(u_int *)(uintptr_t)&nifp->ni_tx_rings = na->num_tx_rings;
 	*(u_int *)(uintptr_t)&nifp->ni_rx_rings = na->num_rx_rings;
-	strncpy(nifp->ni_name, na->name, (size_t)IFNAMSIZ);
+	*(u_int *)(uintptr_t)&nifp->ni_host_tx_rings =
+		(na->num_host_tx_rings ? na->num_host_tx_rings : 1);
+	*(u_int *)(uintptr_t)&nifp->ni_host_rx_rings =
+		(na->num_host_rx_rings ? na->num_host_rx_rings : 1);
+	strlcpy(nifp->ni_name, na->name, sizeof(nifp->ni_name));
 
 	/*
 	 * fill the slots for the rx and tx rings. They contain the offset
 	 * between the ring and nifp, so the information is usable in
 	 * userspace to reach the ring from the nifp.
 	 */
-	base = netmap_if_offset(na->nm_mem, nifp);
+	base = netmap_if_offset(nmd, nifp);
 	for (i = 0; i < n[NR_TX]; i++) {
 		/* XXX instead of ofs == 0 maybe use the offset of an error
 		 * ring, like we do for buffers? */
 		ssize_t ofs = 0;
 
-		if (na->tx_rings[i].ring != NULL && i >= priv->np_qfirst[NR_TX]
+		if (na->tx_rings[i]->ring != NULL && i >= priv->np_qfirst[NR_TX]
 				&& i < priv->np_qlast[NR_TX]) {
-			ofs = netmap_ring_offset(na->nm_mem,
-						 na->tx_rings[i].ring) - base;
+			ofs = netmap_ring_offset(nmd,
+						 na->tx_rings[i]->ring) - base;
 		}
 		*(ssize_t *)(uintptr_t)&nifp->ring_ofs[i] = ofs;
 	}
@@ -1812,42 +2145,35 @@ netmap_mem2_if_new(struct netmap_adapter *na, struct netmap_priv_d *priv)
 		 * ring, like we do for buffers? */
 		ssize_t ofs = 0;
 
-		if (na->rx_rings[i].ring != NULL && i >= priv->np_qfirst[NR_RX]
+		if (na->rx_rings[i]->ring != NULL && i >= priv->np_qfirst[NR_RX]
 				&& i < priv->np_qlast[NR_RX]) {
-			ofs = netmap_ring_offset(na->nm_mem,
-						 na->rx_rings[i].ring) - base;
+			ofs = netmap_ring_offset(nmd,
+						 na->rx_rings[i]->ring) - base;
 		}
 		*(ssize_t *)(uintptr_t)&nifp->ring_ofs[i+n[NR_TX]] = ofs;
 	}
 
-	NMA_UNLOCK(na->nm_mem);
-
 	return (nifp);
 }
 
 static void
-netmap_mem2_if_delete(struct netmap_adapter *na, struct netmap_if *nifp)
+netmap_mem2_if_delete(struct netmap_mem_d *nmd,
+		struct netmap_adapter *na, struct netmap_if *nifp)
 {
 	if (nifp == NULL)
 		/* nothing to do */
 		return;
-	NMA_LOCK(na->nm_mem);
 	if (nifp->ni_bufs_head)
 		netmap_extra_free(na, nifp->ni_bufs_head);
-	netmap_if_free(na->nm_mem, nifp);
-
-	NMA_UNLOCK(na->nm_mem);
+	netmap_if_free(nmd, nifp);
 }
 
 static void
-netmap_mem2_deref(struct netmap_mem_d *nmd)
+netmap_mem2_deref(struct netmap_mem_d *nmd, struct netmap_adapter *na)
 {
 
-	nmd->active--;
-	if (!nmd->active)
-		nmd->nm_grp = -1;
-	if (netmap_verbose)
-		D("active = %d", nmd->active);
+	if (netmap_debug & NM_DEBUG_MEM)
+		nm_prinf("active = %d", nmd->active);
 
 }
 
@@ -1867,52 +2193,297 @@ struct netmap_mem_ops netmap_mem_global_ops = {
 };
 
 int
-netmap_mem_pools_info_get(struct nmreq *nmr, struct netmap_adapter *na)
+netmap_mem_pools_info_get(struct nmreq_pools_info *req,
+				struct netmap_mem_d *nmd)
 {
-	uintptr_t *pp = (uintptr_t *)&nmr->nr_arg1;
-	struct netmap_pools_info *upi = (struct netmap_pools_info *)(*pp);
-	struct netmap_mem_d *nmd = na->nm_mem;
-	struct netmap_pools_info pi;
-	unsigned int memsize;
-	uint16_t memid;
 	int ret;
 
-	if (!nmd) {
-		return -1;
-	}
-
-	ret = netmap_mem_get_info(nmd, &memsize, NULL, &memid);
+	ret = netmap_mem_get_info(nmd, &req->nr_memsize, NULL,
+					&req->nr_mem_id);
 	if (ret) {
 		return ret;
 	}
 
-	pi.memsize = memsize;
-	pi.memid = memid;
-	pi.if_pool_offset = 0;
-	pi.if_pool_objtotal = nmd->pools[NETMAP_IF_POOL].objtotal;
-	pi.if_pool_objsize = nmd->pools[NETMAP_IF_POOL]._objsize;
+	NMA_LOCK(nmd);
+	req->nr_if_pool_offset = 0;
+	req->nr_if_pool_objtotal = nmd->pools[NETMAP_IF_POOL].objtotal;
+	req->nr_if_pool_objsize = nmd->pools[NETMAP_IF_POOL]._objsize;
 
-	pi.ring_pool_offset = nmd->pools[NETMAP_IF_POOL].memtotal;
-	pi.ring_pool_objtotal = nmd->pools[NETMAP_RING_POOL].objtotal;
-	pi.ring_pool_objsize = nmd->pools[NETMAP_RING_POOL]._objsize;
+	req->nr_ring_pool_offset = nmd->pools[NETMAP_IF_POOL].memtotal;
+	req->nr_ring_pool_objtotal = nmd->pools[NETMAP_RING_POOL].objtotal;
+	req->nr_ring_pool_objsize = nmd->pools[NETMAP_RING_POOL]._objsize;
 
-	pi.buf_pool_offset = nmd->pools[NETMAP_IF_POOL].memtotal +
+	req->nr_buf_pool_offset = nmd->pools[NETMAP_IF_POOL].memtotal +
 			     nmd->pools[NETMAP_RING_POOL].memtotal;
-	pi.buf_pool_objtotal = nmd->pools[NETMAP_BUF_POOL].objtotal;
-	pi.buf_pool_objsize = nmd->pools[NETMAP_BUF_POOL]._objsize;
+	req->nr_buf_pool_objtotal = nmd->pools[NETMAP_BUF_POOL].objtotal;
+	req->nr_buf_pool_objsize = nmd->pools[NETMAP_BUF_POOL]._objsize;
+	NMA_UNLOCK(nmd);
 
-	ret = copyout(&pi, upi, sizeof(pi));
-	if (ret) {
-		return ret;
+	return 0;
+}
+
+#ifdef WITH_EXTMEM
+struct netmap_mem_ext {
+	struct netmap_mem_d up;
+
+	struct nm_os_extmem *os;
+	struct netmap_mem_ext *next, *prev;
+};
+
+/* call with nm_mem_list_lock held */
+static void
+netmap_mem_ext_register(struct netmap_mem_ext *e)
+{
+	NM_MTX_LOCK(nm_mem_ext_list_lock);
+	if (netmap_mem_ext_list)
+		netmap_mem_ext_list->prev = e;
+	e->next = netmap_mem_ext_list;
+	netmap_mem_ext_list = e;
+	e->prev = NULL;
+	NM_MTX_UNLOCK(nm_mem_ext_list_lock);
+}
+
+/* call with nm_mem_list_lock held */
+static void
+netmap_mem_ext_unregister(struct netmap_mem_ext *e)
+{
+	if (e->prev)
+		e->prev->next = e->next;
+	else
+		netmap_mem_ext_list = e->next;
+	if (e->next)
+		e->next->prev = e->prev;
+	e->prev = e->next = NULL;
+}
+
+static struct netmap_mem_ext *
+netmap_mem_ext_search(struct nm_os_extmem *os)
+{
+	struct netmap_mem_ext *e;
+
+	NM_MTX_LOCK(nm_mem_ext_list_lock);
+	for (e = netmap_mem_ext_list; e; e = e->next) {
+		if (nm_os_extmem_isequal(e->os, os)) {
+			netmap_mem_get(&e->up);
+			break;
+		}
+	}
+	NM_MTX_UNLOCK(nm_mem_ext_list_lock);
+	return e;
+}
+
+
+static void
+netmap_mem_ext_delete(struct netmap_mem_d *d)
+{
+	int i;
+	struct netmap_mem_ext *e =
+		(struct netmap_mem_ext *)d;
+
+	netmap_mem_ext_unregister(e);
+
+	for (i = 0; i < NETMAP_POOLS_NR; i++) {
+		struct netmap_obj_pool *p = &d->pools[i];
+
+		if (p->lut) {
+			nm_free_lut(p->lut, p->objtotal);
+			p->lut = NULL;
+		}
 	}
+	if (e->os)
+		nm_os_extmem_delete(e->os);
+	netmap_mem2_delete(d);
+}
 
+static int
+netmap_mem_ext_config(struct netmap_mem_d *nmd)
+{
 	return 0;
 }
 
-#ifdef WITH_PTNETMAP_GUEST
+struct netmap_mem_ops netmap_mem_ext_ops = {
+	.nmd_get_lut = netmap_mem2_get_lut,
+	.nmd_get_info = netmap_mem2_get_info,
+	.nmd_ofstophys = netmap_mem2_ofstophys,
+	.nmd_config = netmap_mem_ext_config,
+	.nmd_finalize = netmap_mem2_finalize,
+	.nmd_deref = netmap_mem2_deref,
+	.nmd_delete = netmap_mem_ext_delete,
+	.nmd_if_offset = netmap_mem2_if_offset,
+	.nmd_if_new = netmap_mem2_if_new,
+	.nmd_if_delete = netmap_mem2_if_delete,
+	.nmd_rings_create = netmap_mem2_rings_create,
+	.nmd_rings_delete = netmap_mem2_rings_delete
+};
+
+struct netmap_mem_d *
+netmap_mem_ext_create(uint64_t usrptr, struct nmreq_pools_info *pi, int *perror)
+{
+	int error = 0;
+	int i, j;
+	struct netmap_mem_ext *nme;
+	char *clust;
+	size_t off;
+	struct nm_os_extmem *os = NULL;
+	int nr_pages;
+
+	// XXX sanity checks
+	if (pi->nr_if_pool_objtotal == 0)
+		pi->nr_if_pool_objtotal = netmap_min_priv_params[NETMAP_IF_POOL].num;
+	if (pi->nr_if_pool_objsize == 0)
+		pi->nr_if_pool_objsize = netmap_min_priv_params[NETMAP_IF_POOL].size;
+	if (pi->nr_ring_pool_objtotal == 0)
+		pi->nr_ring_pool_objtotal = netmap_min_priv_params[NETMAP_RING_POOL].num;
+	if (pi->nr_ring_pool_objsize == 0)
+		pi->nr_ring_pool_objsize = netmap_min_priv_params[NETMAP_RING_POOL].size;
+	if (pi->nr_buf_pool_objtotal == 0)
+		pi->nr_buf_pool_objtotal = netmap_min_priv_params[NETMAP_BUF_POOL].num;
+	if (pi->nr_buf_pool_objsize == 0)
+		pi->nr_buf_pool_objsize = netmap_min_priv_params[NETMAP_BUF_POOL].size;
+	if (netmap_verbose & NM_DEBUG_MEM)
+		nm_prinf("if %d %d ring %d %d buf %d %d",
+			pi->nr_if_pool_objtotal, pi->nr_if_pool_objsize,
+			pi->nr_ring_pool_objtotal, pi->nr_ring_pool_objsize,
+			pi->nr_buf_pool_objtotal, pi->nr_buf_pool_objsize);
+
+	os = nm_os_extmem_create(usrptr, pi, &error);
+	if (os == NULL) {
+		nm_prerr("os extmem creation failed");
+		goto out;
+	}
+
+	nme = netmap_mem_ext_search(os);
+	if (nme) {
+		nm_os_extmem_delete(os);
+		return &nme->up;
+	}
+	if (netmap_verbose & NM_DEBUG_MEM)
+		nm_prinf("not found, creating new");
+
+	nme = _netmap_mem_private_new(sizeof(*nme),
+
+			(struct netmap_obj_params[]){
+				{ pi->nr_if_pool_objsize, pi->nr_if_pool_objtotal },
+				{ pi->nr_ring_pool_objsize, pi->nr_ring_pool_objtotal },
+				{ pi->nr_buf_pool_objsize, pi->nr_buf_pool_objtotal }},
+			-1,
+			&netmap_mem_ext_ops,
+			pi->nr_memsize,
+			&error);
+	if (nme == NULL)
+		goto out_unmap;
+
+	nr_pages = nm_os_extmem_nr_pages(os);
+
+	/* from now on pages will be released by nme destructor;
+	 * we let res = 0 to prevent release in out_unmap below
+	 */
+	nme->os = os;
+	os = NULL; /* pass ownership */
+
+	clust = nm_os_extmem_nextpage(nme->os);
+	for (i = 0; i < NETMAP_POOLS_NR; i++) {
+		struct netmap_obj_pool *p = &nme->up.pools[i];
+		struct netmap_obj_params *o = &nme->up.params[i];
+
+		off = 0;
+
+		p->_objsize = o->size;
+		p->_clustsize = o->size;
+		p->_clustentries = 1;
+
+		p->lut = nm_alloc_lut(o->num);
+		if (p->lut == NULL) {
+			error = ENOMEM;
+			goto out_delete;
+		}
+
+		p->bitmap_slots = (o->num + sizeof(uint32_t) - 1) / sizeof(uint32_t);
+		p->invalid_bitmap = nm_os_malloc(sizeof(uint32_t) * p->bitmap_slots);
+		if (p->invalid_bitmap == NULL) {
+			error = ENOMEM;
+			goto out_delete;
+		}
+
+		if (nr_pages == 0) {
+			p->objtotal = 0;
+			p->memtotal = 0;
+			p->objfree = 0;
+			continue;
+		}
+
+		for (j = 0; j < o->num && nr_pages > 0; j++) {
+			size_t noff;
+
+			p->lut[j].vaddr = clust + off;
+#if !defined(linux) && !defined(_WIN32)
+			p->lut[j].paddr = vtophys(p->lut[j].vaddr);
+#endif
+			nm_prdis("%s %d at %p", p->name, j, p->lut[j].vaddr);
+			noff = off + p->_objsize;
+			if (noff < PAGE_SIZE) {
+				off = noff;
+				continue;
+			}
+			nm_prdis("too big, recomputing offset...");
+			while (noff >= PAGE_SIZE) {
+				char *old_clust = clust;
+				noff -= PAGE_SIZE;
+				clust = nm_os_extmem_nextpage(nme->os);
+				nr_pages--;
+				nm_prdis("noff %zu page %p nr_pages %d", noff,
+						page_to_virt(*pages), nr_pages);
+				if (noff > 0 && !nm_isset(p->invalid_bitmap, j) &&
+					(nr_pages == 0 ||
+					 old_clust + PAGE_SIZE != clust))
+				{
+					/* out of space or non contiguous,
+					 * drop this object
+					 * */
+					p->invalid_bitmap[ (j>>5) ] |= 1U << (j & 31U);
+					nm_prdis("non contiguous at off %zu, drop", noff);
+				}
+				if (nr_pages == 0)
+					break;
+			}
+			off = noff;
+		}
+		p->objtotal = j;
+		p->numclusters = p->objtotal;
+		p->memtotal = j * (size_t)p->_objsize;
+		if (p->memtotal & (PAGE_SIZE - 1)) {
+			// make sure that the objects of the next pool start page-aligned
+			p->memtotal = (p->memtotal & ~(PAGE_SIZE - 1)) + PAGE_SIZE;
+			if (nr_pages > 0) {
+				clust = nm_os_extmem_nextpage(nme->os);
+				nr_pages--;
+			}
+		}
+		nm_prdis("%d memtotal %zu", j, p->memtotal);
+	}
+
+	netmap_mem_ext_register(nme);
+
+	return &nme->up;
+
+out_delete:
+	netmap_mem_put(&nme->up);
+out_unmap:
+	if (os)
+		nm_os_extmem_delete(os);
+out:
+	if (perror)
+		*perror = error;
+	return NULL;
+
+}
+#endif /* WITH_EXTMEM */
+
+
+#ifdef WITH_PTNETMAP
 struct mem_pt_if {
 	struct mem_pt_if *next;
-	struct ifnet *ifp;
+	if_t ifp;
 	unsigned int nifp_offset;
 };
 
@@ -1930,7 +2501,7 @@ struct netmap_mem_ptg {
 
 /* Link a passthrough interface to a passthrough netmap allocator. */
 static int
-netmap_mem_pt_guest_ifp_add(struct netmap_mem_d *nmd, struct ifnet *ifp,
+netmap_mem_pt_guest_ifp_add(struct netmap_mem_d *nmd, if_t ifp,
 			    unsigned int nifp_offset)
 {
 	struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)nmd;
@@ -1952,14 +2523,15 @@ netmap_mem_pt_guest_ifp_add(struct netmap_mem_d *nmd, struct ifnet *ifp,
 
 	NMA_UNLOCK(nmd);
 
-	D("added (ifp=%p,nifp_offset=%u)", ptif->ifp, ptif->nifp_offset);
+	nm_prinf("ifp=%s,nifp_offset=%u",
+		if_name(ptif->ifp), ptif->nifp_offset);
 
 	return 0;
 }
 
 /* Called with NMA_LOCK(nmd) held. */
 static struct mem_pt_if *
-netmap_mem_pt_guest_ifp_lookup(struct netmap_mem_d *nmd, struct ifnet *ifp)
+netmap_mem_pt_guest_ifp_lookup(struct netmap_mem_d *nmd, if_t ifp)
 {
 	struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)nmd;
 	struct mem_pt_if *curr;
@@ -1975,7 +2547,7 @@ netmap_mem_pt_guest_ifp_lookup(struct netmap_mem_d *nmd, struct ifnet *ifp)
 
 /* Unlink a passthrough interface from a passthrough netmap allocator. */
 int
-netmap_mem_pt_guest_ifp_del(struct netmap_mem_d *nmd, struct ifnet *ifp)
+netmap_mem_pt_guest_ifp_del(struct netmap_mem_d *nmd, if_t ifp)
 {
 	struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)nmd;
 	struct mem_pt_if *prev = NULL;
@@ -1991,8 +2563,8 @@ netmap_mem_pt_guest_ifp_del(struct netmap_mem_d *nmd, struct ifnet *ifp)
 			} else {
 				ptnmd->pt_ifs = curr->next;
 			}
-			D("removed (ifp=%p,nifp_offset=%u)",
-			  curr->ifp, curr->nifp_offset);
+			nm_prinf("removed (ifp=%s,nifp_offset=%u)",
+			  if_name(curr->ifp), curr->nifp_offset);
 			nm_os_free(curr);
 			ret = 0;
 			break;
@@ -2019,13 +2591,11 @@ netmap_mem_pt_guest_get_lut(struct netmap_mem_d *nmd, struct netmap_lut *lut)
 }
 
 static int
-netmap_mem_pt_guest_get_info(struct netmap_mem_d *nmd, u_int *size,
+netmap_mem_pt_guest_get_info(struct netmap_mem_d *nmd, uint64_t *size,
 			     u_int *memflags, uint16_t *id)
 {
 	int error = 0;
 
-	NMA_LOCK(nmd);
-
 	error = nmd->ops->nmd_config(nmd);
 	if (error)
 		goto out;
@@ -2038,7 +2608,6 @@ netmap_mem_pt_guest_get_info(struct netmap_mem_d *nmd, u_int *size,
 		*id = nmd->nm_id;
 
 out:
-	NMA_UNLOCK(nmd);
 
 	return error;
 }
@@ -2050,7 +2619,7 @@ netmap_mem_pt_guest_ofstophys(struct netmap_mem_d *nmd, vm_ooffset_t off)
 	vm_paddr_t paddr;
 	/* if the offset is valid, just return csb->base_addr + off */
 	paddr = (vm_paddr_t)(ptnmd->nm_paddr + off);
-	ND("off %lx padr %lx", off, (unsigned long)paddr);
+	nm_prdis("off %lx padr %lx", off, (unsigned long)paddr);
 	return paddr;
 }
 
@@ -2064,7 +2633,7 @@ netmap_mem_pt_guest_config(struct netmap_mem_d *nmd)
 }
 
 static int
-netmap_mem_pt_guest_finalize(struct netmap_mem_d *nmd)
+netmap_mem_pt_guest_finalize(struct netmap_mem_d *nmd, struct netmap_adapter *na)
 {
 	struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)nmd;
 	uint64_t mem_size;
@@ -2076,35 +2645,33 @@ netmap_mem_pt_guest_finalize(struct netmap_mem_d *nmd)
 	int i;
 	int error = 0;
 
-	nmd->active++;
-
 	if (nmd->flags & NETMAP_MEM_FINALIZED)
 		goto out;
 
 	if (ptnmd->ptn_dev == NULL) {
-		D("ptnetmap memdev not attached");
+		nm_prerr("ptnetmap memdev not attached");
 		error = ENOMEM;
-		goto err;
+		goto out;
 	}
 	/* Map memory through ptnetmap-memdev BAR. */
 	error = nm_os_pt_memdev_iomap(ptnmd->ptn_dev, &ptnmd->nm_paddr,
 				      &ptnmd->nm_addr, &mem_size);
 	if (error)
-		goto err;
+		goto out;
 
-        /* Initialize the lut using the information contained in the
+	/* Initialize the lut using the information contained in the
 	 * ptnetmap memory device. */
-        bufsize = nm_os_pt_memdev_ioread(ptnmd->ptn_dev,
+	bufsize = nm_os_pt_memdev_ioread(ptnmd->ptn_dev,
 					 PTNET_MDEV_IO_BUF_POOL_OBJSZ);
-        nbuffers = nm_os_pt_memdev_ioread(ptnmd->ptn_dev,
+	nbuffers = nm_os_pt_memdev_ioread(ptnmd->ptn_dev,
 					 PTNET_MDEV_IO_BUF_POOL_OBJNUM);
 
 	/* allocate the lut */
 	if (ptnmd->buf_lut.lut == NULL) {
-		D("allocating lut");
+		nm_prinf("allocating lut");
 		ptnmd->buf_lut.lut = nm_alloc_lut(nbuffers);
 		if (ptnmd->buf_lut.lut == NULL) {
-			D("lut allocation failed");
+			nm_prerr("lut allocation failed");
 			return ENOMEM;
 		}
 	}
@@ -2117,37 +2684,40 @@ netmap_mem_pt_guest_finalize(struct netmap_mem_d *nmd)
 
 	for (i = 0; i < nbuffers; i++) {
 		ptnmd->buf_lut.lut[i].vaddr = vaddr;
-		ptnmd->buf_lut.lut[i].paddr = paddr;
 		vaddr += bufsize;
 		paddr += bufsize;
 	}
 
 	ptnmd->buf_lut.objtotal = nbuffers;
 	ptnmd->buf_lut.objsize = bufsize;
-	nmd->nm_totalsize = (unsigned int)mem_size;
+	nmd->nm_totalsize = mem_size;
+
+	/* Initialize these fields as are needed by
+	 * netmap_mem_bufsize().
+	 * XXX please improve this, why do we need this
+	 * replication? maybe we nmd->pools[] should no be
+	 * there for the guest allocator? */
+	nmd->pools[NETMAP_BUF_POOL]._objsize = bufsize;
+	nmd->pools[NETMAP_BUF_POOL]._objtotal = nbuffers;
 
 	nmd->flags |= NETMAP_MEM_FINALIZED;
 out:
-	return 0;
-err:
-	nmd->active--;
 	return error;
 }
 
 static void
-netmap_mem_pt_guest_deref(struct netmap_mem_d *nmd)
+netmap_mem_pt_guest_deref(struct netmap_mem_d *nmd, struct netmap_adapter *na)
 {
 	struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)nmd;
 
-	nmd->active--;
-	if (nmd->active <= 0 &&
+	if (nmd->active == 1 &&
 		(nmd->flags & NETMAP_MEM_FINALIZED)) {
 	    nmd->flags  &= ~NETMAP_MEM_FINALIZED;
 	    /* unmap ptnetmap-memdev memory */
 	    if (ptnmd->ptn_dev) {
 		nm_os_pt_memdev_iounmap(ptnmd->ptn_dev);
 	    }
-	    ptnmd->nm_addr = 0;
+	    ptnmd->nm_addr = NULL;
 	    ptnmd->nm_paddr = 0;
 	}
 }
@@ -2166,101 +2736,103 @@ netmap_mem_pt_guest_delete(struct netmap_mem_d *nmd)
 	if (nmd == NULL)
 		return;
 	if (netmap_verbose)
-		D("deleting %p", nmd);
+		nm_prinf("deleting %p", nmd);
 	if (nmd->active > 0)
-		D("bug: deleting mem allocator with active=%d!", nmd->active);
+		nm_prerr("bug: deleting mem allocator with active=%d!", nmd->active);
 	if (netmap_verbose)
-		D("done deleting %p", nmd);
+		nm_prinf("done deleting %p", nmd);
 	NMA_LOCK_DESTROY(nmd);
 	nm_os_free(nmd);
 }
 
 static struct netmap_if *
-netmap_mem_pt_guest_if_new(struct netmap_adapter *na, struct netmap_priv_d *priv)
+netmap_mem_pt_guest_if_new(struct netmap_mem_d *nmd,
+		struct netmap_adapter *na, struct netmap_priv_d *priv)
 {
-	struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)na->nm_mem;
+	struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)nmd;
 	struct mem_pt_if *ptif;
 	struct netmap_if *nifp = NULL;
 
-	NMA_LOCK(na->nm_mem);
-
-	ptif = netmap_mem_pt_guest_ifp_lookup(na->nm_mem, na->ifp);
+	ptif = netmap_mem_pt_guest_ifp_lookup(nmd, na->ifp);
 	if (ptif == NULL) {
-		D("Error: interface %p is not in passthrough", na->ifp);
+		nm_prerr("interface %s is not in passthrough", na->name);
 		goto out;
 	}
 
 	nifp = (struct netmap_if *)((char *)(ptnmd->nm_addr) +
 				    ptif->nifp_offset);
-	NMA_UNLOCK(na->nm_mem);
 out:
 	return nifp;
 }
 
 static void
-netmap_mem_pt_guest_if_delete(struct netmap_adapter *na, struct netmap_if *nifp)
+netmap_mem_pt_guest_if_delete(struct netmap_mem_d * nmd,
+		struct netmap_adapter *na, struct netmap_if *nifp)
 {
 	struct mem_pt_if *ptif;
 
-	NMA_LOCK(na->nm_mem);
-	ptif = netmap_mem_pt_guest_ifp_lookup(na->nm_mem, na->ifp);
+	ptif = netmap_mem_pt_guest_ifp_lookup(nmd, na->ifp);
 	if (ptif == NULL) {
-		D("Error: interface %p is not in passthrough", na->ifp);
+		nm_prerr("interface %s is not in passthrough", na->name);
 	}
-	NMA_UNLOCK(na->nm_mem);
 }
 
 static int
-netmap_mem_pt_guest_rings_create(struct netmap_adapter *na)
+netmap_mem_pt_guest_rings_create(struct netmap_mem_d *nmd,
+		struct netmap_adapter *na)
 {
-	struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)na->nm_mem;
+	struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)nmd;
 	struct mem_pt_if *ptif;
 	struct netmap_if *nifp;
 	int i, error = -1;
 
-	NMA_LOCK(na->nm_mem);
-
-	ptif = netmap_mem_pt_guest_ifp_lookup(na->nm_mem, na->ifp);
+	ptif = netmap_mem_pt_guest_ifp_lookup(nmd, na->ifp);
 	if (ptif == NULL) {
-		D("Error: interface %p is not in passthrough", na->ifp);
+		nm_prerr("interface %s is not in passthrough", na->name);
 		goto out;
 	}
 
 
 	/* point each kring to the corresponding backend ring */
 	nifp = (struct netmap_if *)((char *)ptnmd->nm_addr + ptif->nifp_offset);
-	for (i = 0; i <= na->num_tx_rings; i++) {
-		struct netmap_kring *kring = na->tx_rings + i;
+	for (i = 0; i < netmap_all_rings(na, NR_TX); i++) {
+		struct netmap_kring *kring = na->tx_rings[i];
 		if (kring->ring)
 			continue;
 		kring->ring = (struct netmap_ring *)
 			((char *)nifp + nifp->ring_ofs[i]);
 	}
-	for (i = 0; i <= na->num_rx_rings; i++) {
-		struct netmap_kring *kring = na->rx_rings + i;
+	for (i = 0; i < netmap_all_rings(na, NR_RX); i++) {
+		struct netmap_kring *kring = na->rx_rings[i];
 		if (kring->ring)
 			continue;
 		kring->ring = (struct netmap_ring *)
 			((char *)nifp +
-			 nifp->ring_ofs[i + na->num_tx_rings + 1]);
+			 nifp->ring_ofs[netmap_all_rings(na, NR_TX) + i]);
 	}
 
 	error = 0;
 out:
-	NMA_UNLOCK(na->nm_mem);
-
 	return error;
 }
 
 static void
-netmap_mem_pt_guest_rings_delete(struct netmap_adapter *na)
+netmap_mem_pt_guest_rings_delete(struct netmap_mem_d *nmd, struct netmap_adapter *na)
 {
-	/* TODO: remove?? */
 #if 0
-	struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)na->nm_mem;
-	struct mem_pt_if *ptif = netmap_mem_pt_guest_ifp_lookup(na->nm_mem,
-								na->ifp);
+	enum txrx t;
+
+	for_rx_tx(t) {
+		u_int i;
+		for (i = 0; i < nma_get_nrings(na, t) + 1; i++) {
+			struct netmap_kring *kring = &NMR(na, t)[i];
+
+			kring->ring = NULL;
+		}
+	}
 #endif
+	(void)nmd;
+	(void)na;
 }
 
 static struct netmap_mem_ops netmap_mem_pt_guest_ops = {
@@ -2291,6 +2863,7 @@ netmap_mem_pt_guest_find_memid(nm_memid_t mem_id)
 			((struct netmap_mem_ptg *)(scan))->host_mem_id == mem_id) {
 			mem = scan;
 			mem->refcount++;
+			NM_DBG_REFC(mem, __FUNCTION__, __LINE__);
 			break;
 		}
 		scan = scan->next;
@@ -2316,8 +2889,8 @@ netmap_mem_pt_guest_create(nm_memid_t mem_id)
 	ptnmd->host_mem_id = mem_id;
 	ptnmd->pt_ifs = NULL;
 
-        /* Assign new id in the guest (We have the lock) */
-	err = nm_mem_assign_id_locked(&ptnmd->up);
+	/* Assign new id in the guest (We have the lock) */
+	err = nm_mem_assign_id_locked(&ptnmd->up, -1);
 	if (err)
 		goto error;
 
@@ -2383,7 +2956,7 @@ netmap_mem_pt_guest_attach(struct ptnetmap_memdev *ptn_dev, nm_memid_t mem_id)
 
 /* Called when ptnet device is attaching */
 struct netmap_mem_d *
-netmap_mem_pt_guest_new(struct ifnet *ifp,
+netmap_mem_pt_guest_new(if_t ifp,
 			unsigned int nifp_offset,
 			unsigned int memid)
 {
@@ -2402,4 +2975,4 @@ netmap_mem_pt_guest_new(struct ifnet *ifp,
 	return nmd;
 }
 
-#endif /* WITH_PTNETMAP_GUEST */
+#endif /* WITH_PTNETMAP */
diff --git a/sys/dev/netmap/netmap_mem2.h b/sys/dev/netmap/netmap_mem2.h
index 423b21451..b1ccc0138 100644
--- a/sys/dev/netmap/netmap_mem2.h
+++ b/sys/dev/netmap/netmap_mem2.h
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2012-2014 Matteo Landi
  * Copyright (C) 2012-2016 Luigi Rizzo
  * Copyright (C) 2012-2016 Giuseppe Lettieri
@@ -27,8 +29,6 @@
  */
 
 /*
- * $FreeBSD: head/sys/dev/netmap/netmap_mem2.c 234290 2012-04-14 16:44:18Z luigi $
- *
  * (New) memory allocator for netmap
  */
 
@@ -55,7 +55,7 @@
  * of the object, and from there locate the offset from the beginning
  * of the region.
  *
- * The invididual allocators manage a pool of memory for objects of
+ * The individual allocators manage a pool of memory for objects of
  * the same size.
  * The pool is split into smaller clusters, whose size is a
  * multiple of the page size. The cluster size is chosen
@@ -68,7 +68,7 @@
  * Allocation scans the bitmap; this is done only on attach, so we are not
  * too worried about performance
  *
- * For each allocator we can define (thorugh sysctl) the size and
+ * For each allocator we can define (through sysctl) the size and
  * number of each object. Memory is allocated at the first use of a
  * netmap file descriptor, and can be freed when all such descriptors
  * have been released (including unmapping the memory).
@@ -134,32 +134,54 @@ struct netmap_if * netmap_mem_if_new(struct netmap_adapter *, struct netmap_priv
 void 	   netmap_mem_if_delete(struct netmap_adapter *, struct netmap_if *);
 int	   netmap_mem_rings_create(struct netmap_adapter *);
 void	   netmap_mem_rings_delete(struct netmap_adapter *);
-void 	   netmap_mem_deref(struct netmap_mem_d *, struct netmap_adapter *);
-int	netmap_mem2_get_pool_info(struct netmap_mem_d *, u_int, u_int *, u_int *);
-int	   netmap_mem_get_info(struct netmap_mem_d *, u_int *size, u_int *memflags, uint16_t *id);
+int 	   netmap_mem_deref(struct netmap_mem_d *, struct netmap_adapter *);
+int	   netmap_mem2_get_pool_info(struct netmap_mem_d *, u_int, u_int *, u_int *);
+int	   netmap_mem_get_info(struct netmap_mem_d *, uint64_t *size,
+				u_int *memflags, nm_memid_t *id);
 ssize_t    netmap_mem_if_offset(struct netmap_mem_d *, const void *vaddr);
 struct netmap_mem_d* netmap_mem_private_new( u_int txr, u_int txd, u_int rxr, u_int rxd,
 		u_int extra_bufs, u_int npipes, int* error);
-void	   netmap_mem_delete(struct netmap_mem_d *);
 
-struct netmap_mem_d* netmap_mem_get(struct netmap_mem_d *);
-void netmap_mem_put(struct netmap_mem_d *);
+#define netmap_mem_get(d) __netmap_mem_get(d, __FUNCTION__, __LINE__)
+#define netmap_mem_put(d) __netmap_mem_put(d, __FUNCTION__, __LINE__)
+struct netmap_mem_d* __netmap_mem_get(struct netmap_mem_d *, const char *, int);
+struct netmap_mem_d* netmap_mem_get_iommu(struct netmap_adapter *);
+void __netmap_mem_put(struct netmap_mem_d *, const char *, int);
 struct netmap_mem_d* netmap_mem_find(nm_memid_t);
+unsigned netmap_mem_bufsize(struct netmap_mem_d *nmd);
 
-#ifdef WITH_PTNETMAP_GUEST
-struct netmap_mem_d* netmap_mem_pt_guest_new(struct ifnet *,
+#ifdef WITH_EXTMEM
+struct netmap_mem_d* netmap_mem_ext_create(uint64_t, struct nmreq_pools_info *, int *);
+#else /* !WITH_EXTMEM */
+#define netmap_mem_ext_create(nmr, _perr) \
+	({ int *perr = _perr; if (perr) *(perr) = EOPNOTSUPP; NULL; })
+#endif /* WITH_EXTMEM */
+
+#ifdef WITH_PTNETMAP
+struct netmap_mem_d* netmap_mem_pt_guest_new(if_t,
 					     unsigned int nifp_offset,
 					     unsigned int memid);
 struct ptnetmap_memdev;
 struct netmap_mem_d* netmap_mem_pt_guest_attach(struct ptnetmap_memdev *, uint16_t);
-int netmap_mem_pt_guest_ifp_del(struct netmap_mem_d *, struct ifnet *);
-#endif /* WITH_PTNETMAP_GUEST */
+int netmap_mem_pt_guest_ifp_del(struct netmap_mem_d *, if_t);
+#endif /* WITH_PTNETMAP */
 
-int netmap_mem_pools_info_get(struct nmreq *, struct netmap_adapter *);
+int netmap_mem_pools_info_get(struct nmreq_pools_info *,
+				struct netmap_mem_d *);
 
 #define NETMAP_MEM_PRIVATE	0x2	/* allocator uses private address space */
 #define NETMAP_MEM_IO		0x4	/* the underlying memory is mmapped I/O */
 
 uint32_t netmap_extra_alloc(struct netmap_adapter *, uint32_t *, uint32_t n);
 
+#ifdef WITH_EXTMEM
+#include 
+struct nm_os_extmem; /* opaque */
+struct nm_os_extmem *nm_os_extmem_create(unsigned long, struct nmreq_pools_info *, int *perror);
+char *nm_os_extmem_nextpage(struct nm_os_extmem *);
+int nm_os_extmem_nr_pages(struct nm_os_extmem *);
+int nm_os_extmem_isequal(struct nm_os_extmem *, struct nm_os_extmem *);
+void nm_os_extmem_delete(struct nm_os_extmem *);
+#endif /* WITH_EXTMEM */
+
 #endif
diff --git a/sys/dev/netmap/netmap_monitor.c b/sys/dev/netmap/netmap_monitor.c
index 174f35e5c..2e04f4e7a 100644
--- a/sys/dev/netmap/netmap_monitor.c
+++ b/sys/dev/netmap/netmap_monitor.c
@@ -25,8 +25,6 @@
  */
 
 /*
- * $FreeBSD: head/sys/dev/netmap/netmap_zmon.c 270063 2014-08-16 15:00:01Z luigi $
- *
  * Monitors
  *
  * netmap monitors can be used to do monitoring of network traffic
@@ -38,6 +36,8 @@
  * the traffic transiting on both the tx and rx corresponding rings in the
  * monitored adapter. During registration, the user can choose if she wants
  * to intercept tx only, rx only, or both tx and rx traffic.
+ * The slots containing traffic intercepted in the tx direction will have
+ * the NS_TXMON flag set.
  *
  * If the monitor is not able to cope with the stream of frames, excess traffic
  * will be dropped.
@@ -66,9 +66,7 @@
  *    has released them. In most cases, the consumer is a userspace
  *    application which may have modified the frame contents.
  *
- * Several copy monitors may be active on any ring.  Zero-copy monitors,
- * instead, need exclusive access to each of the monitored rings.  This may
- * change in the future, if we implement zero-copy monitor chaining.
+ * Several copy or zero-copy monitors may be active on any ring.
  *
  */
 
@@ -141,7 +139,7 @@ nm_is_zmon(struct netmap_adapter *na)
 static int
 netmap_monitor_txsync(struct netmap_kring *kring, int flags)
 {
-        RD(1, "%s %x", kring->name, flags);
+	nm_prlim(1, "%s %x", kring->name, flags);
 	return EIO;
 }
 
@@ -154,10 +152,16 @@ netmap_monitor_txsync(struct netmap_kring *kring, int flags)
 static int
 netmap_monitor_rxsync(struct netmap_kring *kring, int flags)
 {
-        ND("%s %x", kring->name, flags);
+	struct netmap_monitor_adapter *mna =
+		(struct netmap_monitor_adapter *)kring->na;
+	if (unlikely(mna->priv.np_na == NULL)) {
+		/* parent left netmap mode */
+		return EIO;
+	}
+	nm_prdis("%s %x", kring->name, flags);
 	kring->nr_hwcur = kring->rhead;
 	mb();
-        return 0;
+	return 0;
 }
 
 /* nm_krings_create callbacks for monitors.
@@ -166,11 +170,20 @@ static int
 netmap_monitor_krings_create(struct netmap_adapter *na)
 {
 	int error = netmap_krings_create(na, 0);
+	enum txrx t;
+
 	if (error)
 		return error;
 	/* override the host rings callbacks */
-	na->tx_rings[na->num_tx_rings].nm_sync = netmap_monitor_txsync;
-	na->rx_rings[na->num_rx_rings].nm_sync = netmap_monitor_rxsync;
+	for_rx_tx(t) {
+		int i;
+		u_int first = nma_get_nrings(na, t);
+		for (i = 0; i < nma_get_host_nrings(na, t); i++) {
+			struct netmap_kring *kring = NMR(na, t)[first + i];
+			kring->nm_sync = t == NR_TX ? netmap_monitor_txsync :
+						      netmap_monitor_rxsync;
+		}
+	}
 	return 0;
 }
 
@@ -200,7 +213,7 @@ nm_monitor_alloc(struct netmap_kring *kring, u_int n)
 		return 0;
 
 	old_len = sizeof(struct netmap_kring *)*kring->max_monitors;
-        len = sizeof(struct netmap_kring *) * n;
+	len = sizeof(struct netmap_kring *) * n;
 	nm = nm_os_realloc(kring->monitors, len, old_len);
 	if (nm == NULL)
 		return ENOMEM;
@@ -217,8 +230,8 @@ nm_monitor_dealloc(struct netmap_kring *kring)
 {
 	if (kring->monitors) {
 		if (kring->n_monitors > 0) {
-			D("freeing not empty monitor array for %s (%d dangling monitors)!", kring->name,
-					kring->n_monitors);
+			nm_prerr("freeing not empty monitor array for %s (%d dangling monitors)!",
+			    kring->name, kring->n_monitors);
 		}
 		nm_os_free(kring->monitors);
 		kring->monitors = NULL;
@@ -246,6 +259,57 @@ static int netmap_monitor_parent_txsync(struct netmap_kring *, int);
 static int netmap_monitor_parent_rxsync(struct netmap_kring *, int);
 static int netmap_monitor_parent_notify(struct netmap_kring *, int);
 
+static int
+nm_monitor_dummycb(struct netmap_kring *kring, int flags)
+{
+	(void)kring;
+	(void)flags;
+	return 0;
+}
+
+static void
+nm_monitor_intercept_callbacks(struct netmap_kring *kring)
+{
+	nm_prdis("intercept callbacks on %s", kring->name);
+	kring->mon_sync = kring->nm_sync != NULL ?
+		kring->nm_sync : nm_monitor_dummycb;
+	kring->mon_notify = kring->nm_notify;
+	if (kring->tx == NR_TX) {
+		kring->nm_sync = netmap_monitor_parent_txsync;
+	} else {
+		kring->nm_sync = netmap_monitor_parent_rxsync;
+		kring->nm_notify = netmap_monitor_parent_notify;
+		kring->mon_tail = kring->nr_hwtail;
+	}
+}
+
+static void
+nm_monitor_restore_callbacks(struct netmap_kring *kring)
+{
+	nm_prdis("restoring callbacks on %s", kring->name);
+	kring->nm_sync = kring->mon_sync;
+	kring->mon_sync = NULL;
+	if (kring->tx == NR_RX) {
+		kring->nm_notify = kring->mon_notify;
+	}
+	kring->mon_notify = NULL;
+}
+
+static struct netmap_kring *
+nm_zmon_list_head(struct netmap_kring *mkring, enum txrx t)
+{
+	struct netmap_adapter *na = mkring->na;
+	struct netmap_kring *kring = mkring;
+	struct netmap_zmon_list *z = &kring->zmon_list[t];
+	/* reach the head of the list */
+	while (nm_is_zmon(na) && z->prev != NULL) {
+		kring = z->prev;
+		na = kring->na;
+		z = &kring->zmon_list[t];
+	}
+	return nm_is_zmon(na) ? NULL : kring;
+}
+
 /* add the monitor mkring to the list of monitors of kring.
  * If this is the first monitor, intercept the callbacks
  */
@@ -256,51 +320,34 @@ netmap_monitor_add(struct netmap_kring *mkring, struct netmap_kring *kring, int
 	enum txrx t = kring->tx;
 	struct netmap_zmon_list *z = &kring->zmon_list[t];
 	struct netmap_zmon_list *mz = &mkring->zmon_list[t];
+	struct netmap_kring *ikring = kring;
 
 	/* a zero-copy monitor which is not the first in the list
 	 * must monitor the previous monitor
 	 */
 	if (zmon && z->prev != NULL)
-		kring = z->prev;
+		ikring = z->prev; /* tail of the list */
 
-	/* sinchronize with concurrently running nm_sync()s */
+	/* synchronize with concurrently running nm_sync()s */
 	nm_kr_stop(kring, NM_KR_LOCKED);
 
-	if (nm_monitor_none(kring)) {
-		/* this is the first monitor, intercept callbacks */
-		ND("intercept callbacks on %s", kring->name);
-		kring->mon_sync = kring->nm_sync;
-		kring->mon_notify = kring->nm_notify;
-		if (kring->tx == NR_TX) {
-			kring->nm_sync = netmap_monitor_parent_txsync;
-		} else {
-			kring->nm_sync = netmap_monitor_parent_rxsync;
-			kring->nm_notify = netmap_monitor_parent_notify;
-			kring->mon_tail = kring->nr_hwtail;
-		}
+	if (nm_monitor_none(ikring)) {
+		/* this is the first monitor, intercept the callbacks */
+		nm_prdis("%s: intercept callbacks on %s", mkring->name, ikring->name);
+		nm_monitor_intercept_callbacks(ikring);
 	}
 
 	if (zmon) {
 		/* append the zmon to the list */
-		struct netmap_monitor_adapter *mna =
-			(struct netmap_monitor_adapter *)mkring->na;
-		struct netmap_adapter *pna;
-
-		if (z->prev != NULL)
-			z->prev->zmon_list[t].next = mkring;
-		mz->prev = z->prev;
-		z->prev = mkring;
-		if (z->next == NULL)
-			z->next = mkring;
-
-		/* grap a reference to the previous netmap adapter
+		ikring->zmon_list[t].next = mkring;
+		z->prev = mkring; /* new tail */
+		mz->prev = ikring;
+		mz->next = NULL;
+		/* grab a reference to the previous netmap adapter
 		 * in the chain (this may be the monitored port
 		 * or another zero-copy monitor)
 		 */
-		pna = kring->na;
-		netmap_adapter_get(pna);
-		netmap_adapter_put(mna->priv.np_na);
-		mna->priv.np_na = pna;
+		netmap_adapter_get(ikring->na);
 	} else {
 		/* make sure the monitor array exists and is big enough */
 		error = nm_monitor_alloc(kring, kring->n_monitors + 1);
@@ -320,29 +367,50 @@ netmap_monitor_add(struct netmap_kring *mkring, struct netmap_kring *kring, int
  * If this is the last monitor, restore the original callbacks
  */
 static void
-netmap_monitor_del(struct netmap_kring *mkring, struct netmap_kring *kring)
+netmap_monitor_del(struct netmap_kring *mkring, struct netmap_kring *kring, enum txrx t)
 {
-	struct netmap_zmon_list *mz = &mkring->zmon_list[kring->tx];
 	int zmon = nm_is_zmon(mkring->na);
+	struct netmap_zmon_list *mz = &mkring->zmon_list[t];
+	struct netmap_kring *ikring = kring;
 
 
-	if (zmon && mz->prev != NULL)
-		kring = mz->prev;
+	if (zmon) {
+		/* get to the head of the list */
+		kring = nm_zmon_list_head(mkring, t);
+		ikring = mz->prev;
+	}
 
-	/* sinchronize with concurrently running nm_sync()s */
-	nm_kr_stop(kring, NM_KR_LOCKED);
+	/* synchronize with concurrently running nm_sync()s
+	 * if kring is NULL (orphaned list) the monitored port
+	 * has exited netmap mode, so there is nothing to stop
+	 */
+	if (kring != NULL)
+		nm_kr_stop(kring, NM_KR_LOCKED);
 
 	if (zmon) {
 		/* remove the monitor from the list */
-		if (mz->prev != NULL)
-			mz->prev->zmon_list[kring->tx].next = mz->next;
-		else
-			kring->zmon_list[kring->tx].next = mz->next;
 		if (mz->next != NULL) {
-			mz->next->zmon_list[kring->tx].prev = mz->prev;
-		} else {
-			kring->zmon_list[kring->tx].prev = mz->prev;
+			mz->next->zmon_list[t].prev = mz->prev;
+			/* we also need to let the next monitor drop the
+			 * reference to us and grab the reference to the
+			 * previous ring owner, instead
+			 */
+			if (mz->prev != NULL)
+				netmap_adapter_get(mz->prev->na);
+			netmap_adapter_put(mkring->na);
+		} else if (kring != NULL) {
+			/* in the monitored kring, prev is actually the
+			 * pointer to the tail of the list
+			 */
+			kring->zmon_list[t].prev =
+				(mz->prev != kring ? mz->prev : NULL);
+		}
+		if (mz->prev != NULL) {
+			netmap_adapter_put(mz->prev->na);
+			mz->prev->zmon_list[t].next = mz->next;
 		}
+		mz->prev = NULL;
+		mz->next = NULL;
 	} else {
 		/* this is a copy monitor */
 		uint32_t mon_pos = mkring->mon_pos[kring->tx];
@@ -358,21 +426,13 @@ netmap_monitor_del(struct netmap_kring *mkring, struct netmap_kring *kring)
 		}
 	}
 
-	if (nm_monitor_none(kring)) {
+	if (ikring != NULL && nm_monitor_none(ikring)) {
 		/* this was the last monitor, restore the callbacks */
-		ND("%s: restoring sync on %s: %p", mkring->name, kring->name,
-				kring->mon_sync);
-		kring->nm_sync = kring->mon_sync;
-		kring->mon_sync = NULL;
-		if (kring->tx == NR_RX) {
-			ND("%s: restoring notify on %s: %p",
-					mkring->name, kring->name, kring->mon_notify);
-			kring->nm_notify = kring->mon_notify;
-			kring->mon_notify = NULL;
-		}
+		nm_monitor_restore_callbacks(ikring);
 	}
 
-	nm_kr_start(kring);
+	if (kring != NULL)
+		nm_kr_start(kring);
 }
 
 
@@ -391,11 +451,14 @@ netmap_monitor_stop(struct netmap_adapter *na)
 	for_rx_tx(t) {
 		u_int i;
 
-		for (i = 0; i < nma_get_nrings(na, t) + 1; i++) {
-			struct netmap_kring *kring = &NMR(na, t)[i];
-			struct netmap_kring *zkring;
+		for (i = 0; i < netmap_all_rings(na, t); i++) {
+			struct netmap_kring *kring = NMR(na, t)[i];
+			struct netmap_zmon_list *z = &kring->zmon_list[t];
 			u_int j;
 
+			if (nm_monitor_none(kring))
+				continue;
+
 			for (j = 0; j < kring->n_monitors; j++) {
 				struct netmap_kring *mkring =
 					kring->monitors[j];
@@ -406,30 +469,33 @@ netmap_monitor_stop(struct netmap_adapter *na)
 					netmap_adapter_put(mna->priv.np_na);
 					mna->priv.np_na = NULL;
 				}
+				kring->monitors[j] = NULL;
 			}
+			kring->n_monitors = 0;
+			nm_monitor_dealloc(kring);
 
-			zkring = kring->zmon_list[kring->tx].next;
-			if (zkring != NULL) {
-				struct netmap_monitor_adapter *next =
-					(struct netmap_monitor_adapter *)zkring->na;
-				struct netmap_monitor_adapter *this =
-						(struct netmap_monitor_adapter *)na;
-				struct netmap_adapter *pna = this->priv.np_na;
-				/* let the next monitor forget about us */
-				if (next->priv.np_na != NULL) {
-					netmap_adapter_put(next->priv.np_na);
-				}
-				if (pna != NULL && nm_is_zmon(na)) {
-					/* we are a monitor ourselves and we may
-					 * need to pass down the reference to
-					 * the previous adapter in the chain
-					 */
-					netmap_adapter_get(pna);
-					next->priv.np_na = pna;
-					continue;
+			if (!nm_is_zmon(na)) {
+				/* we are the head of at most one list */
+				struct netmap_kring *zkring;
+				for (zkring = z->next; zkring != NULL;
+						zkring = zkring->zmon_list[t].next)
+				{
+					struct netmap_monitor_adapter *next =
+						(struct netmap_monitor_adapter *)zkring->na;
+					/* let the monitor forget about us */
+					netmap_adapter_put(next->priv.np_na); /* nop if null */
+					next->priv.np_na = NULL;
+					/* drop the additional ref taken in netmap_monitor_add() */
+					netmap_adapter_put(zkring->zmon_list[t].prev->na);
 				}
-				next->priv.np_na = NULL;
+				/* orphan the zmon list */
+				if (z->next != NULL)
+					z->next->zmon_list[t].prev = NULL;
+				z->next = NULL;
+				z->prev = NULL;
 			}
+
+			nm_monitor_restore_callbacks(kring);
 		}
 	}
 }
@@ -449,16 +515,16 @@ netmap_monitor_reg_common(struct netmap_adapter *na, int onoff, int zmon)
 	int i;
 	enum txrx t, s;
 
-	ND("%p: onoff %d", na, onoff);
+	nm_prdis("%p: onoff %d", na, onoff);
 	if (onoff) {
 		if (pna == NULL) {
 			/* parent left netmap mode, fatal */
-			D("%s: internal error", na->name);
+			nm_prerr("%s: parent left netmap mode", na->name);
 			return ENXIO;
 		}
 		for_rx_tx(t) {
-			for (i = 0; i < nma_get_nrings(na, t) + 1; i++) {
-				mkring = &NMR(na, t)[i];
+			for (i = 0; i < netmap_all_rings(na, t); i++) {
+				mkring = NMR(na, t)[i];
 				if (!nm_kring_pending_on(mkring))
 					continue;
 				mkring->nr_mode = NKR_NETMAP_ON;
@@ -468,7 +534,7 @@ netmap_monitor_reg_common(struct netmap_adapter *na, int onoff, int zmon)
 					if (i > nma_get_nrings(pna, s))
 						continue;
 					if (mna->flags & nm_txrx2flag(s)) {
-						kring = &NMR(pna, s)[i];
+						kring = NMR(pna, s)[i];
 						netmap_monitor_add(mkring, kring, zmon);
 					}
 				}
@@ -479,8 +545,8 @@ netmap_monitor_reg_common(struct netmap_adapter *na, int onoff, int zmon)
 		if (na->active_fds == 0)
 			na->na_flags &= ~NAF_NETMAP_ON;
 		for_rx_tx(t) {
-			for (i = 0; i < nma_get_nrings(na, t) + 1; i++) {
-				mkring = &NMR(na, t)[i];
+			for (i = 0; i < netmap_all_rings(na, t); i++) {
+				mkring = NMR(na, t)[i];
 				if (!nm_kring_pending_off(mkring))
 					continue;
 				mkring->nr_mode = NKR_NETMAP_OFF;
@@ -496,8 +562,8 @@ netmap_monitor_reg_common(struct netmap_adapter *na, int onoff, int zmon)
 					if (i > nma_get_nrings(pna, s))
 						continue;
 					if (mna->flags & nm_txrx2flag(s)) {
-						kring = &NMR(pna, s)[i];
-						netmap_monitor_del(mkring, kring);
+						kring = NMR(pna, s)[i];
+						netmap_monitor_del(mkring, kring, s);
 					}
 				}
 			}
@@ -526,15 +592,16 @@ netmap_zmon_parent_sync(struct netmap_kring *kring, int flags, enum txrx tx)
 	u_int beg, end, i;
 	u_int lim = kring->nkr_num_slots - 1,
 	      mlim; // = mkring->nkr_num_slots - 1;
+	uint16_t txmon = kring->tx == NR_TX ? NS_TXMON : 0;
 
 	if (mkring == NULL) {
-		RD(5, "NULL monitor on %s", kring->name);
+		nm_prlim(5, "NULL monitor on %s", kring->name);
 		return 0;
 	}
 	mring = mkring->ring;
 	mlim = mkring->nkr_num_slots - 1;
 
-	/* get the relased slots (rel_slots) */
+	/* get the released slots (rel_slots) */
 	if (tx == NR_TX) {
 		beg = kring->nr_hwtail + 1;
 		error = kring->mon_sync(kring, flags);
@@ -589,12 +656,13 @@ netmap_zmon_parent_sync(struct netmap_kring *kring, int flags, enum txrx tx)
 		tmp = ms->buf_idx;
 		ms->buf_idx = s->buf_idx;
 		s->buf_idx = tmp;
-		ND(5, "beg %d buf_idx %d", beg, tmp);
+		nm_prdis(5, "beg %d buf_idx %d", beg, tmp);
 
 		tmp = ms->len;
 		ms->len = s->len;
 		s->len = tmp;
 
+		ms->flags = (s->flags & ~NS_TXMON) | txmon;
 		s->flags |= NS_BUF_CHANGED;
 
 		beg = nm_next(beg, lim);
@@ -623,14 +691,14 @@ netmap_zmon_parent_sync(struct netmap_kring *kring, int flags, enum txrx tx)
 static int
 netmap_zmon_parent_txsync(struct netmap_kring *kring, int flags)
 {
-        return netmap_zmon_parent_sync(kring, flags, NR_TX);
+	return netmap_zmon_parent_sync(kring, flags, NR_TX);
 }
 
 /* callback used to replace the nm_sync callback in the monitored rx rings */
 static int
 netmap_zmon_parent_rxsync(struct netmap_kring *kring, int flags)
 {
-        return netmap_zmon_parent_sync(kring, flags, NR_RX);
+	return netmap_zmon_parent_sync(kring, flags, NR_RX);
 }
 
 static int
@@ -661,6 +729,7 @@ static void
 netmap_monitor_parent_sync(struct netmap_kring *kring, u_int first_new, int new_slots)
 {
 	u_int j;
+	uint16_t txmon = kring->tx == NR_TX ? NS_TXMON : 0;
 
 	for (j = 0; j < kring->n_monitors; j++) {
 		struct netmap_kring *mkring = kring->monitors[j];
@@ -668,8 +737,7 @@ netmap_monitor_parent_sync(struct netmap_kring *kring, u_int first_new, int new_
 		int free_slots, busy, sent = 0, m;
 		u_int lim = kring->nkr_num_slots - 1;
 		struct netmap_ring *ring = kring->ring, *mring = mkring->ring;
-		u_int max_len = NETMAP_BUF_SIZE(mkring->na);
-
+		u_int max_len;
 		mlim = mkring->nkr_num_slots - 1;
 
 		/* we need to lock the monitor receive ring, since it
@@ -701,17 +769,19 @@ netmap_monitor_parent_sync(struct netmap_kring *kring, u_int first_new, int new_
 			struct netmap_slot *s = &ring->slot[beg];
 			struct netmap_slot *ms = &mring->slot[i];
 			u_int copy_len = s->len;
-			char *src = NMB(kring->na, s),
-			     *dst = NMB(mkring->na, ms);
+			char *src = NMB_O(kring, s),
+			     *dst = NMB_O(mkring, ms);
 
+			max_len = NETMAP_BUF_SIZE(mkring->na) - nm_get_offset(mkring, ms);
 			if (unlikely(copy_len > max_len)) {
-				RD(5, "%s->%s: truncating %d to %d", kring->name,
+				nm_prlim(5, "%s->%s: truncating %d to %d", kring->name,
 						mkring->name, copy_len, max_len);
 				copy_len = max_len;
 			}
 
 			memcpy(dst, src, copy_len);
 			ms->len = copy_len;
+			ms->flags = (s->flags & ~NS_TXMON) | txmon;
 			sent++;
 
 			beg = nm_next(beg, lim);
@@ -783,7 +853,7 @@ static int
 netmap_monitor_parent_notify(struct netmap_kring *kring, int flags)
 {
 	int (*notify)(struct netmap_kring*, int);
-	ND(5, "%s %x", kring->name, flags);
+	nm_prdis(5, "%s %x", kring->name, flags);
 	/* ?xsync callbacks have tryget called by their callers
 	 * (NIOCREGIF and poll()), but here we have to call it
 	 * by ourself
@@ -804,7 +874,7 @@ netmap_monitor_parent_notify(struct netmap_kring *kring, int flags)
 		notify = kring->mon_notify;
 	}
 	nm_kr_put(kring);
-        return notify(kring, flags);
+	return notify(kring, flags);
 }
 
 
@@ -826,79 +896,79 @@ netmap_monitor_dtor(struct netmap_adapter *na)
 }
 
 
-/* check if nmr is a request for a monitor adapter that we can satisfy */
+/* check if req is a request for a monitor adapter that we can satisfy */
 int
-netmap_get_monitor_na(struct nmreq *nmr, struct netmap_adapter **na,
-		struct netmap_mem_d *nmd, int create)
+netmap_get_monitor_na(struct nmreq_header *hdr, struct netmap_adapter **na,
+			struct netmap_mem_d *nmd, int create)
 {
-	struct nmreq pnmr;
+	struct nmreq_register *req = (struct nmreq_register *)(uintptr_t)hdr->nr_body;
+	struct nmreq_register preq;
 	struct netmap_adapter *pna; /* parent adapter */
 	struct netmap_monitor_adapter *mna;
-	struct ifnet *ifp = NULL;
+	if_t ifp = NULL;
 	int  error;
-	int zcopy = (nmr->nr_flags & NR_ZCOPY_MON);
-	char monsuff[10] = "";
+	int zcopy = (req->nr_flags & NR_ZCOPY_MON);
 
 	if (zcopy) {
-		nmr->nr_flags |= (NR_MONITOR_TX | NR_MONITOR_RX);
+		req->nr_flags |= (NR_MONITOR_TX | NR_MONITOR_RX);
 	}
-	if ((nmr->nr_flags & (NR_MONITOR_TX | NR_MONITOR_RX)) == 0) {
-		ND("not a monitor");
+	if ((req->nr_flags & (NR_MONITOR_TX | NR_MONITOR_RX)) == 0) {
+		nm_prdis("not a monitor");
 		return 0;
 	}
 	/* this is a request for a monitor adapter */
 
-	ND("flags %x", nmr->nr_flags);
+	nm_prdis("flags %lx", req->nr_flags);
 
-	/* first, try to find the adapter that we want to monitor
-	 * We use the same nmr, after we have turned off the monitor flags.
+	/* First, try to find the adapter that we want to monitor.
+	 * We use the same req, after we have turned off the monitor flags.
 	 * In this way we can potentially monitor everything netmap understands,
 	 * except other monitors.
 	 */
-	memcpy(&pnmr, nmr, sizeof(pnmr));
-	pnmr.nr_flags &= ~(NR_MONITOR_TX | NR_MONITOR_RX | NR_ZCOPY_MON);
-	error = netmap_get_na(&pnmr, &pna, &ifp, nmd, create);
+	memcpy(&preq, req, sizeof(preq));
+	preq.nr_flags &= ~(NR_MONITOR_TX | NR_MONITOR_RX | NR_ZCOPY_MON);
+	hdr->nr_body = (uintptr_t)&preq;
+	error = netmap_get_na(hdr, &pna, &ifp, nmd, create);
+	hdr->nr_body = (uintptr_t)req;
 	if (error) {
-		D("parent lookup failed: %d", error);
+		nm_prerr("parent lookup failed: %d", error);
 		return error;
 	}
-	ND("found parent: %s", pna->name);
+	nm_prdis("found parent: %s", pna->name);
 
 	if (!nm_netmap_on(pna)) {
 		/* parent not in netmap mode */
 		/* XXX we can wait for the parent to enter netmap mode,
 		 * by intercepting its nm_register callback (2014-03-16)
 		 */
-		D("%s not in netmap mode", pna->name);
+		nm_prerr("%s not in netmap mode", pna->name);
 		error = EINVAL;
 		goto put_out;
 	}
 
 	mna = nm_os_malloc(sizeof(*mna));
 	if (mna == NULL) {
-		D("memory error");
 		error = ENOMEM;
 		goto put_out;
 	}
 	mna->priv.np_na = pna;
 
 	/* grab all the rings we need in the parent */
-	error = netmap_interp_ringid(&mna->priv, nmr->nr_ringid, nmr->nr_flags);
+	error = netmap_interp_ringid(&mna->priv, hdr);
 	if (error) {
-		D("ringid error");
+		nm_prerr("ringid error");
 		goto free_out;
 	}
-	if (mna->priv.np_qlast[NR_TX] - mna->priv.np_qfirst[NR_TX] == 1) {
-		snprintf(monsuff, 10, "-%d", mna->priv.np_qfirst[NR_TX]);
-	}
-	snprintf(mna->up.name, sizeof(mna->up.name), "%s%s/%s%s%s", pna->name,
-			monsuff,
+	snprintf(mna->up.name, sizeof(mna->up.name), "%s/%s%s%s#%lu", pna->name,
 			zcopy ? "z" : "",
-			(nmr->nr_flags & NR_MONITOR_RX) ? "r" : "",
-			(nmr->nr_flags & NR_MONITOR_TX) ? "t" : "");
+			(req->nr_flags & NR_MONITOR_RX) ? "r" : "",
+			(req->nr_flags & NR_MONITOR_TX) ? "t" : "",
+			pna->monitor_id++);
 
 	/* the monitor supports the host rings iff the parent does */
-	mna->up.na_flags |= (pna->na_flags & NAF_HOST_RINGS);
+	mna->up.na_flags |= (pna->na_flags & NAF_HOST_RINGS) & ~NAF_OFFSETS;
+	if (!zcopy)
+		mna->up.na_flags |= NAF_OFFSETS;
 	/* a do-nothing txsync: monitors cannot be used to inject packets */
 	mna->up.nm_txsync = netmap_monitor_txsync;
 	mna->up.nm_rxsync = netmap_monitor_rxsync;
@@ -915,10 +985,10 @@ netmap_get_monitor_na(struct nmreq *nmr, struct netmap_adapter **na,
 	 * the parent rings, but the user may ask for a different
 	 * number
 	 */
-	mna->up.num_tx_desc = nmr->nr_tx_slots;
+	mna->up.num_tx_desc = req->nr_tx_slots;
 	nm_bound_var(&mna->up.num_tx_desc, pna->num_tx_desc,
 			1, NM_MONITOR_MAXSLOTS, NULL);
-	mna->up.num_rx_desc = nmr->nr_rx_slots;
+	mna->up.num_rx_desc = req->nr_rx_slots;
 	nm_bound_var(&mna->up.num_rx_desc, pna->num_rx_desc,
 			1, NM_MONITOR_MAXSLOTS, NULL);
 	if (zcopy) {
@@ -947,18 +1017,18 @@ netmap_get_monitor_na(struct nmreq *nmr, struct netmap_adapter **na,
 
 	error = netmap_attach_common(&mna->up);
 	if (error) {
-		D("attach_common error");
+		nm_prerr("netmap_attach_common failed");
 		goto mem_put_out;
 	}
 
 	/* remember the traffic directions we have to monitor */
-	mna->flags = (nmr->nr_flags & (NR_MONITOR_TX | NR_MONITOR_RX | NR_ZCOPY_MON));
+	mna->flags = (req->nr_flags & (NR_MONITOR_TX | NR_MONITOR_RX | NR_ZCOPY_MON));
 
 	*na = &mna->up;
 	netmap_adapter_get(*na);
 
 	/* keep the reference to the parent */
-	ND("monitor ok");
+	nm_prdis("monitor ok");
 
 	/* drop the reference to the ifp, if any */
 	if (ifp)
diff --git a/sys/dev/netmap/netmap_null.c b/sys/dev/netmap/netmap_null.c
new file mode 100644
index 000000000..8066c1363
--- /dev/null
+++ b/sys/dev/netmap/netmap_null.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2018 Giuseppe Lettieri
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(__FreeBSD__)
+#include  /* prerequisite */
+
+#include 
+#include 
+#include 	/* defines used in kernel.h */
+#include 	/* types used in module initialization */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include  /* sockaddrs */
+#include 
+#include 
+#include 	/* bus_dmamap_* */
+#include 
+
+
+#elif defined(linux)
+
+#include "bsd_glue.h"
+
+#elif defined(__APPLE__)
+
+#warning OSX support is only partial
+#include "osx_glue.h"
+
+#elif defined(_WIN32)
+#include "win_glue.h"
+
+#else
+
+#error	Unsupported platform
+
+#endif /* unsupported */
+
+/*
+ * common headers
+ */
+
+#include 
+#include 
+#include 
+
+#ifdef WITH_NMNULL
+
+static int
+netmap_null_sync(struct netmap_kring *kring, int flags)
+{
+	(void)kring;
+	(void)flags;
+	return 0;
+}
+
+static int
+netmap_null_krings_create(struct netmap_adapter *na)
+{
+	return netmap_krings_create(na, 0);
+}
+
+static int
+netmap_null_reg(struct netmap_adapter *na, int onoff)
+{
+	if (na->active_fds == 0) {
+		if (onoff)
+			na->na_flags |= NAF_NETMAP_ON;
+		else
+			na->na_flags &= ~NAF_NETMAP_ON;
+	}
+	return 0;
+}
+
+static int
+netmap_null_bdg_attach(const char *name, struct netmap_adapter *na,
+		struct nm_bridge *b)
+{
+	(void)name;
+	(void)na;
+	(void)b;
+	return EINVAL;
+}
+
+int
+netmap_get_null_na(struct nmreq_header *hdr, struct netmap_adapter **na,
+		struct netmap_mem_d *nmd, int create)
+{
+	struct nmreq_register *req = (struct nmreq_register *)(uintptr_t)hdr->nr_body;
+	struct netmap_null_adapter *nna;
+	int error;
+
+	if (req->nr_mode != NR_REG_NULL) {
+		nm_prdis("not a null port");
+		return 0;
+	}
+
+	if (!create) {
+		nm_prerr("null ports cannot be re-opened");
+		return EINVAL;
+	}
+
+	if (nmd == NULL) {
+		nm_prerr("null ports must use an existing allocator");
+		return EINVAL;
+	}
+
+	nna = nm_os_malloc(sizeof(*nna));
+	if (nna == NULL) {
+		error = ENOMEM;
+		goto err;
+	}
+	snprintf(nna->up.name, sizeof(nna->up.name), "null:%s", hdr->nr_name);
+
+	nna->up.nm_txsync = netmap_null_sync;
+	nna->up.nm_rxsync = netmap_null_sync;
+	nna->up.nm_register = netmap_null_reg;
+	nna->up.nm_krings_create = netmap_null_krings_create;
+	nna->up.nm_krings_delete = netmap_krings_delete;
+	nna->up.nm_bdg_attach = netmap_null_bdg_attach;
+	nna->up.nm_mem = netmap_mem_get(nmd);
+
+	nna->up.num_tx_rings = req->nr_tx_rings;
+	nna->up.num_rx_rings = req->nr_rx_rings;
+	nna->up.num_tx_desc = req->nr_tx_slots;
+	nna->up.num_rx_desc = req->nr_rx_slots;
+	nna->up.na_flags = NAF_OFFSETS;
+	error = netmap_attach_common(&nna->up);
+	if (error)
+		goto free_nna;
+	*na = &nna->up;
+	netmap_adapter_get(*na);
+	nm_prdis("created null %s", nna->up.name);
+
+	return 0;
+
+free_nna:
+	nm_os_free(nna);
+err:
+	return error;
+}
+
+
+#endif /* WITH_NMNULL */
diff --git a/sys/dev/netmap/netmap_offloadings.c b/sys/dev/netmap/netmap_offloadings.c
index 8e5de7f7a..96dad21a6 100644
--- a/sys/dev/netmap/netmap_offloadings.c
+++ b/sys/dev/netmap/netmap_offloadings.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2014-2015 Vincenzo Maffione
  * All rights reserved.
  *
@@ -24,8 +26,6 @@
  * SUCH DAMAGE.
  */
 
-/* $FreeBSD: head/sys/dev/netmap/netmap_offloadings.c 261909 2014-02-15 04:53:04Z luigi $ */
-
 #if defined(__FreeBSD__)
 #include  /* prerequisite */
 
@@ -80,16 +80,16 @@ gso_fix_segment(uint8_t *pkt, size_t len, u_int ipv4, u_int iphlen, u_int tcp,
 	if (ipv4) {
 		/* Set the IPv4 "Total Length" field. */
 		iph->tot_len = htobe16(len);
-		ND("ip total length %u", be16toh(ip->tot_len));
+		nm_prdis("ip total length %u", be16toh(ip->tot_len));
 
 		/* Set the IPv4 "Identification" field. */
 		iph->id = htobe16(be16toh(iph->id) + idx);
-		ND("ip identification %u", be16toh(iph->id));
+		nm_prdis("ip identification %u", be16toh(iph->id));
 
 		/* Compute and insert the IPv4 header checksum. */
 		iph->check = 0;
 		iph->check = nm_os_csum_ipv4(iph);
-		ND("IP csum %x", be16toh(iph->check));
+		nm_prdis("IP csum %x", be16toh(iph->check));
 	} else {
 		/* Set the IPv6 "Payload Len" field. */
 		ip6h->payload_len = htobe16(len-iphlen);
@@ -100,13 +100,13 @@ gso_fix_segment(uint8_t *pkt, size_t len, u_int ipv4, u_int iphlen, u_int tcp,
 
 		/* Set the TCP sequence number. */
 		tcph->seq = htobe32(be32toh(tcph->seq) + segmented_bytes);
-		ND("tcp seq %u", be32toh(tcph->seq));
+		nm_prdis("tcp seq %u", be32toh(tcph->seq));
 
 		/* Zero the PSH and FIN TCP flags if this is not the last
 		   segment. */
 		if (!last_segment)
 			tcph->flags &= ~(0x8 | 0x1);
-		ND("last_segment %u", last_segment);
+		nm_prdis("last_segment %u", last_segment);
 
 		check = &tcph->check;
 		check_data = (uint8_t *)tcph;
@@ -127,10 +127,10 @@ gso_fix_segment(uint8_t *pkt, size_t len, u_int ipv4, u_int iphlen, u_int tcp,
 	else
 		nm_os_csum_tcpudp_ipv6(ip6h, check_data, len-iphlen, check);
 
-	ND("TCP/UDP csum %x", be16toh(*check));
+	nm_prdis("TCP/UDP csum %x", be16toh(*check));
 }
 
-static int
+static inline int
 vnet_hdr_is_bad(struct nm_vnet_hdr *vh)
 {
 	uint8_t gso_type = vh->gso_type & ~VIRTIO_NET_HDR_GSO_ECN;
@@ -168,7 +168,7 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 	u_int dst_slots = 0;
 
 	if (unlikely(ft_p == ft_end)) {
-		RD(3, "No source slots to process");
+		nm_prlim(1, "No source slots to process");
 		return;
 	}
 
@@ -187,11 +187,11 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 		/* Initial sanity check on the source virtio-net header. If
 		 * something seems wrong, just drop the packet. */
 		if (src_len < na->up.virt_hdr_len) {
-			RD(3, "Short src vnet header, dropping");
+			nm_prlim(1, "Short src vnet header, dropping");
 			return;
 		}
-		if (vnet_hdr_is_bad(vh)) {
-			RD(3, "Bad src vnet header, dropping");
+		if (unlikely(vnet_hdr_is_bad(vh))) {
+			nm_prlim(1, "Bad src vnet header, dropping");
 			return;
 		}
 	}
@@ -264,7 +264,7 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 			if (dst_slots >= *howmany) {
 				/* We still have work to do, but we've run out of
 				 * dst slots, so we have to drop the packet. */
-				RD(3, "Not enough slots, dropping GSO packet");
+				nm_prdis(1, "Not enough slots, dropping GSO packet");
 				return;
 			}
 
@@ -279,7 +279,7 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 				 * encapsulation. */
 				for (;;) {
 					if (src_len < ethhlen) {
-						RD(3, "Short GSO fragment [eth], dropping");
+						nm_prlim(1, "Short GSO fragment [eth], dropping");
 						return;
 					}
 					ethertype = be16toh(*((uint16_t *)
@@ -295,7 +295,7 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 									(gso_hdr + ethhlen);
 
 						if (src_len < ethhlen + 20) {
-							RD(3, "Short GSO fragment "
+							nm_prlim(1, "Short GSO fragment "
 							      "[IPv4], dropping");
 							return;
 						}
@@ -308,14 +308,14 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 						iphlen = 40;
 						break;
 					default:
-						RD(3, "Unsupported ethertype, "
+						nm_prlim(1, "Unsupported ethertype, "
 						      "dropping GSO packet");
 						return;
 				}
-				ND(3, "type=%04x", ethertype);
+				nm_prdis(3, "type=%04x", ethertype);
 
 				if (src_len < ethhlen + iphlen) {
-					RD(3, "Short GSO fragment [IP], dropping");
+					nm_prlim(1, "Short GSO fragment [IP], dropping");
 					return;
 				}
 
@@ -327,7 +327,7 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 								(gso_hdr + ethhlen + iphlen);
 
 					if (src_len < ethhlen + iphlen + 20) {
-						RD(3, "Short GSO fragment "
+						nm_prlim(1, "Short GSO fragment "
 								"[TCP], dropping");
 						return;
 					}
@@ -338,11 +338,11 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 				}
 
 				if (src_len < gso_hdr_len) {
-					RD(3, "Short GSO fragment [TCP/UDP], dropping");
+					nm_prlim(1, "Short GSO fragment [TCP/UDP], dropping");
 					return;
 				}
 
-				ND(3, "gso_hdr_len %u gso_mtu %d", gso_hdr_len,
+				nm_prdis(3, "gso_hdr_len %u gso_mtu %d", gso_hdr_len,
 								   dst_na->mfs);
 
 				/* Advance source pointers. */
@@ -384,7 +384,7 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 						gso_idx, segmented_bytes,
 						src_len == 0 && ft_p + 1 == ft_end);
 
-				ND("frame %u completed with %d bytes", gso_idx, (int)gso_bytes);
+				nm_prdis("frame %u completed with %d bytes", gso_idx, (int)gso_bytes);
 				dst_slot->len = gso_bytes;
 				dst_slot->flags = 0;
 				dst_slots++;
@@ -408,7 +408,7 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 				src_len = ft_p->ft_len;
 			}
 		}
-		ND(3, "%d bytes segmented", segmented_bytes);
+		nm_prdis(3, "%d bytes segmented", segmented_bytes);
 
 	} else {
 		/* Address of a checksum field into a destination slot. */
@@ -421,7 +421,7 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 		/* Init 'check' if necessary. */
 		if (vh && (vh->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) {
 			if (unlikely(vh->csum_offset + vh->csum_start > src_len))
-				D("invalid checksum request");
+				nm_prerr("invalid checksum request");
 			else
 				check = (uint16_t *)(dst + vh->csum_start +
 						vh->csum_offset);
@@ -466,7 +466,7 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 		if (check && vh && (vh->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) {
 			*check = nm_os_csum_fold(csum);
 		}
-		ND(3, "using %u dst_slots", dst_slots);
+		nm_prdis(3, "using %u dst_slots", dst_slots);
 
 		/* A second pass on the destination slots to set the slot flags,
 		 * using the right number of destination slots.
@@ -483,7 +483,7 @@ bdg_mismatch_datapath(struct netmap_vp_adapter *na,
 	/* Update howmany and j. This is to commit the use of
 	 * those slots in the destination ring. */
 	if (unlikely(dst_slots > *howmany)) {
-		D("Slot allocation error: This is a bug");
+		nm_prerr("bug: slot allocation error");
 	}
 	*j = j_cur;
 	*howmany -= dst_slots;
diff --git a/sys/dev/netmap/netmap_pipe.c b/sys/dev/netmap/netmap_pipe.c
index 36f5a3c9d..cfaa6554b 100644
--- a/sys/dev/netmap/netmap_pipe.c
+++ b/sys/dev/netmap/netmap_pipe.c
@@ -1,5 +1,7 @@
-/*
- * Copyright (C) 2014-2016 Giuseppe Lettieri
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2014-2018 Giuseppe Lettieri
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,8 +26,6 @@
  * SUCH DAMAGE.
  */
 
-/* $FreeBSD: head/sys/dev/netmap/netmap_pipe.c 261909 2014-02-15 04:53:04Z luigi $ */
-
 #if defined(__FreeBSD__)
 #include  /* prerequisite */
 
@@ -75,11 +75,13 @@
 #ifdef WITH_PIPES
 
 #define NM_PIPE_MAXSLOTS	4096
+#define NM_PIPE_MAXRINGS	256
 
 static int netmap_default_pipes = 0; /* ignored, kept for compatibility */
 SYSBEGIN(vars_pipes);
 SYSCTL_DECL(_dev_netmap);
-SYSCTL_INT(_dev_netmap, OID_AUTO, default_pipes, CTLFLAG_RW, &netmap_default_pipes, 0 , "");
+SYSCTL_INT(_dev_netmap, OID_AUTO, default_pipes, CTLFLAG_RW,
+		&netmap_default_pipes, 0, "For compatibility only");
 SYSEND;
 
 /* allocate the pipe array in the parent adapter */
@@ -97,7 +99,7 @@ nm_pipe_alloc(struct netmap_adapter *na, u_int npipes)
 		return EINVAL;
 
 	old_len = sizeof(struct netmap_pipe_adapter *)*na->na_max_pipes;
-        len = sizeof(struct netmap_pipe_adapter *) * npipes;
+	len = sizeof(struct netmap_pipe_adapter *) * npipes;
 	npa = nm_os_realloc(na->na_pipes, len, old_len);
 	if (npa == NULL)
 		return ENOMEM;
@@ -114,8 +116,8 @@ netmap_pipe_dealloc(struct netmap_adapter *na)
 {
 	if (na->na_pipes) {
 		if (na->na_next_pipe > 0) {
-			D("freeing not empty pipe array for %s (%d dangling pipes)!", na->name,
-					na->na_next_pipe);
+			nm_prerr("freeing not empty pipe array for %s (%d dangling pipes)!",
+			    na->name, na->na_next_pipe);
 		}
 		nm_os_free(na->na_pipes);
 		na->na_pipes = NULL;
@@ -126,14 +128,19 @@ netmap_pipe_dealloc(struct netmap_adapter *na)
 
 /* find a pipe endpoint with the given id among the parent's pipes */
 static struct netmap_pipe_adapter *
-netmap_pipe_find(struct netmap_adapter *parent, u_int pipe_id)
+netmap_pipe_find(struct netmap_adapter *parent, const char *pipe_id)
 {
 	int i;
 	struct netmap_pipe_adapter *na;
 
 	for (i = 0; i < parent->na_next_pipe; i++) {
+		const char *na_pipe_id;
 		na = parent->na_pipes[i];
-		if (na->id == pipe_id) {
+		na_pipe_id = strrchr(na->up.name,
+			na->role == NM_PIPE_ROLE_MASTER ? '{' : '}');
+		KASSERT(na_pipe_id != NULL, ("Invalid pipe name"));
+		++na_pipe_id;
+		if (!strcmp(na_pipe_id, pipe_id)) {
 			return na;
 		}
 	}
@@ -175,64 +182,56 @@ netmap_pipe_remove(struct netmap_adapter *parent, struct netmap_pipe_adapter *na
 int
 netmap_pipe_txsync(struct netmap_kring *txkring, int flags)
 {
-        struct netmap_kring *rxkring = txkring->pipe;
-        u_int limit; /* slots to transfer */
-        u_int j, k, lim_tx = txkring->nkr_num_slots - 1,
-                lim_rx = rxkring->nkr_num_slots - 1;
-        int m, busy;
-
-        ND("%p: %s %x -> %s", txkring, txkring->name, flags, rxkring->name);
-        ND(2, "before: hwcur %d hwtail %d cur %d head %d tail %d", txkring->nr_hwcur, txkring->nr_hwtail,
-                txkring->rcur, txkring->rhead, txkring->rtail);
-
-        j = rxkring->nr_hwtail; /* RX */
-        k = txkring->nr_hwcur;  /* TX */
-        m = txkring->rhead - txkring->nr_hwcur; /* new slots */
-        if (m < 0)
-                m += txkring->nkr_num_slots;
-        limit = m;
-        m = lim_rx; /* max avail space on destination */
-        busy = j - rxkring->nr_hwcur; /* busy slots */
-	if (busy < 0)
-		busy += rxkring->nkr_num_slots;
-	m -= busy; /* subtract busy slots */
-        ND(2, "m %d limit %d", m, limit);
-        if (m < limit)
-                limit = m;
-
-	if (limit == 0) {
-		/* either the rxring is full, or nothing to send */
+	struct netmap_kring *rxkring = txkring->pipe;
+	u_int k, lim = txkring->nkr_num_slots - 1, nk;
+	int m; /* slots to transfer */
+	int complete; /* did we see a complete packet ? */
+	struct netmap_ring *txring = txkring->ring, *rxring = rxkring->ring;
+
+	nm_prdis("%p: %s %x -> %s", txkring, txkring->name, flags, rxkring->name);
+	nm_prdis(20, "TX before: hwcur %d hwtail %d cur %d head %d tail %d",
+		txkring->nr_hwcur, txkring->nr_hwtail,
+		txkring->rcur, txkring->rhead, txkring->rtail);
+
+	/* update the hwtail */
+	txkring->nr_hwtail = txkring->pipe_tail;
+
+	m = txkring->rhead - txkring->nr_hwcur; /* new slots */
+	if (m < 0)
+		m += txkring->nkr_num_slots;
+
+	if (m == 0) {
+		/* nothing to send */
 		return 0;
 	}
 
-        while (limit-- > 0) {
-                struct netmap_slot *rs = &rxkring->ring->slot[j];
-                struct netmap_slot *ts = &txkring->ring->slot[k];
-                struct netmap_slot tmp;
-
-                /* swap the slots */
-                tmp = *rs;
-                *rs = *ts;
-                *ts = tmp;
+	for (k = txkring->nr_hwcur, nk = lim + 1, complete = 0; m;
+			m--, k = nm_next(k, lim), nk = (complete ? k : nk)) {
+		struct netmap_slot *rs = &rxring->slot[k];
+		struct netmap_slot *ts = &txring->slot[k];
+		uint64_t off = nm_get_offset(rxkring, rs);
 
-                /* report the buffer change */
-		ts->flags |= NS_BUF_CHANGED;
-		rs->flags |= NS_BUF_CHANGED;
-
-                j = nm_next(j, lim_rx);
-                k = nm_next(k, lim_tx);
-        }
+		*rs = *ts;
+		if (nm_get_offset(rxkring, rs) < off) {
+			nm_write_offset(rxkring, rs, off);
+		}
+		if (ts->flags & NS_BUF_CHANGED) {
+			ts->flags &= ~NS_BUF_CHANGED;
+		}
+		complete = !(ts->flags & NS_MOREFRAG);
+	}
 
-        mb(); /* make sure the slots are updated before publishing them */
-        rxkring->nr_hwtail = j;
-        txkring->nr_hwcur = k;
-        txkring->nr_hwtail = nm_prev(k, lim_tx);
+	txkring->nr_hwcur = k;
 
-        ND(2, "after: hwcur %d hwtail %d cur %d head %d tail %d j %d", txkring->nr_hwcur, txkring->nr_hwtail,
-                txkring->rcur, txkring->rhead, txkring->rtail, j);
+	nm_prdis(20, "TX after : hwcur %d hwtail %d cur %d head %d tail %d k %d",
+		txkring->nr_hwcur, txkring->nr_hwtail,
+		txkring->rcur, txkring->rhead, txkring->rtail, k);
 
-        mb(); /* make sure rxkring->nr_hwtail is updated before notifying */
-        rxkring->nm_notify(rxkring, 0);
+	if (likely(nk <= lim)) {
+		mb(); /* make sure the slots are updated before publishing them */
+		rxkring->pipe_tail = nk; /* only publish complete packets */
+		rxkring->nm_notify(rxkring, 0);
+	}
 
 	return 0;
 }
@@ -240,21 +239,50 @@ netmap_pipe_txsync(struct netmap_kring *txkring, int flags)
 int
 netmap_pipe_rxsync(struct netmap_kring *rxkring, int flags)
 {
-        struct netmap_kring *txkring = rxkring->pipe;
-	uint32_t oldhwcur = rxkring->nr_hwcur;
-
-        ND("%s %x <- %s", rxkring->name, flags, txkring->name);
-        rxkring->nr_hwcur = rxkring->rhead; /* recover user-relased slots */
-        ND(5, "hwcur %d hwtail %d cur %d head %d tail %d", rxkring->nr_hwcur, rxkring->nr_hwtail,
-                rxkring->rcur, rxkring->rhead, rxkring->rtail);
-        mb(); /* paired with the first mb() in txsync */
-
-	if (oldhwcur != rxkring->nr_hwcur) {
-		/* we have released some slots, notify the other end */
-		mb(); /* make sure nr_hwcur is updated before notifying */
-		txkring->nm_notify(txkring, 0);
+	struct netmap_kring *txkring = rxkring->pipe;
+	u_int k, lim = rxkring->nkr_num_slots - 1;
+	int m; /* slots to release */
+	struct netmap_ring *txring = txkring->ring, *rxring = rxkring->ring;
+
+	nm_prdis("%p: %s %x -> %s", txkring, txkring->name, flags, rxkring->name);
+	nm_prdis(20, "RX before: hwcur %d hwtail %d cur %d head %d tail %d",
+		rxkring->nr_hwcur, rxkring->nr_hwtail,
+		rxkring->rcur, rxkring->rhead, rxkring->rtail);
+
+	/* update the hwtail */
+	rxkring->nr_hwtail = rxkring->pipe_tail;
+
+	m = rxkring->rhead - rxkring->nr_hwcur; /* released slots */
+	if (m < 0)
+		m += rxkring->nkr_num_slots;
+
+	if (m == 0) {
+		/* nothing to release */
+		return 0;
 	}
-        return 0;
+
+	for (k = rxkring->nr_hwcur; m; m--, k = nm_next(k, lim)) {
+		struct netmap_slot *rs = &rxring->slot[k];
+		struct netmap_slot *ts = &txring->slot[k];
+
+		/* copy the slot. This also propagates any offset */
+		*ts = *rs;
+		if (rs->flags & NS_BUF_CHANGED) {
+			rs->flags &= ~NS_BUF_CHANGED;
+		}
+	}
+
+	mb(); /* make sure the slots are updated before publishing them */
+	txkring->pipe_tail = nm_prev(k, lim);
+	rxkring->nr_hwcur = k;
+
+	nm_prdis(20, "RX after : hwcur %d hwtail %d cur %d head %d tail %d k %d",
+		rxkring->nr_hwcur, rxkring->nr_hwtail,
+		rxkring->rcur, rxkring->rhead, rxkring->rtail, k);
+
+	txkring->nm_notify(txkring, 0);
+
+	return 0;
 }
 
 /* Pipe endpoints are created and destroyed together, so that endopoints do not
@@ -286,6 +314,47 @@ netmap_pipe_rxsync(struct netmap_kring *rxkring, int flags)
  */
 
 
+int netmap_pipe_krings_create_both(struct netmap_adapter *na,
+				  struct netmap_adapter *ona)
+{
+	enum txrx t;
+	int error;
+	int i;
+
+	/* case 1) below */
+	nm_prdis("%p: case 1, create both ends", na);
+	error = netmap_krings_create(na, 0);
+	if (error)
+		return error;
+
+	/* create the krings of the other end */
+	error = netmap_krings_create(ona, 0);
+	if (error)
+		goto del_krings1;
+
+	/* cross link the krings and initialize the pipe_tails */
+	for_rx_tx(t) {
+		enum txrx r = nm_txrx_swap(t); /* swap NR_TX <-> NR_RX */
+		for (i = 0; i < nma_get_nrings(na, t); i++) {
+			struct netmap_kring *k1 = NMR(na, t)[i],
+					    *k2 = NMR(ona, r)[i];
+			k1->pipe = k2;
+			k2->pipe = k1;
+			/* mark all peer-adapter rings as fake */
+			k2->nr_kflags |= NKR_FAKERING;
+			/* init tails */
+			k1->pipe_tail = k1->nr_hwtail;
+			k2->pipe_tail = k2->nr_hwtail;
+		}
+	}
+
+	return 0;
+
+del_krings1:
+	netmap_krings_delete(na);
+	return error;
+}
+
 /* netmap_pipe_krings_create.
  *
  * There are two cases:
@@ -310,39 +379,80 @@ netmap_pipe_krings_create(struct netmap_adapter *na)
 	struct netmap_pipe_adapter *pna =
 		(struct netmap_pipe_adapter *)na;
 	struct netmap_adapter *ona = &pna->peer->up;
-	int error = 0;
-	enum txrx t;
 
-	if (pna->peer_ref) {
-		int i;
+	if (pna->peer_ref)
+		return netmap_pipe_krings_create_both(na, ona);
 
-		/* case 1) above */
-		ND("%p: case 1, create both ends", na);
-		error = netmap_krings_create(na, 0);
-		if (error)
-			goto err;
+	return 0;
+}
 
-		/* create the krings of the other end */
-		error = netmap_krings_create(ona, 0);
-		if (error)
-			goto del_krings1;
-
-		/* cross link the krings */
-		for_rx_tx(t) {
-			enum txrx r = nm_txrx_swap(t); /* swap NR_TX <-> NR_RX */
-			for (i = 0; i < nma_get_nrings(na, t); i++) {
-				NMR(na, t)[i].pipe = NMR(ona, r) + i;
-				NMR(ona, r)[i].pipe = NMR(na, t) + i;
+int
+netmap_pipe_reg_both(struct netmap_adapter *na, struct netmap_adapter *ona)
+{
+	int i, error = 0;
+	enum txrx t;
+
+	for_rx_tx(t) {
+		for (i = 0; i < nma_get_nrings(na, t); i++) {
+			struct netmap_kring *kring = NMR(na, t)[i];
+
+			if (nm_kring_pending_on(kring)) {
+				/* mark the peer ring as needed */
+				kring->pipe->nr_kflags |= NKR_NEEDRING;
 			}
 		}
+	}
+
+	/* create all missing needed rings on the other end.
+	 * Either our end, or the other, has been marked as
+	 * fake, so the allocation will not be done twice.
+	 */
+	error = netmap_mem_rings_create(ona);
+	if (error)
+		return error;
+
+	/* In case of no error we put our rings in netmap mode */
+	for_rx_tx(t) {
+		for (i = 0; i < nma_get_nrings(na, t); i++) {
+			struct netmap_kring *kring = NMR(na, t)[i];
+			if (nm_kring_pending_on(kring)) {
+
+				kring->nr_mode = NKR_NETMAP_ON;
+				if ((kring->nr_kflags & NKR_FAKERING) &&
+				    (kring->pipe->nr_kflags & NKR_FAKERING)) {
+					/* this is a re-open of a pipe
+					 * end-point kept alive by the other end.
+					 * We need to leave everything as it is
+					 */
+					continue;
+				}
 
+				/* copy the buffers from the non-fake ring
+				 * (this also propagates any initial offset)
+				 */
+				memcpy(kring->pipe->ring->slot,
+				       kring->ring->slot,
+				       sizeof(struct netmap_slot) *
+						kring->nkr_num_slots);
+				/* copy the offset-related fields */
+				*(uint64_t *)(uintptr_t)&kring->pipe->ring->offset_mask =
+					kring->ring->offset_mask;
+				*(uint64_t *)(uintptr_t)&kring->pipe->ring->buf_align =
+					kring->ring->buf_align;
+				/* mark both rings as fake and needed,
+				 * so that buffers will not be
+				 * deleted by the standard machinery
+				 * (we will delete them by ourselves in
+				 * netmap_pipe_krings_delete)
+				 */
+				kring->nr_kflags |=
+					(NKR_FAKERING | NKR_NEEDRING);
+				kring->nr_mode = NKR_NETMAP_ON;
+			}
+		}
 	}
-	return 0;
 
-del_krings1:
-	netmap_krings_delete(na);
-err:
-	return error;
+	return 0;
 }
 
 /* netmap_pipe_reg.
@@ -376,7 +486,7 @@ netmap_pipe_krings_create(struct netmap_adapter *na)
  *         usr1 --> e1     e2 <-- usr2
  *
  *       and we are either e1 or e2. Add a ref from the
- *       other end and hide our rings.
+ *       other end.
  */
 static int
 netmap_pipe_reg(struct netmap_adapter *na, int onoff)
@@ -384,82 +494,105 @@ netmap_pipe_reg(struct netmap_adapter *na, int onoff)
 	struct netmap_pipe_adapter *pna =
 		(struct netmap_pipe_adapter *)na;
 	struct netmap_adapter *ona = &pna->peer->up;
-	int i, error = 0;
-	enum txrx t;
+	int error = 0;
 
-	ND("%p: onoff %d", na, onoff);
+	nm_prdis("%p: onoff %d", na, onoff);
 	if (onoff) {
-		for_rx_tx(t) {
-			for (i = 0; i < nma_get_nrings(na, t); i++) {
-				struct netmap_kring *kring = &NMR(na, t)[i];
-
-				if (nm_kring_pending_on(kring)) {
-					/* mark the peer ring as needed */
-					kring->pipe->nr_kflags |= NKR_NEEDRING;
-				}
-			}
-		}
-
-		/* create all missing needed rings on the other end */
-		error = netmap_mem_rings_create(ona);
-		if (error)
+		error = netmap_pipe_reg_both(na, ona);
+		if (error) {
 			return error;
-
-		/* In case of no error we put our rings in netmap mode */
-		for_rx_tx(t) {
-			for (i = 0; i < nma_get_nrings(na, t) + 1; i++) {
-				struct netmap_kring *kring = &NMR(na, t)[i];
-
-				if (nm_kring_pending_on(kring)) {
-					kring->nr_mode = NKR_NETMAP_ON;
-				}
-			}
 		}
 		if (na->active_fds == 0)
 			na->na_flags |= NAF_NETMAP_ON;
 	} else {
 		if (na->active_fds == 0)
 			na->na_flags &= ~NAF_NETMAP_ON;
-		for_rx_tx(t) {
-			for (i = 0; i < nma_get_nrings(na, t) + 1; i++) {
-				struct netmap_kring *kring = &NMR(na, t)[i];
-
-				if (nm_kring_pending_off(kring)) {
-					kring->nr_mode = NKR_NETMAP_OFF;
-					/* mark the peer ring as no longer needed by us
-					 * (it may still be kept if sombody else is using it)
-					 */
-					if (kring->pipe) {
-						kring->pipe->nr_kflags &= ~NKR_NEEDRING;
-					}
-				}
-			}
-		}
-		/* delete all the peer rings that are no longer needed */
-		netmap_mem_rings_delete(ona);
+		netmap_krings_mode_commit(na, onoff);
 	}
 
 	if (na->active_fds) {
-		ND("active_fds %d", na->active_fds);
+		nm_prdis("active_fds %d", na->active_fds);
 		return 0;
 	}
 
 	if (pna->peer_ref) {
-		ND("%p: case 1.a or 2.a, nothing to do", na);
+		nm_prdis("%p: case 1.a or 2.a, nothing to do", na);
 		return 0;
 	}
 	if (onoff) {
-		ND("%p: case 1.b, drop peer", na);
+		nm_prdis("%p: case 1.b, drop peer", na);
 		pna->peer->peer_ref = 0;
 		netmap_adapter_put(na);
 	} else {
-		ND("%p: case 2.b, grab peer", na);
+		nm_prdis("%p: case 2.b, grab peer", na);
 		netmap_adapter_get(na);
 		pna->peer->peer_ref = 1;
 	}
 	return error;
 }
 
+void
+netmap_pipe_krings_delete_both(struct netmap_adapter *na,
+			       struct netmap_adapter *ona)
+{
+	struct netmap_adapter *sna;
+	enum txrx t;
+	int i;
+
+	/* case 1) below */
+	nm_prdis("%p: case 1, deleting everything", na);
+	/* To avoid double-frees we zero-out all the buffers in the kernel part
+	 * of each ring. The reason is this: If the user is behaving correctly,
+	 * all buffers are found in exactly one slot in the userspace part of
+	 * some ring.  If the user is not behaving correctly, we cannot release
+	 * buffers cleanly anyway. In the latter case, the allocator will
+	 * return to a clean state only when all its users will close.
+	 */
+	sna = na;
+cleanup:
+	for_rx_tx(t) {
+		for (i = 0; i < nma_get_nrings(sna, t); i++) {
+			struct netmap_kring *kring = NMR(sna, t)[i];
+			struct netmap_ring *ring = kring->ring;
+			uint32_t j, lim = kring->nkr_num_slots - 1;
+
+			nm_prdis("%s ring %p hwtail %u hwcur %u",
+				kring->name, ring, kring->nr_hwtail, kring->nr_hwcur);
+
+			if (ring == NULL)
+				continue;
+
+			if (kring->tx == NR_RX)
+				ring->slot[kring->pipe_tail].buf_idx = 0;
+
+			for (j = nm_next(kring->pipe_tail, lim);
+			     j != kring->nr_hwcur;
+			     j = nm_next(j, lim))
+			{
+				nm_prdis("%s[%d] %u", kring->name, j, ring->slot[j].buf_idx);
+				ring->slot[j].buf_idx = 0;
+			}
+			kring->nr_kflags &= ~(NKR_FAKERING | NKR_NEEDRING);
+		}
+
+	}
+	if (sna != ona && ona->tx_rings) {
+		sna = ona;
+		goto cleanup;
+	}
+
+	netmap_mem_rings_delete(na);
+	netmap_krings_delete(na); /* also zeroes tx_rings etc. */
+
+	if (ona->tx_rings == NULL) {
+		/* already deleted, we must be on an
+		 * cleanup-after-error path */
+		return;
+	}
+	netmap_mem_rings_delete(ona);
+	netmap_krings_delete(ona);
+}
+
 /* netmap_pipe_krings_delete.
  *
  * There are two cases:
@@ -478,8 +611,7 @@ netmap_pipe_reg(struct netmap_adapter *na, int onoff)
  *    and we are either e1 or e2.
  *
  * In the former case we have to also delete the krings of e2;
- * in the latter case we do nothing (note that our krings
- * have already been hidden in the unregister callback).
+ * in the latter case we do nothing.
  */
 static void
 netmap_pipe_krings_delete(struct netmap_adapter *na)
@@ -489,19 +621,11 @@ netmap_pipe_krings_delete(struct netmap_adapter *na)
 	struct netmap_adapter *ona; /* na of the other end */
 
 	if (!pna->peer_ref) {
-		ND("%p: case 2, kept alive by peer",  na);
+		nm_prdis("%p: case 2, kept alive by peer",  na);
 		return;
 	}
-	/* case 1) above */
-	ND("%p: case 1, deleting everything", na);
-	netmap_krings_delete(na); /* also zeroes tx_rings etc. */
 	ona = &pna->peer->up;
-	if (ona->tx_rings == NULL) {
-		/* already deleted, we must be on an
-                 * cleanup-after-error path */
-		return;
-	}
-	netmap_krings_delete(ona);
+	netmap_pipe_krings_delete_both(na, ona);
 }
 
 
@@ -510,13 +634,13 @@ netmap_pipe_dtor(struct netmap_adapter *na)
 {
 	struct netmap_pipe_adapter *pna =
 		(struct netmap_pipe_adapter *)na;
-	ND("%p %p", na, pna->parent_ifp);
+	nm_prdis("%p %p", na, pna->parent_ifp);
 	if (pna->peer_ref) {
-		ND("%p: clean up peer", na);
+		nm_prdis("%p: clean up peer", na);
 		pna->peer_ref = 0;
 		netmap_adapter_put(&pna->peer->up);
 	}
-	if (pna->role == NR_REG_PIPE_MASTER)
+	if (pna->role == NM_PIPE_ROLE_MASTER)
 		netmap_pipe_remove(pna->parent, pna);
 	if (pna->parent_ifp)
 		if_rele(pna->parent_ifp);
@@ -525,95 +649,116 @@ netmap_pipe_dtor(struct netmap_adapter *na)
 }
 
 int
-netmap_get_pipe_na(struct nmreq *nmr, struct netmap_adapter **na,
+netmap_get_pipe_na(struct nmreq_header *hdr, struct netmap_adapter **na,
 		struct netmap_mem_d *nmd, int create)
 {
-	struct nmreq pnmr;
+	struct nmreq_register *req = (struct nmreq_register *)(uintptr_t)hdr->nr_body;
 	struct netmap_adapter *pna; /* parent adapter */
-	struct netmap_pipe_adapter *mna, *sna, *req;
-	struct ifnet *ifp = NULL;
-	u_int pipe_id;
-	int role = nmr->nr_flags & NR_REG_MASK;
+	struct netmap_pipe_adapter *mna, *sna, *reqna;
+	if_t ifp = NULL;
+	const char *pipe_id = NULL;
+	int role = 0;
 	int error, retries = 0;
+	char *cbra, pipe_char;
 
-	ND("flags %x", nmr->nr_flags);
+	/* Try to parse the pipe syntax 'xx{yy' or 'xx}yy'. */
+	cbra = strrchr(hdr->nr_name, '{');
+	if (cbra != NULL) {
+		role = NM_PIPE_ROLE_MASTER;
+	} else {
+		cbra = strrchr(hdr->nr_name, '}');
+		if (cbra != NULL) {
+			role = NM_PIPE_ROLE_SLAVE;
+		} else {
+			nm_prdis("not a pipe");
+			return 0;
+		}
+	}
+	pipe_char = *cbra;
+	pipe_id = cbra + 1;
+	if (*pipe_id == '\0' || cbra == hdr->nr_name) {
+		/* Bracket is the last character, so pipe name is missing;
+		 * or bracket is the first character, so base port name
+		 * is missing. */
+		return EINVAL;
+	}
 
-	if (role != NR_REG_PIPE_MASTER && role != NR_REG_PIPE_SLAVE) {
-		ND("not a pipe");
-		return 0;
+	if (req->nr_mode != NR_REG_ALL_NIC && req->nr_mode != NR_REG_ONE_NIC) {
+		/* We only accept modes involving hardware rings. */
+		return EINVAL;
 	}
-	role = nmr->nr_flags & NR_REG_MASK;
 
 	/* first, try to find the parent adapter */
-	bzero(&pnmr, sizeof(pnmr));
-	memcpy(&pnmr.nr_name, nmr->nr_name, IFNAMSIZ);
-	/* pass to parent the requested number of pipes */
-	pnmr.nr_arg1 = nmr->nr_arg1;
 	for (;;) {
 		int create_error;
 
-		error = netmap_get_na(&pnmr, &pna, &ifp, nmd, create);
+		/* Temporarily remove the pipe suffix. */
+		*cbra = '\0';
+		error = netmap_get_na(hdr, &pna, &ifp, nmd, create);
+		/* Restore the pipe suffix. */
+		*cbra = pipe_char;
 		if (!error)
 			break;
 		if (error != ENXIO || retries++) {
-			ND("parent lookup failed: %d", error);
+			nm_prdis("parent lookup failed: %d", error);
 			return error;
 		}
-		ND("try to create a persistent vale port");
+		nm_prdis("try to create a persistent vale port");
 		/* create a persistent vale port and try again */
+		*cbra = '\0';
 		NMG_UNLOCK();
-		create_error = netmap_vi_create(&pnmr, 1 /* autodelete */);
+		create_error = netmap_vi_create(hdr, 1 /* autodelete */);
 		NMG_LOCK();
+		*cbra = pipe_char;
 		if (create_error && create_error != EEXIST) {
 			if (create_error != EOPNOTSUPP) {
-				D("failed to create a persistent vale port: %d", create_error);
+				nm_prerr("failed to create a persistent vale port: %d",
+				    create_error);
 			}
 			return error;
 		}
 	}
 
 	if (NETMAP_OWNED_BY_KERN(pna)) {
-		ND("parent busy");
+		nm_prdis("parent busy");
 		error = EBUSY;
 		goto put_out;
 	}
 
 	/* next, lookup the pipe id in the parent list */
-	req = NULL;
-	pipe_id = nmr->nr_ringid & NETMAP_RING_MASK;
+	reqna = NULL;
 	mna = netmap_pipe_find(pna, pipe_id);
 	if (mna) {
 		if (mna->role == role) {
-			ND("found %d directly at %d", pipe_id, mna->parent_slot);
-			req = mna;
+			nm_prdis("found %s directly at %d", pipe_id, mna->parent_slot);
+			reqna = mna;
 		} else {
-			ND("found %d indirectly at %d", pipe_id, mna->parent_slot);
-			req = mna->peer;
+			nm_prdis("found %s indirectly at %d", pipe_id, mna->parent_slot);
+			reqna = mna->peer;
 		}
 		/* the pipe we have found already holds a ref to the parent,
-                 * so we need to drop the one we got from netmap_get_na()
-                 */
+		 * so we need to drop the one we got from netmap_get_na()
+		 */
 		netmap_unget_na(pna, ifp);
 		goto found;
 	}
-	ND("pipe %d not found, create %d", pipe_id, create);
+	nm_prdis("pipe %s not found, create %d", pipe_id, create);
 	if (!create) {
 		error = ENODEV;
 		goto put_out;
 	}
 	/* we create both master and slave.
-         * The endpoint we were asked for holds a reference to
-         * the other one.
-         */
+	 * The endpoint we were asked for holds a reference to
+	 * the other one.
+	 */
 	mna = nm_os_malloc(sizeof(*mna));
 	if (mna == NULL) {
 		error = ENOMEM;
 		goto put_out;
 	}
-	snprintf(mna->up.name, sizeof(mna->up.name), "%s{%d", pna->name, pipe_id);
+	snprintf(mna->up.name, sizeof(mna->up.name), "%s{%s", pna->name, pipe_id);
 
-	mna->id = pipe_id;
-	mna->role = NR_REG_PIPE_MASTER;
+	mna->role = NM_PIPE_ROLE_MASTER;
 	mna->parent = pna;
 	mna->parent_ifp = ifp;
 
@@ -624,15 +769,19 @@ netmap_get_pipe_na(struct nmreq *nmr, struct netmap_adapter **na,
 	mna->up.nm_krings_create = netmap_pipe_krings_create;
 	mna->up.nm_krings_delete = netmap_pipe_krings_delete;
 	mna->up.nm_mem = netmap_mem_get(pna->nm_mem);
-	mna->up.na_flags |= NAF_MEM_OWNER;
+	mna->up.na_flags |= NAF_MEM_OWNER | NAF_OFFSETS;
 	mna->up.na_lut = pna->na_lut;
 
-	mna->up.num_tx_rings = 1;
-	mna->up.num_rx_rings = 1;
-	mna->up.num_tx_desc = nmr->nr_tx_slots;
+	mna->up.num_tx_rings = req->nr_tx_rings;
+	nm_bound_var(&mna->up.num_tx_rings, 1,
+			1, NM_PIPE_MAXRINGS, NULL);
+	mna->up.num_rx_rings = req->nr_rx_rings;
+	nm_bound_var(&mna->up.num_rx_rings, 1,
+			1, NM_PIPE_MAXRINGS, NULL);
+	mna->up.num_tx_desc = req->nr_tx_slots;
 	nm_bound_var(&mna->up.num_tx_desc, pna->num_tx_desc,
 			1, NM_PIPE_MAXSLOTS, NULL);
-	mna->up.num_rx_desc = nmr->nr_rx_slots;
+	mna->up.num_rx_desc = req->nr_rx_slots;
 	nm_bound_var(&mna->up.num_rx_desc, pna->num_rx_desc,
 			1, NM_PIPE_MAXSLOTS, NULL);
 	error = netmap_attach_common(&mna->up);
@@ -652,8 +801,13 @@ netmap_get_pipe_na(struct nmreq *nmr, struct netmap_adapter **na,
 	/* most fields are the same, copy from master and then fix */
 	*sna = *mna;
 	sna->up.nm_mem = netmap_mem_get(mna->up.nm_mem);
-	snprintf(sna->up.name, sizeof(sna->up.name), "%s}%d", pna->name, pipe_id);
-	sna->role = NR_REG_PIPE_SLAVE;
+	/* swap the number of tx/rx rings and slots */
+	sna->up.num_tx_rings = mna->up.num_rx_rings;
+	sna->up.num_tx_desc  = mna->up.num_rx_desc;
+	sna->up.num_rx_rings = mna->up.num_tx_rings;
+	sna->up.num_rx_desc  = mna->up.num_tx_desc;
+	snprintf(sna->up.name, sizeof(sna->up.name), "%s}%s", pna->name, pipe_id);
+	sna->role = NM_PIPE_ROLE_SLAVE;
 	error = netmap_attach_common(&sna->up);
 	if (error)
 		goto free_sna;
@@ -663,33 +817,33 @@ netmap_get_pipe_na(struct nmreq *nmr, struct netmap_adapter **na,
 	sna->peer = mna;
 
 	/* we already have a reference to the parent, but we
-         * need another one for the other endpoint we created
-         */
+	 * need another one for the other endpoint we created
+	 */
 	netmap_adapter_get(pna);
 	/* likewise for the ifp, if any */
 	if (ifp)
 		if_ref(ifp);
 
-	if (role == NR_REG_PIPE_MASTER) {
-		req = mna;
+	if (role == NM_PIPE_ROLE_MASTER) {
+		reqna = mna;
 		mna->peer_ref = 1;
 		netmap_adapter_get(&sna->up);
 	} else {
-		req = sna;
+		reqna = sna;
 		sna->peer_ref = 1;
 		netmap_adapter_get(&mna->up);
 	}
-	ND("created master %p and slave %p", mna, sna);
+	nm_prdis("created master %p and slave %p", mna, sna);
 found:
 
-	ND("pipe %d %s at %p", pipe_id,
-		(req->role == NR_REG_PIPE_MASTER ? "master" : "slave"), req);
-	*na = &req->up;
+	nm_prdis("pipe %s %s at %p", pipe_id,
+		(reqna->role == NM_PIPE_ROLE_MASTER ? "master" : "slave"), reqna);
+	*na = &reqna->up;
 	netmap_adapter_get(*na);
 
 	/* keep the reference to the parent.
-         * It will be released by the req destructor
-         */
+	 * It will be released by the req destructor
+	 */
 
 	return 0;
 
diff --git a/sys/dev/netmap/netmap_pt.c b/sys/dev/netmap/netmap_pt.c
deleted file mode 100644
index 8b5c7edfa..000000000
--- a/sys/dev/netmap/netmap_pt.c
+++ /dev/null
@@ -1,1453 +0,0 @@
-/*
- * Copyright (C) 2015 Stefano Garzarella
- * Copyright (C) 2016 Vincenzo Maffione
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *   1. Redistributions of source code must retain the above copyright
- *      notice, this list of conditions and the following disclaimer.
- *   2. Redistributions in binary form must reproduce the above copyright
- *      notice, this list of conditions and the following disclaimer in the
- *      documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD$
- */
-
-/*
- * common headers
- */
-#if defined(__FreeBSD__)
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-//#define usleep_range(_1, _2)
-#define usleep_range(_1, _2) \
-	pause_sbt("ptnetmap-sleep", SBT_1US * _1, SBT_1US * 1, C_ABSOLUTE)
-
-#elif defined(linux)
-#include 
-#endif
-
-#include 
-#include 
-#include 
-#include 
-
-#ifdef WITH_PTNETMAP_HOST
-
-/* RX cycle without receive any packets */
-#define PTN_RX_DRY_CYCLES_MAX	10
-
-/* Limit Batch TX to half ring.
- * Currently disabled, since it does not manage NS_MOREFRAG, which
- * results in random drops in the VALE txsync. */
-//#define PTN_TX_BATCH_LIM(_n)	((_n >> 1))
-
-//#define BUSY_WAIT
-
-#define NETMAP_PT_DEBUG  /* Enables communication debugging. */
-#ifdef NETMAP_PT_DEBUG
-#define DBG(x) x
-#else
-#define DBG(x)
-#endif
-
-
-#undef RATE
-//#define RATE  /* Enables communication statistics. */
-#ifdef RATE
-#define IFRATE(x) x
-struct rate_batch_stats {
-    unsigned long sync;
-    unsigned long sync_dry;
-    unsigned long pkt;
-};
-
-struct rate_stats {
-    unsigned long gtxk;     /* Guest --> Host Tx kicks. */
-    unsigned long grxk;     /* Guest --> Host Rx kicks. */
-    unsigned long htxk;     /* Host --> Guest Tx kicks. */
-    unsigned long hrxk;     /* Host --> Guest Rx Kicks. */
-    unsigned long btxwu;    /* Backend Tx wake-up. */
-    unsigned long brxwu;    /* Backend Rx wake-up. */
-    struct rate_batch_stats txbs;
-    struct rate_batch_stats rxbs;
-};
-
-struct rate_context {
-    struct timer_list timer;
-    struct rate_stats new;
-    struct rate_stats old;
-};
-
-#define RATE_PERIOD  2
-static void
-rate_callback(unsigned long arg)
-{
-    struct rate_context * ctx = (struct rate_context *)arg;
-    struct rate_stats cur = ctx->new;
-    struct rate_batch_stats *txbs = &cur.txbs;
-    struct rate_batch_stats *rxbs = &cur.rxbs;
-    struct rate_batch_stats *txbs_old = &ctx->old.txbs;
-    struct rate_batch_stats *rxbs_old = &ctx->old.rxbs;
-    uint64_t tx_batch, rx_batch;
-    unsigned long txpkts, rxpkts;
-    unsigned long gtxk, grxk;
-    int r;
-
-    txpkts = txbs->pkt - txbs_old->pkt;
-    rxpkts = rxbs->pkt - rxbs_old->pkt;
-
-    tx_batch = ((txbs->sync - txbs_old->sync) > 0) ?
-	       txpkts / (txbs->sync - txbs_old->sync): 0;
-    rx_batch = ((rxbs->sync - rxbs_old->sync) > 0) ?
-	       rxpkts / (rxbs->sync - rxbs_old->sync): 0;
-
-    /* Fix-up gtxk and grxk estimates. */
-    gtxk = (cur.gtxk - ctx->old.gtxk) - (cur.btxwu - ctx->old.btxwu);
-    grxk = (cur.grxk - ctx->old.grxk) - (cur.brxwu - ctx->old.brxwu);
-
-    printk("txpkts  = %lu Hz\n", txpkts/RATE_PERIOD);
-    printk("gtxk    = %lu Hz\n", gtxk/RATE_PERIOD);
-    printk("htxk    = %lu Hz\n", (cur.htxk - ctx->old.htxk)/RATE_PERIOD);
-    printk("btxw    = %lu Hz\n", (cur.btxwu - ctx->old.btxwu)/RATE_PERIOD);
-    printk("rxpkts  = %lu Hz\n", rxpkts/RATE_PERIOD);
-    printk("grxk    = %lu Hz\n", grxk/RATE_PERIOD);
-    printk("hrxk    = %lu Hz\n", (cur.hrxk - ctx->old.hrxk)/RATE_PERIOD);
-    printk("brxw    = %lu Hz\n", (cur.brxwu - ctx->old.brxwu)/RATE_PERIOD);
-    printk("txbatch = %llu avg\n", tx_batch);
-    printk("rxbatch = %llu avg\n", rx_batch);
-    printk("\n");
-
-    ctx->old = cur;
-    r = mod_timer(&ctx->timer, jiffies +
-            msecs_to_jiffies(RATE_PERIOD * 1000));
-    if (unlikely(r))
-        D("[ptnetmap] Error: mod_timer()\n");
-}
-
-static void
-rate_batch_stats_update(struct rate_batch_stats *bf, uint32_t pre_tail,
-		        uint32_t act_tail, uint32_t num_slots)
-{
-    int n = (int)act_tail - pre_tail;
-
-    if (n) {
-        if (n < 0)
-            n += num_slots;
-
-        bf->sync++;
-        bf->pkt += n;
-    } else {
-        bf->sync_dry++;
-    }
-}
-
-#else /* !RATE */
-#define IFRATE(x)
-#endif /* RATE */
-
-struct ptnetmap_state {
-    /* Kthreads. */
-    struct nm_kthread **kthreads;
-
-    /* Shared memory with the guest (TX/RX) */
-    struct ptnet_ring __user *ptrings;
-
-    bool stopped;
-
-    /* Netmap adapter wrapping the backend. */
-    struct netmap_pt_host_adapter *pth_na;
-
-    IFRATE(struct rate_context rate_ctx;)
-};
-
-static inline void
-ptnetmap_kring_dump(const char *title, const struct netmap_kring *kring)
-{
-    RD(1, "%s - name: %s hwcur: %d hwtail: %d rhead: %d rcur: %d \
-    		    rtail: %d head: %d cur: %d tail: %d",
-            title, kring->name, kring->nr_hwcur,
-            kring->nr_hwtail, kring->rhead, kring->rcur, kring->rtail,
-            kring->ring->head, kring->ring->cur, kring->ring->tail);
-}
-
-/*
- * TX functions to set/get and to handle host/guest kick.
- */
-
-
-/* Enable or disable guest --> host kicks. */
-static inline void
-ptring_kick_enable(struct ptnet_ring __user *ptring, uint32_t val)
-{
-    CSB_WRITE(ptring, host_need_kick, val);
-}
-
-/* Are guest interrupt enabled or disabled? */
-static inline uint32_t
-ptring_intr_enabled(struct ptnet_ring __user *ptring)
-{
-    uint32_t v;
-
-    CSB_READ(ptring, guest_need_kick, v);
-
-    return v;
-}
-
-/* Enable or disable guest interrupts. */
-static inline void
-ptring_intr_enable(struct ptnet_ring __user *ptring, uint32_t val)
-{
-    CSB_WRITE(ptring, guest_need_kick, val);
-}
-
-/* Handle TX events: from the guest or from the backend */
-static void
-ptnetmap_tx_handler(void *data)
-{
-    struct netmap_kring *kring = data;
-    struct netmap_pt_host_adapter *pth_na =
-		(struct netmap_pt_host_adapter *)kring->na->na_private;
-    struct ptnetmap_state *ptns = pth_na->ptns;
-    struct ptnet_ring __user *ptring;
-    struct netmap_ring shadow_ring; /* shadow copy of the netmap_ring */
-    bool more_txspace = false;
-    struct nm_kthread *kth;
-    uint32_t num_slots;
-    int batch;
-    IFRATE(uint32_t pre_tail);
-
-    if (unlikely(!ptns)) {
-        D("ERROR ptnetmap state is NULL");
-        return;
-    }
-
-    if (unlikely(ptns->stopped)) {
-        RD(1, "backend netmap is being stopped");
-        return;
-    }
-
-    if (unlikely(nm_kr_tryget(kring, 1, NULL))) {
-        D("ERROR nm_kr_tryget()");
-        return;
-    }
-
-    /* This is a guess, to be fixed in the rate callback. */
-    IFRATE(ptns->rate_ctx.new.gtxk++);
-
-    /* Get TX ptring pointer from the CSB. */
-    ptring = ptns->ptrings + kring->ring_id;
-    kth = ptns->kthreads[kring->ring_id];
-
-    num_slots = kring->nkr_num_slots;
-    shadow_ring.head = kring->rhead;
-    shadow_ring.cur = kring->rcur;
-
-    /* Disable guest --> host notifications. */
-    ptring_kick_enable(ptring, 0);
-    /* Copy the guest kring pointers from the CSB */
-    ptnetmap_host_read_kring_csb(ptring, &shadow_ring, num_slots);
-
-    for (;;) {
-	/* If guest moves ahead too fast, let's cut the move so
-	 * that we don't exceed our batch limit. */
-        batch = shadow_ring.head - kring->nr_hwcur;
-        if (batch < 0)
-            batch += num_slots;
-
-#ifdef PTN_TX_BATCH_LIM
-        if (batch > PTN_TX_BATCH_LIM(num_slots)) {
-            uint32_t head_lim = kring->nr_hwcur + PTN_TX_BATCH_LIM(num_slots);
-
-            if (head_lim >= num_slots)
-                head_lim -= num_slots;
-            ND(1, "batch: %d head: %d head_lim: %d", batch, shadow_ring.head,
-						     head_lim);
-            shadow_ring.head = head_lim;
-	    batch = PTN_TX_BATCH_LIM(num_slots);
-        }
-#endif /* PTN_TX_BATCH_LIM */
-
-        if (nm_kr_txspace(kring) <= (num_slots >> 1)) {
-            shadow_ring.flags |= NAF_FORCE_RECLAIM;
-        }
-
-        /* Netmap prologue */
-	shadow_ring.tail = kring->rtail;
-        if (unlikely(nm_txsync_prologue(kring, &shadow_ring) >= num_slots)) {
-            /* Reinit ring and enable notifications. */
-            netmap_ring_reinit(kring);
-            ptring_kick_enable(ptring, 1);
-            break;
-        }
-
-        if (unlikely(netmap_verbose & NM_VERB_TXSYNC)) {
-            ptnetmap_kring_dump("pre txsync", kring);
-	}
-
-        IFRATE(pre_tail = kring->rtail);
-        if (unlikely(kring->nm_sync(kring, shadow_ring.flags))) {
-            /* Reenable notifications. */
-            ptring_kick_enable(ptring, 1);
-            D("ERROR txsync()");
-	    break;
-        }
-
-        /*
-         * Finalize
-         * Copy host hwcur and hwtail into the CSB for the guest sync(), and
-	 * do the nm_sync_finalize.
-         */
-        ptnetmap_host_write_kring_csb(ptring, kring->nr_hwcur,
-				      kring->nr_hwtail);
-        if (kring->rtail != kring->nr_hwtail) {
-	    /* Some more room available in the parent adapter. */
-	    kring->rtail = kring->nr_hwtail;
-	    more_txspace = true;
-        }
-
-        IFRATE(rate_batch_stats_update(&ptns->rate_ctx.new.txbs, pre_tail,
-				       kring->rtail, num_slots));
-
-        if (unlikely(netmap_verbose & NM_VERB_TXSYNC)) {
-            ptnetmap_kring_dump("post txsync", kring);
-	}
-
-#ifndef BUSY_WAIT
-        /* Interrupt the guest if needed. */
-        if (more_txspace && ptring_intr_enabled(ptring)) {
-            /* Disable guest kick to avoid sending unnecessary kicks */
-            ptring_intr_enable(ptring, 0);
-            nm_os_kthread_send_irq(kth);
-            IFRATE(ptns->rate_ctx.new.htxk++);
-            more_txspace = false;
-        }
-#endif
-        /* Read CSB to see if there is more work to do. */
-        ptnetmap_host_read_kring_csb(ptring, &shadow_ring, num_slots);
-#ifndef BUSY_WAIT
-        if (shadow_ring.head == kring->rhead) {
-            /*
-             * No more packets to transmit. We enable notifications and
-             * go to sleep, waiting for a kick from the guest when new
-             * new slots are ready for transmission.
-             */
-            usleep_range(1,1);
-            /* Reenable notifications. */
-            ptring_kick_enable(ptring, 1);
-            /* Doublecheck. */
-            ptnetmap_host_read_kring_csb(ptring, &shadow_ring, num_slots);
-            if (shadow_ring.head != kring->rhead) {
-		/* We won the race condition, there are more packets to
-		 * transmit. Disable notifications and do another cycle */
-		ptring_kick_enable(ptring, 0);
-		continue;
-	    }
-	    break;
-        }
-
-	if (nm_kr_txempty(kring)) {
-	    /* No more available TX slots. We stop waiting for a notification
-	     * from the backend (netmap_tx_irq). */
-            ND(1, "TX ring");
-            break;
-        }
-#endif
-        if (unlikely(ptns->stopped)) {
-            D("backend netmap is being stopped");
-            break;
-        }
-    }
-
-    nm_kr_put(kring);
-
-    if (more_txspace && ptring_intr_enabled(ptring)) {
-        ptring_intr_enable(ptring, 0);
-        nm_os_kthread_send_irq(kth);
-        IFRATE(ptns->rate_ctx.new.htxk++);
-    }
-}
-
-/*
- * We need RX kicks from the guest when (tail == head-1), where we wait
- * for the guest to refill.
- */
-#ifndef BUSY_WAIT
-static inline int
-ptnetmap_norxslots(struct netmap_kring *kring, uint32_t g_head)
-{
-    return (NM_ACCESS_ONCE(kring->nr_hwtail) == nm_prev(g_head,
-    			    kring->nkr_num_slots - 1));
-}
-#endif /* !BUSY_WAIT */
-
-/* Handle RX events: from the guest or from the backend */
-static void
-ptnetmap_rx_handler(void *data)
-{
-    struct netmap_kring *kring = data;
-    struct netmap_pt_host_adapter *pth_na =
-		(struct netmap_pt_host_adapter *)kring->na->na_private;
-    struct ptnetmap_state *ptns = pth_na->ptns;
-    struct ptnet_ring __user *ptring;
-    struct netmap_ring shadow_ring; /* shadow copy of the netmap_ring */
-    struct nm_kthread *kth;
-    uint32_t num_slots;
-    int dry_cycles = 0;
-    bool some_recvd = false;
-    IFRATE(uint32_t pre_tail);
-
-    if (unlikely(!ptns || !ptns->pth_na)) {
-        D("ERROR ptnetmap state %p, ptnetmap host adapter %p", ptns,
-	  ptns ? ptns->pth_na : NULL);
-        return;
-    }
-
-    if (unlikely(ptns->stopped)) {
-        RD(1, "backend netmap is being stopped");
-	return;
-    }
-
-    if (unlikely(nm_kr_tryget(kring, 1, NULL))) {
-        D("ERROR nm_kr_tryget()");
-	return;
-    }
-
-    /* This is a guess, to be fixed in the rate callback. */
-    IFRATE(ptns->rate_ctx.new.grxk++);
-
-    /* Get RX ptring pointer from the CSB. */
-    ptring = ptns->ptrings + (pth_na->up.num_tx_rings + kring->ring_id);
-    kth = ptns->kthreads[pth_na->up.num_tx_rings + kring->ring_id];
-
-    num_slots = kring->nkr_num_slots;
-    shadow_ring.head = kring->rhead;
-    shadow_ring.cur = kring->rcur;
-
-    /* Disable notifications. */
-    ptring_kick_enable(ptring, 0);
-    /* Copy the guest kring pointers from the CSB */
-    ptnetmap_host_read_kring_csb(ptring, &shadow_ring, num_slots);
-
-    for (;;) {
-	uint32_t hwtail;
-
-        /* Netmap prologue */
-	shadow_ring.tail = kring->rtail;
-        if (unlikely(nm_rxsync_prologue(kring, &shadow_ring) >= num_slots)) {
-            /* Reinit ring and enable notifications. */
-            netmap_ring_reinit(kring);
-            ptring_kick_enable(ptring, 1);
-            break;
-        }
-
-        if (unlikely(netmap_verbose & NM_VERB_RXSYNC)) {
-            ptnetmap_kring_dump("pre rxsync", kring);
-	}
-
-        IFRATE(pre_tail = kring->rtail);
-        if (unlikely(kring->nm_sync(kring, shadow_ring.flags))) {
-            /* Reenable notifications. */
-            ptring_kick_enable(ptring, 1);
-            D("ERROR rxsync()");
-	    break;
-        }
-        /*
-         * Finalize
-         * Copy host hwcur and hwtail into the CSB for the guest sync()
-         */
-	hwtail = NM_ACCESS_ONCE(kring->nr_hwtail);
-        ptnetmap_host_write_kring_csb(ptring, kring->nr_hwcur, hwtail);
-        if (kring->rtail != hwtail) {
-	    kring->rtail = hwtail;
-            some_recvd = true;
-            dry_cycles = 0;
-        } else {
-            dry_cycles++;
-        }
-
-        IFRATE(rate_batch_stats_update(&ptns->rate_ctx.new.rxbs, pre_tail,
-	                               kring->rtail, num_slots));
-
-        if (unlikely(netmap_verbose & NM_VERB_RXSYNC)) {
-            ptnetmap_kring_dump("post rxsync", kring);
-	}
-
-#ifndef BUSY_WAIT
-	/* Interrupt the guest if needed. */
-        if (some_recvd && ptring_intr_enabled(ptring)) {
-            /* Disable guest kick to avoid sending unnecessary kicks */
-            ptring_intr_enable(ptring, 0);
-            nm_os_kthread_send_irq(kth);
-            IFRATE(ptns->rate_ctx.new.hrxk++);
-            some_recvd = false;
-        }
-#endif
-        /* Read CSB to see if there is more work to do. */
-        ptnetmap_host_read_kring_csb(ptring, &shadow_ring, num_slots);
-#ifndef BUSY_WAIT
-        if (ptnetmap_norxslots(kring, shadow_ring.head)) {
-            /*
-             * No more slots available for reception. We enable notification and
-             * go to sleep, waiting for a kick from the guest when new receive
-	     * slots are available.
-             */
-            usleep_range(1,1);
-            /* Reenable notifications. */
-            ptring_kick_enable(ptring, 1);
-            /* Doublecheck. */
-            ptnetmap_host_read_kring_csb(ptring, &shadow_ring, num_slots);
-            if (!ptnetmap_norxslots(kring, shadow_ring.head)) {
-		/* We won the race condition, more slots are available. Disable
-		 * notifications and do another cycle. */
-                ptring_kick_enable(ptring, 0);
-                continue;
-	    }
-            break;
-        }
-
-	hwtail = NM_ACCESS_ONCE(kring->nr_hwtail);
-        if (unlikely(hwtail == kring->rhead ||
-		     dry_cycles >= PTN_RX_DRY_CYCLES_MAX)) {
-	    /* No more packets to be read from the backend. We stop and
-	     * wait for a notification from the backend (netmap_rx_irq). */
-            ND(1, "nr_hwtail: %d rhead: %d dry_cycles: %d",
-	       hwtail, kring->rhead, dry_cycles);
-            break;
-        }
-#endif
-        if (unlikely(ptns->stopped)) {
-            D("backend netmap is being stopped");
-            break;
-        }
-    }
-
-    nm_kr_put(kring);
-
-    /* Interrupt the guest if needed. */
-    if (some_recvd && ptring_intr_enabled(ptring)) {
-        ptring_intr_enable(ptring, 0);
-        nm_os_kthread_send_irq(kth);
-        IFRATE(ptns->rate_ctx.new.hrxk++);
-    }
-}
-
-#ifdef NETMAP_PT_DEBUG
-static void
-ptnetmap_print_configuration(struct ptnetmap_cfg *cfg)
-{
-	int k;
-
-	D("ptnetmap configuration:");
-	D("  CSB ptrings @%p, num_rings=%u, cfgtype %08x", cfg->ptrings,
-	  cfg->num_rings, cfg->cfgtype);
-	for (k = 0; k < cfg->num_rings; k++) {
-		switch (cfg->cfgtype) {
-		case PTNETMAP_CFGTYPE_QEMU: {
-			struct ptnetmap_cfgentry_qemu *e =
-				(struct ptnetmap_cfgentry_qemu *)(cfg+1) + k;
-			D("    ring #%d: ioeventfd=%lu, irqfd=%lu", k,
-				(unsigned long)e->ioeventfd,
-				(unsigned long)e->irqfd);
-			break;
-		}
-
-		case PTNETMAP_CFGTYPE_BHYVE:
-		{
-			struct ptnetmap_cfgentry_bhyve *e =
-				(struct ptnetmap_cfgentry_bhyve *)(cfg+1) + k;
-			D("    ring #%d: wchan=%lu, ioctl_fd=%lu, "
-			  "ioctl_cmd=%lu, msix_msg_data=%lu, msix_addr=%lu",
-				k, (unsigned long)e->wchan,
-				(unsigned long)e->ioctl_fd,
-				(unsigned long)e->ioctl_cmd,
-				(unsigned long)e->ioctl_data.msg_data,
-				(unsigned long)e->ioctl_data.addr);
-			break;
-		}
-		}
-	}
-
-}
-#endif /* NETMAP_PT_DEBUG */
-
-/* Copy actual state of the host ring into the CSB for the guest init */
-static int
-ptnetmap_kring_snapshot(struct netmap_kring *kring, struct ptnet_ring __user *ptring)
-{
-    if(CSB_WRITE(ptring, head, kring->rhead))
-        goto err;
-    if(CSB_WRITE(ptring, cur, kring->rcur))
-        goto err;
-
-    if(CSB_WRITE(ptring, hwcur, kring->nr_hwcur))
-        goto err;
-    if(CSB_WRITE(ptring, hwtail, NM_ACCESS_ONCE(kring->nr_hwtail)))
-        goto err;
-
-    DBG(ptnetmap_kring_dump("ptnetmap_kring_snapshot", kring);)
-
-    return 0;
-err:
-    return EFAULT;
-}
-
-static struct netmap_kring *
-ptnetmap_kring(struct netmap_pt_host_adapter *pth_na, int k)
-{
-	if (k < pth_na->up.num_tx_rings) {
-		return pth_na->up.tx_rings + k;
-	}
-	return pth_na->up.rx_rings + k - pth_na->up.num_tx_rings;
-}
-
-static int
-ptnetmap_krings_snapshot(struct netmap_pt_host_adapter *pth_na)
-{
-	struct ptnetmap_state *ptns = pth_na->ptns;
-	struct netmap_kring *kring;
-	unsigned int num_rings;
-	int err = 0, k;
-
-	num_rings = pth_na->up.num_tx_rings +
-		    pth_na->up.num_rx_rings;
-
-	for (k = 0; k < num_rings; k++) {
-		kring = ptnetmap_kring(pth_na, k);
-		err |= ptnetmap_kring_snapshot(kring, ptns->ptrings + k);
-	}
-
-	return err;
-}
-
-/*
- * Functions to create, start and stop the kthreads
- */
-
-static int
-ptnetmap_create_kthreads(struct netmap_pt_host_adapter *pth_na,
-			 struct ptnetmap_cfg *cfg)
-{
-	struct ptnetmap_state *ptns = pth_na->ptns;
-	struct nm_kthread_cfg nmk_cfg;
-	unsigned int num_rings;
-	uint8_t *cfg_entries = (uint8_t *)(cfg + 1);
-	int k;
-
-	num_rings = pth_na->up.num_tx_rings +
-		    pth_na->up.num_rx_rings;
-
-	for (k = 0; k < num_rings; k++) {
-		nmk_cfg.attach_user = 1; /* attach kthread to user process */
-		nmk_cfg.worker_private = ptnetmap_kring(pth_na, k);
-		nmk_cfg.type = k;
-		if (k < pth_na->up.num_tx_rings) {
-			nmk_cfg.worker_fn = ptnetmap_tx_handler;
-		} else {
-			nmk_cfg.worker_fn = ptnetmap_rx_handler;
-		}
-
-		ptns->kthreads[k] = nm_os_kthread_create(&nmk_cfg,
-			cfg->cfgtype, cfg_entries + k * cfg->entry_size);
-		if (ptns->kthreads[k] == NULL) {
-			goto err;
-		}
-	}
-
-	return 0;
-err:
-	for (k = 0; k < num_rings; k++) {
-		if (ptns->kthreads[k]) {
-			nm_os_kthread_delete(ptns->kthreads[k]);
-			ptns->kthreads[k] = NULL;
-		}
-	}
-	return EFAULT;
-}
-
-static int
-ptnetmap_start_kthreads(struct netmap_pt_host_adapter *pth_na)
-{
-	struct ptnetmap_state *ptns = pth_na->ptns;
-	int num_rings;
-	int error;
-	int k;
-
-	if (!ptns) {
-		D("BUG ptns is NULL");
-		return EFAULT;
-	}
-
-	ptns->stopped = false;
-
-	num_rings = ptns->pth_na->up.num_tx_rings +
-		    ptns->pth_na->up.num_rx_rings;
-	for (k = 0; k < num_rings; k++) {
-		//nm_os_kthread_set_affinity(ptns->kthreads[k], xxx);
-		error = nm_os_kthread_start(ptns->kthreads[k]);
-		if (error) {
-			return error;
-		}
-	}
-
-	return 0;
-}
-
-static void
-ptnetmap_stop_kthreads(struct netmap_pt_host_adapter *pth_na)
-{
-	struct ptnetmap_state *ptns = pth_na->ptns;
-	int num_rings;
-	int k;
-
-	if (!ptns) {
-		/* Nothing to do. */
-		return;
-	}
-
-	ptns->stopped = true;
-
-	num_rings = ptns->pth_na->up.num_tx_rings +
-		    ptns->pth_na->up.num_rx_rings;
-	for (k = 0; k < num_rings; k++) {
-		nm_os_kthread_stop(ptns->kthreads[k]);
-	}
-}
-
-static struct ptnetmap_cfg *
-ptnetmap_read_cfg(struct nmreq *nmr)
-{
-	uintptr_t *nmr_ptncfg = (uintptr_t *)&nmr->nr_arg1;
-	struct ptnetmap_cfg *cfg;
-	struct ptnetmap_cfg tmp;
-	size_t cfglen;
-
-	if (copyin((const void *)*nmr_ptncfg, &tmp, sizeof(tmp))) {
-		D("Partial copyin() failed");
-		return NULL;
-	}
-
-	cfglen = sizeof(tmp) + tmp.num_rings * tmp.entry_size;
-	cfg = nm_os_malloc(cfglen);
-	if (!cfg) {
-		return NULL;
-	}
-
-	if (copyin((const void *)*nmr_ptncfg, cfg, cfglen)) {
-		D("Full copyin() failed");
-		nm_os_free(cfg);
-		return NULL;
-	}
-
-	return cfg;
-}
-
-static int nm_unused_notify(struct netmap_kring *, int);
-static int nm_pt_host_notify(struct netmap_kring *, int);
-
-/* Create ptnetmap state and switch parent adapter to ptnetmap mode. */
-static int
-ptnetmap_create(struct netmap_pt_host_adapter *pth_na,
-		struct ptnetmap_cfg *cfg)
-{
-    struct ptnetmap_state *ptns;
-    unsigned int num_rings;
-    int ret, i;
-
-    /* Check if ptnetmap state is already there. */
-    if (pth_na->ptns) {
-        D("ERROR adapter %p already in ptnetmap mode", pth_na->parent);
-        return EINVAL;
-    }
-
-    num_rings = pth_na->up.num_tx_rings + pth_na->up.num_rx_rings;
-
-    if (num_rings != cfg->num_rings) {
-        D("ERROR configuration mismatch, expected %u rings, found %u",
-           num_rings, cfg->num_rings);
-        return EINVAL;
-    }
-
-    ptns = nm_os_malloc(sizeof(*ptns) + num_rings * sizeof(*ptns->kthreads));
-    if (!ptns) {
-        return ENOMEM;
-    }
-
-    ptns->kthreads = (struct nm_kthread **)(ptns + 1);
-    ptns->stopped = true;
-
-    /* Cross-link data structures. */
-    pth_na->ptns = ptns;
-    ptns->pth_na = pth_na;
-
-    /* Store the CSB address provided by the hypervisor. */
-    ptns->ptrings = cfg->ptrings;
-
-    DBG(ptnetmap_print_configuration(cfg));
-
-    /* Create kthreads */
-    if ((ret = ptnetmap_create_kthreads(pth_na, cfg))) {
-        D("ERROR ptnetmap_create_kthreads()");
-        goto err;
-    }
-    /* Copy krings state into the CSB for the guest initialization */
-    if ((ret = ptnetmap_krings_snapshot(pth_na))) {
-        D("ERROR ptnetmap_krings_snapshot()");
-        goto err;
-    }
-
-    /* Overwrite parent nm_notify krings callback. */
-    pth_na->parent->na_private = pth_na;
-    pth_na->parent_nm_notify = pth_na->parent->nm_notify;
-    pth_na->parent->nm_notify = nm_unused_notify;
-
-    for (i = 0; i < pth_na->parent->num_rx_rings; i++) {
-        pth_na->up.rx_rings[i].save_notify =
-        	pth_na->up.rx_rings[i].nm_notify;
-        pth_na->up.rx_rings[i].nm_notify = nm_pt_host_notify;
-    }
-    for (i = 0; i < pth_na->parent->num_tx_rings; i++) {
-        pth_na->up.tx_rings[i].save_notify =
-        	pth_na->up.tx_rings[i].nm_notify;
-        pth_na->up.tx_rings[i].nm_notify = nm_pt_host_notify;
-    }
-
-#ifdef RATE
-    memset(&ptns->rate_ctx, 0, sizeof(ptns->rate_ctx));
-    setup_timer(&ptns->rate_ctx.timer, &rate_callback,
-            (unsigned long)&ptns->rate_ctx);
-    if (mod_timer(&ptns->rate_ctx.timer, jiffies + msecs_to_jiffies(1500)))
-        D("[ptn] Error: mod_timer()\n");
-#endif
-
-    DBG(D("[%s] ptnetmap configuration DONE", pth_na->up.name));
-
-    return 0;
-
-err:
-    pth_na->ptns = NULL;
-    nm_os_free(ptns);
-    return ret;
-}
-
-/* Switch parent adapter back to normal mode and destroy
- * ptnetmap state. */
-static void
-ptnetmap_delete(struct netmap_pt_host_adapter *pth_na)
-{
-    struct ptnetmap_state *ptns = pth_na->ptns;
-    int num_rings;
-    int i;
-
-    if (!ptns) {
-	/* Nothing to do. */
-        return;
-    }
-
-    /* Restore parent adapter callbacks. */
-    pth_na->parent->nm_notify = pth_na->parent_nm_notify;
-    pth_na->parent->na_private = NULL;
-
-    for (i = 0; i < pth_na->parent->num_rx_rings; i++) {
-        pth_na->up.rx_rings[i].nm_notify =
-        	pth_na->up.rx_rings[i].save_notify;
-        pth_na->up.rx_rings[i].save_notify = NULL;
-    }
-    for (i = 0; i < pth_na->parent->num_tx_rings; i++) {
-        pth_na->up.tx_rings[i].nm_notify =
-        	pth_na->up.tx_rings[i].save_notify;
-        pth_na->up.tx_rings[i].save_notify = NULL;
-    }
-
-    /* Delete kthreads. */
-    num_rings = ptns->pth_na->up.num_tx_rings +
-                ptns->pth_na->up.num_rx_rings;
-    for (i = 0; i < num_rings; i++) {
-        nm_os_kthread_delete(ptns->kthreads[i]);
-	ptns->kthreads[i] = NULL;
-    }
-
-    IFRATE(del_timer(&ptns->rate_ctx.timer));
-
-    nm_os_free(ptns);
-
-    pth_na->ptns = NULL;
-
-    DBG(D("[%s] ptnetmap deleted", pth_na->up.name));
-}
-
-/*
- * Called by netmap_ioctl().
- * Operation is indicated in nmr->nr_cmd.
- *
- * Called without NMG_LOCK.
- */
-int
-ptnetmap_ctl(struct nmreq *nmr, struct netmap_adapter *na)
-{
-    struct netmap_pt_host_adapter *pth_na;
-    struct ptnetmap_cfg *cfg;
-    char *name;
-    int cmd, error = 0;
-
-    name = nmr->nr_name;
-    cmd = nmr->nr_cmd;
-
-    DBG(D("name: %s", name));
-
-    if (!nm_ptnetmap_host_on(na)) {
-        D("ERROR Netmap adapter %p is not a ptnetmap host adapter", na);
-        error = ENXIO;
-        goto done;
-    }
-    pth_na = (struct netmap_pt_host_adapter *)na;
-
-    NMG_LOCK();
-    switch (cmd) {
-    case NETMAP_PT_HOST_CREATE:
-	/* Read hypervisor configuration from userspace. */
-        cfg = ptnetmap_read_cfg(nmr);
-        if (!cfg)
-            break;
-        /* Create ptnetmap state (kthreads, ...) and switch parent
-	 * adapter to ptnetmap mode. */
-        error = ptnetmap_create(pth_na, cfg);
-	nm_os_free(cfg);
-        if (error)
-            break;
-        /* Start kthreads. */
-        error = ptnetmap_start_kthreads(pth_na);
-        if (error)
-            ptnetmap_delete(pth_na);
-        break;
-
-    case NETMAP_PT_HOST_DELETE:
-        /* Stop kthreads. */
-        ptnetmap_stop_kthreads(pth_na);
-        /* Switch parent adapter back to normal mode and destroy
-	 * ptnetmap state (kthreads, ...). */
-        ptnetmap_delete(pth_na);
-        break;
-
-    default:
-        D("ERROR invalid cmd (nmr->nr_cmd) (0x%x)", cmd);
-        error = EINVAL;
-        break;
-    }
-    NMG_UNLOCK();
-
-done:
-    return error;
-}
-
-/* nm_notify callbacks for ptnetmap */
-static int
-nm_pt_host_notify(struct netmap_kring *kring, int flags)
-{
-	struct netmap_adapter *na = kring->na;
-	struct netmap_pt_host_adapter *pth_na =
-		(struct netmap_pt_host_adapter *)na->na_private;
-	struct ptnetmap_state *ptns;
-	int k;
-
-	/* First check that the passthrough port is not being destroyed. */
-	if (unlikely(!pth_na)) {
-		return NM_IRQ_COMPLETED;
-	}
-
-	ptns = pth_na->ptns;
-	if (unlikely(!ptns || ptns->stopped)) {
-		return NM_IRQ_COMPLETED;
-	}
-
-	k = kring->ring_id;
-
-	/* Notify kthreads (wake up if needed) */
-	if (kring->tx == NR_TX) {
-		ND(1, "TX backend irq");
-		IFRATE(ptns->rate_ctx.new.btxwu++);
-	} else {
-		k += pth_na->up.num_tx_rings;
-		ND(1, "RX backend irq");
-		IFRATE(ptns->rate_ctx.new.brxwu++);
-	}
-	nm_os_kthread_wakeup_worker(ptns->kthreads[k]);
-
-	return NM_IRQ_COMPLETED;
-}
-
-static int
-nm_unused_notify(struct netmap_kring *kring, int flags)
-{
-    D("BUG this should never be called");
-    return ENXIO;
-}
-
-/* nm_config callback for bwrap */
-static int
-nm_pt_host_config(struct netmap_adapter *na, u_int *txr, u_int *txd,
-        u_int *rxr, u_int *rxd)
-{
-    struct netmap_pt_host_adapter *pth_na =
-        (struct netmap_pt_host_adapter *)na;
-    struct netmap_adapter *parent = pth_na->parent;
-    int error;
-
-    //XXX: maybe calling parent->nm_config is better
-
-    /* forward the request */
-    error = netmap_update_config(parent);
-
-    *rxr = na->num_rx_rings = parent->num_rx_rings;
-    *txr = na->num_tx_rings = parent->num_tx_rings;
-    *txd = na->num_tx_desc = parent->num_tx_desc;
-    *rxd = na->num_rx_desc = parent->num_rx_desc;
-
-    DBG(D("rxr: %d txr: %d txd: %d rxd: %d", *rxr, *txr, *txd, *rxd));
-
-    return error;
-}
-
-/* nm_krings_create callback for ptnetmap */
-static int
-nm_pt_host_krings_create(struct netmap_adapter *na)
-{
-    struct netmap_pt_host_adapter *pth_na =
-        (struct netmap_pt_host_adapter *)na;
-    struct netmap_adapter *parent = pth_na->parent;
-    enum txrx t;
-    int error;
-
-    DBG(D("%s", pth_na->up.name));
-
-    /* create the parent krings */
-    error = parent->nm_krings_create(parent);
-    if (error) {
-        return error;
-    }
-
-    /* A ptnetmap host adapter points the very same krings
-     * as its parent adapter. These pointer are used in the
-     * TX/RX worker functions. */
-    na->tx_rings = parent->tx_rings;
-    na->rx_rings = parent->rx_rings;
-    na->tailroom = parent->tailroom;
-
-    for_rx_tx(t) {
-	struct netmap_kring *kring;
-
-	/* Parent's kring_create function will initialize
-	 * its own na->si. We have to init our na->si here. */
-	nm_os_selinfo_init(&na->si[t]);
-
-	/* Force the mem_rings_create() method to create the
-	 * host rings independently on what the regif asked for:
-	 * these rings are needed by the guest ptnetmap adapter
-	 * anyway. */
-	kring = &NMR(na, t)[nma_get_nrings(na, t)];
-	kring->nr_kflags |= NKR_NEEDRING;
-    }
-
-    return 0;
-}
-
-/* nm_krings_delete callback for ptnetmap */
-static void
-nm_pt_host_krings_delete(struct netmap_adapter *na)
-{
-    struct netmap_pt_host_adapter *pth_na =
-        (struct netmap_pt_host_adapter *)na;
-    struct netmap_adapter *parent = pth_na->parent;
-
-    DBG(D("%s", pth_na->up.name));
-
-    parent->nm_krings_delete(parent);
-
-    na->tx_rings = na->rx_rings = na->tailroom = NULL;
-}
-
-/* nm_register callback */
-static int
-nm_pt_host_register(struct netmap_adapter *na, int onoff)
-{
-    struct netmap_pt_host_adapter *pth_na =
-        (struct netmap_pt_host_adapter *)na;
-    struct netmap_adapter *parent = pth_na->parent;
-    int error;
-    DBG(D("%s onoff %d", pth_na->up.name, onoff));
-
-    if (onoff) {
-        /* netmap_do_regif has been called on the ptnetmap na.
-         * We need to pass the information about the
-         * memory allocator to the parent before
-         * putting it in netmap mode
-         */
-        parent->na_lut = na->na_lut;
-    }
-
-    /* forward the request to the parent */
-    error = parent->nm_register(parent, onoff);
-    if (error)
-        return error;
-
-
-    if (onoff) {
-        na->na_flags |= NAF_NETMAP_ON | NAF_PTNETMAP_HOST;
-    } else {
-        ptnetmap_delete(pth_na);
-        na->na_flags &= ~(NAF_NETMAP_ON | NAF_PTNETMAP_HOST);
-    }
-
-    return 0;
-}
-
-/* nm_dtor callback */
-static void
-nm_pt_host_dtor(struct netmap_adapter *na)
-{
-    struct netmap_pt_host_adapter *pth_na =
-        (struct netmap_pt_host_adapter *)na;
-    struct netmap_adapter *parent = pth_na->parent;
-
-    DBG(D("%s", pth_na->up.name));
-
-    /* The equivalent of NETMAP_PT_HOST_DELETE if the hypervisor
-     * didn't do it. */
-    ptnetmap_stop_kthreads(pth_na);
-    ptnetmap_delete(pth_na);
-
-    parent->na_flags &= ~NAF_BUSY;
-
-    netmap_adapter_put(pth_na->parent);
-    pth_na->parent = NULL;
-}
-
-/* check if nmr is a request for a ptnetmap adapter that we can satisfy */
-int
-netmap_get_pt_host_na(struct nmreq *nmr, struct netmap_adapter **na,
-		struct netmap_mem_d *nmd, int create)
-{
-    struct nmreq parent_nmr;
-    struct netmap_adapter *parent; /* target adapter */
-    struct netmap_pt_host_adapter *pth_na;
-    struct ifnet *ifp = NULL;
-    int error;
-
-    /* Check if it is a request for a ptnetmap adapter */
-    if ((nmr->nr_flags & (NR_PTNETMAP_HOST)) == 0) {
-        return 0;
-    }
-
-    D("Requesting a ptnetmap host adapter");
-
-    pth_na = nm_os_malloc(sizeof(*pth_na));
-    if (pth_na == NULL) {
-        D("ERROR malloc");
-        return ENOMEM;
-    }
-
-    /* first, try to find the adapter that we want to passthrough
-     * We use the same nmr, after we have turned off the ptnetmap flag.
-     * In this way we can potentially passthrough everything netmap understands.
-     */
-    memcpy(&parent_nmr, nmr, sizeof(parent_nmr));
-    parent_nmr.nr_flags &= ~(NR_PTNETMAP_HOST);
-    error = netmap_get_na(&parent_nmr, &parent, &ifp, nmd, create);
-    if (error) {
-        D("parent lookup failed: %d", error);
-        goto put_out_noputparent;
-    }
-    DBG(D("found parent: %s", parent->name));
-
-    /* make sure the interface is not already in use */
-    if (NETMAP_OWNED_BY_ANY(parent)) {
-        D("NIC %s busy, cannot ptnetmap", parent->name);
-        error = EBUSY;
-        goto put_out;
-    }
-
-    pth_na->parent = parent;
-
-    /* Follow netmap_attach()-like operations for the host
-     * ptnetmap adapter. */
-
-    //XXX pth_na->up.na_flags = parent->na_flags;
-    pth_na->up.num_rx_rings = parent->num_rx_rings;
-    pth_na->up.num_tx_rings = parent->num_tx_rings;
-    pth_na->up.num_tx_desc = parent->num_tx_desc;
-    pth_na->up.num_rx_desc = parent->num_rx_desc;
-
-    pth_na->up.nm_dtor = nm_pt_host_dtor;
-    pth_na->up.nm_register = nm_pt_host_register;
-
-    /* Reuse parent's adapter txsync and rxsync methods. */
-    pth_na->up.nm_txsync = parent->nm_txsync;
-    pth_na->up.nm_rxsync = parent->nm_rxsync;
-
-    pth_na->up.nm_krings_create = nm_pt_host_krings_create;
-    pth_na->up.nm_krings_delete = nm_pt_host_krings_delete;
-    pth_na->up.nm_config = nm_pt_host_config;
-
-    /* Set the notify method only or convenience, it will never
-     * be used, since - differently from default krings_create - we
-     * ptnetmap krings_create callback inits kring->nm_notify
-     * directly. */
-    pth_na->up.nm_notify = nm_unused_notify;
-
-    pth_na->up.nm_mem = netmap_mem_get(parent->nm_mem);
-
-    pth_na->up.na_flags |= NAF_HOST_RINGS;
-
-    error = netmap_attach_common(&pth_na->up);
-    if (error) {
-        D("ERROR netmap_attach_common()");
-        goto put_out;
-    }
-
-    *na = &pth_na->up;
-    netmap_adapter_get(*na);
-
-    /* set parent busy, because attached for ptnetmap */
-    parent->na_flags |= NAF_BUSY;
-
-    strncpy(pth_na->up.name, parent->name, sizeof(pth_na->up.name));
-    strcat(pth_na->up.name, "-PTN");
-
-    DBG(D("%s ptnetmap request DONE", pth_na->up.name));
-
-    /* drop the reference to the ifp, if any */
-    if (ifp)
-        if_rele(ifp);
-
-    return 0;
-
-put_out:
-    netmap_adapter_put(parent);
-    if (ifp)
-	if_rele(ifp);
-put_out_noputparent:
-    nm_os_free(pth_na);
-    return error;
-}
-#endif /* WITH_PTNETMAP_HOST */
-
-#ifdef WITH_PTNETMAP_GUEST
-/*
- * Guest ptnetmap txsync()/rxsync() routines, used in ptnet device drivers.
- * These routines are reused across the different operating systems supported
- * by netmap.
- */
-
-/*
- * Reconcile host and guest views of the transmit ring.
- *
- * Guest user wants to transmit packets up to the one before ring->head,
- * and guest kernel knows tx_ring->hwcur is the first packet unsent
- * by the host kernel.
- *
- * We push out as many packets as possible, and possibly
- * reclaim buffers from previously completed transmission.
- *
- * Notifications from the host are enabled only if the user guest would
- * block (no space in the ring).
- */
-bool
-netmap_pt_guest_txsync(struct ptnet_ring *ptring, struct netmap_kring *kring,
-		       int flags)
-{
-	bool notify = false;
-
-	/* Disable notifications */
-	ptring->guest_need_kick = 0;
-
-	/*
-	 * First part: tell the host (updating the CSB) to process the new
-	 * packets.
-	 */
-	kring->nr_hwcur = ptring->hwcur;
-	ptnetmap_guest_write_kring_csb(ptring, kring->rcur, kring->rhead);
-
-        /* Ask for a kick from a guest to the host if needed. */
-	if ((kring->rhead != kring->nr_hwcur &&
-		NM_ACCESS_ONCE(ptring->host_need_kick)) ||
-			(flags & NAF_FORCE_RECLAIM)) {
-		ptring->sync_flags = flags;
-		notify = true;
-	}
-
-	/*
-	 * Second part: reclaim buffers for completed transmissions.
-	 */
-	if (nm_kr_txempty(kring) || (flags & NAF_FORCE_RECLAIM)) {
-                ptnetmap_guest_read_kring_csb(ptring, kring);
-	}
-
-        /*
-         * No more room in the ring for new transmissions. The user thread will
-	 * go to sleep and we need to be notified by the host when more free
-	 * space is available.
-         */
-	if (nm_kr_txempty(kring)) {
-		/* Reenable notifications. */
-		ptring->guest_need_kick = 1;
-                /* Double check */
-                ptnetmap_guest_read_kring_csb(ptring, kring);
-                /* If there is new free space, disable notifications */
-		if (unlikely(!nm_kr_txempty(kring))) {
-			ptring->guest_need_kick = 0;
-		}
-	}
-
-	ND(1, "TX - CSB: head:%u cur:%u hwtail:%u - KRING: head:%u cur:%u tail: %u",
-			ptring->head, ptring->cur, ptring->hwtail,
-			kring->rhead, kring->rcur, kring->nr_hwtail);
-
-	return notify;
-}
-
-/*
- * Reconcile host and guest view of the receive ring.
- *
- * Update hwcur/hwtail from host (reading from CSB).
- *
- * If guest user has released buffers up to the one before ring->head, we
- * also give them to the host.
- *
- * Notifications from the host are enabled only if the user guest would
- * block (no more completed slots in the ring).
- */
-bool
-netmap_pt_guest_rxsync(struct ptnet_ring *ptring, struct netmap_kring *kring,
-		       int flags)
-{
-	bool notify = false;
-
-        /* Disable notifications */
-	ptring->guest_need_kick = 0;
-
-	/*
-	 * First part: import newly received packets, by updating the kring
-	 * hwtail to the hwtail known from the host (read from the CSB).
-	 * This also updates the kring hwcur.
-	 */
-        ptnetmap_guest_read_kring_csb(ptring, kring);
-	kring->nr_kflags &= ~NKR_PENDINTR;
-
-	/*
-	 * Second part: tell the host about the slots that guest user has
-	 * released, by updating cur and head in the CSB.
-	 */
-	if (kring->rhead != kring->nr_hwcur) {
-		ptnetmap_guest_write_kring_csb(ptring, kring->rcur,
-					       kring->rhead);
-                /* Ask for a kick from the guest to the host if needed. */
-		if (NM_ACCESS_ONCE(ptring->host_need_kick)) {
-			ptring->sync_flags = flags;
-			notify = true;
-		}
-	}
-
-        /*
-         * No more completed RX slots. The user thread will go to sleep and
-	 * we need to be notified by the host when more RX slots have been
-	 * completed.
-         */
-	if (nm_kr_rxempty(kring)) {
-		/* Reenable notifications. */
-                ptring->guest_need_kick = 1;
-                /* Double check */
-                ptnetmap_guest_read_kring_csb(ptring, kring);
-                /* If there are new slots, disable notifications. */
-		if (!nm_kr_rxempty(kring)) {
-                        ptring->guest_need_kick = 0;
-                }
-        }
-
-	ND(1, "RX - CSB: head:%u cur:%u hwtail:%u - KRING: head:%u cur:%u",
-		ptring->head, ptring->cur, ptring->hwtail,
-		kring->rhead, kring->rcur);
-
-	return notify;
-}
-
-/*
- * Callbacks for ptnet drivers: nm_krings_create, nm_krings_delete, nm_dtor.
- */
-int
-ptnet_nm_krings_create(struct netmap_adapter *na)
-{
-	struct netmap_pt_guest_adapter *ptna =
-			(struct netmap_pt_guest_adapter *)na; /* Upcast. */
-	struct netmap_adapter *na_nm = &ptna->hwup.up;
-	struct netmap_adapter *na_dr = &ptna->dr.up;
-	int ret;
-
-	if (ptna->backend_regifs) {
-		return 0;
-	}
-
-	/* Create krings on the public netmap adapter. */
-	ret = netmap_hw_krings_create(na_nm);
-	if (ret) {
-		return ret;
-	}
-
-	/* Copy krings into the netmap adapter private to the driver. */
-	na_dr->tx_rings = na_nm->tx_rings;
-	na_dr->rx_rings = na_nm->rx_rings;
-
-	return 0;
-}
-
-void
-ptnet_nm_krings_delete(struct netmap_adapter *na)
-{
-	struct netmap_pt_guest_adapter *ptna =
-			(struct netmap_pt_guest_adapter *)na; /* Upcast. */
-	struct netmap_adapter *na_nm = &ptna->hwup.up;
-	struct netmap_adapter *na_dr = &ptna->dr.up;
-
-	if (ptna->backend_regifs) {
-		return;
-	}
-
-	na_dr->tx_rings = NULL;
-	na_dr->rx_rings = NULL;
-
-	netmap_hw_krings_delete(na_nm);
-}
-
-void
-ptnet_nm_dtor(struct netmap_adapter *na)
-{
-	struct netmap_pt_guest_adapter *ptna =
-			(struct netmap_pt_guest_adapter *)na;
-
-	netmap_mem_put(ptna->dr.up.nm_mem); // XXX is this needed?
-	memset(&ptna->dr, 0, sizeof(ptna->dr));
-	netmap_mem_pt_guest_ifp_del(na->nm_mem, na->ifp);
-}
-
-#endif /* WITH_PTNETMAP_GUEST */
diff --git a/sys/dev/netmap/netmap_vale.c b/sys/dev/netmap/netmap_vale.c
index d8a252f8d..0ada47922 100644
--- a/sys/dev/netmap/netmap_vale.c
+++ b/sys/dev/netmap/netmap_vale.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2013-2016 Universita` di Pisa
  * All rights reserved.
  *
@@ -25,41 +27,8 @@
  */
 
 
-/*
- * This module implements the VALE switch for netmap
-
---- VALE SWITCH ---
-
-NMG_LOCK() serializes all modifications to switches and ports.
-A switch cannot be deleted until all ports are gone.
-
-For each switch, an SX lock (RWlock on linux) protects
-deletion of ports. When configuring or deleting a new port, the
-lock is acquired in exclusive mode (after holding NMG_LOCK).
-When forwarding, the lock is acquired in shared mode (without NMG_LOCK).
-The lock is held throughout the entire forwarding cycle,
-during which the thread may incur in a page fault.
-Hence it is important that sleepable shared locks are used.
-
-On the rx ring, the per-port lock is grabbed initially to reserve
-a number of slot in the ring, then the lock is released,
-packets are copied from source to destination, and then
-the lock is acquired again and the receive ring is updated.
-(A similar thing is done on the tx ring for NIC and host stack
-ports attached to the switch)
-
- */
-
-/*
- * OS-specific code that is used only within this file.
- * Other OS-specific code that must be accessed by drivers
- * is present in netmap_kern.h
- */
-
 #if defined(__FreeBSD__)
 #include  /* prerequisite */
-__FBSDID("$FreeBSD: head/sys/dev/netmap/netmap.c 257176 2013-10-26 17:58:36Z glebius $");
-
 #include 
 #include 
 #include 	/* defines used in kernel.h */
@@ -79,18 +48,7 @@ __FBSDID("$FreeBSD: head/sys/dev/netmap/netmap.c 257176 2013-10-26 17:58:36Z gle
 #include 	/* bus_dmamap_* */
 #include 
 #include 
-
-
-#define BDG_RWLOCK_T		struct rwlock // struct rwlock
-
-#define	BDG_RWINIT(b)		\
-	rw_init_flags(&(b)->bdg_lock, "bdg lock", RW_NOWITNESS)
-#define BDG_WLOCK(b)		rw_wlock(&(b)->bdg_lock)
-#define BDG_WUNLOCK(b)		rw_wunlock(&(b)->bdg_lock)
-#define BDG_RLOCK(b)		rw_rlock(&(b)->bdg_lock)
-#define BDG_RTRYLOCK(b)		rw_try_rlock(&(b)->bdg_lock)
-#define BDG_RUNLOCK(b)		rw_runlock(&(b)->bdg_lock)
-#define BDG_RWDESTROY(b)	rw_destroy(&(b)->bdg_lock)
+#include 
 
 
 #elif defined(linux)
@@ -118,15 +76,15 @@ __FBSDID("$FreeBSD: head/sys/dev/netmap/netmap.c 257176 2013-10-26 17:58:36Z gle
 #include 
 #include 
 #include 
+#include 
 
 #ifdef WITH_VALE
 
 /*
  * system parameters (most of them in netmap_kern.h)
- * NM_BDG_NAME	prefix for switch port names, default "vale"
+ * NM_BDG_NAME		prefix for switch port names, default "vale"
  * NM_BDG_MAXPORTS	number of ports
- * NM_BRIDGES	max number of switches in the system.
- *	XXX should become a sysctl or tunable
+ * NM_BRIDGES		max number of switches in the system.
  *
  * Switch ports are named valeX:Y where X is the switch name and Y
  * is the port. If Y matches a physical interface name, the port is
@@ -138,14 +96,12 @@ __FBSDID("$FreeBSD: head/sys/dev/netmap/netmap.c 257176 2013-10-26 17:58:36Z gle
  * In the tx loop, we aggregate traffic in batches to make all operations
  * faster. The batch size is bridge_batch.
  */
-#define NM_BDG_MAXRINGS		16	/* XXX unclear how many. */
+#define NM_BDG_MAXRINGS		16	/* XXX unclear how many (must be a pow of 2). */
 #define NM_BDG_MAXSLOTS		4096	/* XXX same as above */
 #define NM_BRIDGE_RINGSIZE	1024	/* in the device */
-#define NM_BDG_HASH		1024	/* forwarding table entries */
 #define NM_BDG_BATCH		1024	/* entries in the forwarding buffer */
-#define NM_MULTISEG		64	/* max size of a chain of bufs */
 /* actual size of the tables */
-#define NM_BDG_BATCH_MAX	(NM_BDG_BATCH + NM_MULTISEG)
+#define NM_BDG_BATCH_MAX	(NM_BDG_BATCH + NETMAP_MAX_FRAGS)
 /* NM_FT_NULL terminates a list of slots in the ft */
 #define NM_FT_NULL		NM_BDG_BATCH_MAX
 
@@ -156,227 +112,74 @@ __FBSDID("$FreeBSD: head/sys/dev/netmap/netmap.c 257176 2013-10-26 17:58:36Z gle
  * last packet in the block may overflow the size.
  */
 static int bridge_batch = NM_BDG_BATCH; /* bridge batch size */
+
+/* Max number of vale bridges (loader tunable). */
+unsigned int vale_max_bridges = NM_BRIDGES;
+
 SYSBEGIN(vars_vale);
 SYSCTL_DECL(_dev_netmap);
-SYSCTL_INT(_dev_netmap, OID_AUTO, bridge_batch, CTLFLAG_RW, &bridge_batch, 0 , "");
+SYSCTL_INT(_dev_netmap, OID_AUTO, bridge_batch, CTLFLAG_RW, &bridge_batch, 0,
+		"Max batch size to be used in the bridge");
+SYSCTL_UINT(_dev_netmap, OID_AUTO, max_bridges, CTLFLAG_RDTUN, &vale_max_bridges, 0,
+		"Max number of vale bridges");
 SYSEND;
 
-static int netmap_vp_create(struct nmreq *, struct ifnet *,
+static int netmap_vale_vp_create(struct nmreq_header *hdr, if_t,
 		struct netmap_mem_d *nmd, struct netmap_vp_adapter **);
-static int netmap_vp_reg(struct netmap_adapter *na, int onoff);
-static int netmap_bwrap_reg(struct netmap_adapter *, int onoff);
+static int netmap_vale_vp_bdg_attach(const char *, struct netmap_adapter *,
+		struct nm_bridge *);
+static int netmap_vale_bwrap_attach(const char *, struct netmap_adapter *);
 
 /*
- * For each output interface, nm_bdg_q is used to construct a list.
+ * For each output interface, nm_vale_q is used to construct a list.
  * bq_len is the number of output buffers (we can have coalescing
  * during the copy).
  */
-struct nm_bdg_q {
+struct nm_vale_q {
 	uint16_t bq_head;
 	uint16_t bq_tail;
 	uint32_t bq_len;	/* number of buffers */
 };
 
-/* XXX revise this */
-struct nm_hash_ent {
-	uint64_t	mac;	/* the top 2 bytes are the epoch */
-	uint64_t	ports;
-};
-
-/*
- * nm_bridge is a descriptor for a VALE switch.
- * Interfaces for a bridge are all in bdg_ports[].
- * The array has fixed size, an empty entry does not terminate
- * the search, but lookups only occur on attach/detach so we
- * don't mind if they are slow.
- *
- * The bridge is non blocking on the transmit ports: excess
- * packets are dropped if there is no room on the output port.
- *
- * bdg_lock protects accesses to the bdg_ports array.
- * This is a rw lock (or equivalent).
- */
-struct nm_bridge {
-	/* XXX what is the proper alignment/layout ? */
-	BDG_RWLOCK_T	bdg_lock;	/* protects bdg_ports */
-	int		bdg_namelen;
-	uint32_t	bdg_active_ports; /* 0 means free */
-	char		bdg_basename[IFNAMSIZ];
-
-	/* Indexes of active ports (up to active_ports)
-	 * and all other remaining ports.
-	 */
-	uint8_t		bdg_port_index[NM_BDG_MAXPORTS];
-
-	struct netmap_vp_adapter *bdg_ports[NM_BDG_MAXPORTS];
-
-
-	/*
-	 * The function to decide the destination port.
-	 * It returns either of an index of the destination port,
-	 * NM_BDG_BROADCAST to broadcast this packet, or NM_BDG_NOPORT not to
-	 * forward this packet.  ring_nr is the source ring index, and the
-	 * function may overwrite this value to forward this packet to a
-	 * different ring index.
-	 * This function must be set by netmap_bdg_ctl().
-	 */
-	struct netmap_bdg_ops bdg_ops;
-
-	/* the forwarding table, MAC+ports.
-	 * XXX should be changed to an argument to be passed to
-	 * the lookup function, and allocated on attach
-	 */
-	struct nm_hash_ent ht[NM_BDG_HASH];
-
-#ifdef CONFIG_NET_NS
-	struct net *ns;
-#endif /* CONFIG_NET_NS */
+/* Holds the default callbacks */
+struct netmap_bdg_ops vale_bdg_ops = {
+	.lookup = netmap_vale_learning,
+	.config = NULL,
+	.dtor = NULL,
+	.vp_create = netmap_vale_vp_create,
+	.bwrap_attach = netmap_vale_bwrap_attach,
+	.name = NM_BDG_NAME,
 };
 
-const char*
-netmap_bdg_name(struct netmap_vp_adapter *vp)
-{
-	struct nm_bridge *b = vp->na_bdg;
-	if (b == NULL)
-		return NULL;
-	return b->bdg_basename;
-}
-
-
-#ifndef CONFIG_NET_NS
-/*
- * XXX in principle nm_bridges could be created dynamically
- * Right now we have a static array and deletions are protected
- * by an exclusive lock.
- */
-static struct nm_bridge *nm_bridges;
-#endif /* !CONFIG_NET_NS */
-
-
 /*
  * this is a slightly optimized copy routine which rounds
  * to multiple of 64 bytes and is often faster than dealing
  * with other odd sizes. We assume there is enough room
  * in the source and destination buffers.
  *
- * XXX only for multiples of 64 bytes, non overlapped.
+ * XXX only for multiples of NM_BUF_ALIGN bytes, non overlapped.
  */
+
 static inline void
 pkt_copy(void *_src, void *_dst, int l)
 {
-        uint64_t *src = _src;
-        uint64_t *dst = _dst;
-        if (unlikely(l >= 1024)) {
-                memcpy(dst, src, l);
-                return;
-        }
-        for (; likely(l > 0); l-=64) {
-                *dst++ = *src++;
-                *dst++ = *src++;
-                *dst++ = *src++;
-                *dst++ = *src++;
-                *dst++ = *src++;
-                *dst++ = *src++;
-                *dst++ = *src++;
-                *dst++ = *src++;
-        }
-}
-
-
-static int
-nm_is_id_char(const char c)
-{
-	return (c >= 'a' && c <= 'z') ||
-	       (c >= 'A' && c <= 'Z') ||
-	       (c >= '0' && c <= '9') ||
-	       (c == '_');
-}
-
-/* Validate the name of a VALE bridge port and return the
- * position of the ":" character. */
-static int
-nm_vale_name_validate(const char *name)
-{
-	int colon_pos = -1;
-	int i;
-
-	if (!name || strlen(name) < strlen(NM_BDG_NAME)) {
-		return -1;
-	}
-
-	for (i = 0; name[i]; i++) {
-		if (name[i] == ':') {
-			if (colon_pos != -1) {
-				return -1;
-			}
-			colon_pos = i;
-		} else if (!nm_is_id_char(name[i])) {
-			return -1;
-		}
-	}
-
-	if (i >= IFNAMSIZ) {
-		return -1;
-	}
-
-	return colon_pos;
-}
-
-/*
- * locate a bridge among the existing ones.
- * MUST BE CALLED WITH NMG_LOCK()
- *
- * a ':' in the name terminates the bridge name. Otherwise, just NM_NAME.
- * We assume that this is called with a name of at least NM_NAME chars.
- */
-static struct nm_bridge *
-nm_find_bridge(const char *name, int create)
-{
-	int i, namelen;
-	struct nm_bridge *b = NULL, *bridges;
-	u_int num_bridges;
-
-	NMG_LOCK_ASSERT();
-
-	netmap_bns_getbridges(&bridges, &num_bridges);
-
-	namelen = nm_vale_name_validate(name);
-	if (namelen < 0) {
-		D("invalid bridge name %s", name ? name : NULL);
-		return NULL;
-	}
-
-	/* lookup the name, remember empty slot if there is one */
-	for (i = 0; i < num_bridges; i++) {
-		struct nm_bridge *x = bridges + i;
-
-		if (x->bdg_active_ports == 0) {
-			if (create && b == NULL)
-				b = x;	/* record empty slot */
-		} else if (x->bdg_namelen != namelen) {
-			continue;
-		} else if (strncmp(name, x->bdg_basename, namelen) == 0) {
-			ND("found '%.*s' at %d", namelen, name, i);
-			b = x;
-			break;
-		}
+	uint64_t *src = _src;
+	uint64_t *dst = _dst;
+	if (unlikely(l >= 1024)) {
+		memcpy(dst, src, l);
+		return;
 	}
-	if (i == num_bridges && b) { /* name not found, can create entry */
-		/* initialize the bridge */
-		strncpy(b->bdg_basename, name, namelen);
-		ND("create new bridge %s with ports %d", b->bdg_basename,
-			b->bdg_active_ports);
-		b->bdg_namelen = namelen;
-		b->bdg_active_ports = 0;
-		for (i = 0; i < NM_BDG_MAXPORTS; i++)
-			b->bdg_port_index[i] = i;
-		/* set the default function */
-		b->bdg_ops.lookup = netmap_bdg_learning;
-		/* reset the MAC address table */
-		bzero(b->ht, sizeof(struct nm_hash_ent) * NM_BDG_HASH);
-		NM_BNS_GET(b);
+	for (; likely(l > 0); l -= NM_BUF_ALIGN) {
+		/* XXX NM_BUF_ALIGN/sizeof(uint64_t) statements */
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
 	}
-	return b;
 }
 
 
@@ -387,15 +190,15 @@ static void
 nm_free_bdgfwd(struct netmap_adapter *na)
 {
 	int nrings, i;
-	struct netmap_kring *kring;
+	struct netmap_kring **kring;
 
 	NMG_LOCK_ASSERT();
 	nrings = na->num_tx_rings;
 	kring = na->tx_rings;
 	for (i = 0; i < nrings; i++) {
-		if (kring[i].nkr_ft) {
-			nm_os_free(kring[i].nkr_ft);
-			kring[i].nkr_ft = NULL; /* protect from freeing twice */
+		if (kring[i]->nkr_ft) {
+			nm_os_free(kring[i]->nkr_ft);
+			kring[i]->nkr_ft = NULL; /* protect from freeing twice */
 		}
 	}
 }
@@ -408,20 +211,20 @@ static int
 nm_alloc_bdgfwd(struct netmap_adapter *na)
 {
 	int nrings, l, i, num_dstq;
-	struct netmap_kring *kring;
+	struct netmap_kring **kring;
 
 	NMG_LOCK_ASSERT();
 	/* all port:rings + broadcast */
 	num_dstq = NM_BDG_MAXPORTS * NM_BDG_MAXRINGS + 1;
 	l = sizeof(struct nm_bdg_fwd) * NM_BDG_BATCH_MAX;
-	l += sizeof(struct nm_bdg_q) * num_dstq;
+	l += sizeof(struct nm_vale_q) * num_dstq;
 	l += sizeof(uint16_t) * NM_BDG_BATCH_MAX;
 
 	nrings = netmap_real_rings(na, NR_TX);
 	kring = na->tx_rings;
 	for (i = 0; i < nrings; i++) {
 		struct nm_bdg_fwd *ft;
-		struct nm_bdg_q *dstq;
+		struct nm_vale_q *dstq;
 		int j;
 
 		ft = nm_os_malloc(l);
@@ -429,1040 +232,275 @@ nm_alloc_bdgfwd(struct netmap_adapter *na)
 			nm_free_bdgfwd(na);
 			return ENOMEM;
 		}
-		dstq = (struct nm_bdg_q *)(ft + NM_BDG_BATCH_MAX);
+		dstq = (struct nm_vale_q *)(ft + NM_BDG_BATCH_MAX);
 		for (j = 0; j < num_dstq; j++) {
 			dstq[j].bq_head = dstq[j].bq_tail = NM_FT_NULL;
 			dstq[j].bq_len = 0;
 		}
-		kring[i].nkr_ft = ft;
+		kring[i]->nkr_ft = ft;
 	}
 	return 0;
 }
 
-
-/* remove from bridge b the ports in slots hw and sw
- * (sw can be -1 if not needed)
+/* Allows external modules to create bridges in exclusive mode,
+ * returns an authentication token that the external module will need
+ * to provide during nm_bdg_ctl_{attach, detach}(), netmap_bdg_regops(),
+ * and nm_bdg_update_private_data() operations.
+ * Successfully executed if ret != NULL and *return_status == 0.
  */
-static void
-netmap_bdg_detach_common(struct nm_bridge *b, int hw, int sw)
+void *
+netmap_vale_create(const char *bdg_name, int *return_status)
 {
-	int s_hw = hw, s_sw = sw;
-	int i, lim =b->bdg_active_ports;
-	uint8_t tmp[NM_BDG_MAXPORTS];
+	struct nm_bridge *b = NULL;
+	void *ret = NULL;
 
-	/*
-	New algorithm:
-	make a copy of bdg_port_index;
-	lookup NA(ifp)->bdg_port and SWNA(ifp)->bdg_port
-	in the array of bdg_port_index, replacing them with
-	entries from the bottom of the array;
-	decrement bdg_active_ports;
-	acquire BDG_WLOCK() and copy back the array.
-	 */
+	NMG_LOCK();
+	b = nm_find_bridge(bdg_name, 0 /* don't create */, NULL);
+	if (b) {
+		*return_status = EEXIST;
+		goto unlock_bdg_create;
+	}
 
-	if (netmap_verbose)
-		D("detach %d and %d (lim %d)", hw, sw, lim);
-	/* make a copy of the list of active ports, update it,
-	 * and then copy back within BDG_WLOCK().
-	 */
-	memcpy(tmp, b->bdg_port_index, sizeof(tmp));
-	for (i = 0; (hw >= 0 || sw >= 0) && i < lim; ) {
-		if (hw >= 0 && tmp[i] == hw) {
-			ND("detach hw %d at %d", hw, i);
-			lim--; /* point to last active port */
-			tmp[i] = tmp[lim]; /* swap with i */
-			tmp[lim] = hw;	/* now this is inactive */
-			hw = -1;
-		} else if (sw >= 0 && tmp[i] == sw) {
-			ND("detach sw %d at %d", sw, i);
-			lim--;
-			tmp[i] = tmp[lim];
-			tmp[lim] = sw;
-			sw = -1;
-		} else {
-			i++;
-		}
+	b = nm_find_bridge(bdg_name, 1 /* create */, &vale_bdg_ops);
+	if (!b) {
+		*return_status = ENOMEM;
+		goto unlock_bdg_create;
 	}
-	if (hw >= 0 || sw >= 0) {
-		D("XXX delete failed hw %d sw %d, should panic...", hw, sw);
+
+	b->bdg_flags |= NM_BDG_ACTIVE | NM_BDG_EXCLUSIVE;
+	ret = nm_bdg_get_auth_token(b);
+	*return_status = 0;
+
+unlock_bdg_create:
+	NMG_UNLOCK();
+	return ret;
+}
+
+/* Allows external modules to destroy a bridge created through
+ * netmap_bdg_create(), the bridge must be empty.
+ */
+int
+netmap_vale_destroy(const char *bdg_name, void *auth_token)
+{
+	struct nm_bridge *b = NULL;
+	int ret = 0;
+
+	NMG_LOCK();
+	b = nm_find_bridge(bdg_name, 0 /* don't create */, NULL);
+	if (!b) {
+		ret = ENXIO;
+		goto unlock_bdg_free;
 	}
 
-	BDG_WLOCK(b);
-	if (b->bdg_ops.dtor)
-		b->bdg_ops.dtor(b->bdg_ports[s_hw]);
-	b->bdg_ports[s_hw] = NULL;
-	if (s_sw >= 0) {
-		b->bdg_ports[s_sw] = NULL;
+	if (!nm_bdg_valid_auth_token(b, auth_token)) {
+		ret = EACCES;
+		goto unlock_bdg_free;
 	}
-	memcpy(b->bdg_port_index, tmp, sizeof(tmp));
-	b->bdg_active_ports = lim;
-	BDG_WUNLOCK(b);
-
-	ND("now %d active ports", lim);
-	if (lim == 0) {
-		ND("marking bridge %s as free", b->bdg_basename);
-		bzero(&b->bdg_ops, sizeof(b->bdg_ops));
-		NM_BNS_PUT(b);
+	if (!(b->bdg_flags & NM_BDG_EXCLUSIVE)) {
+		ret = EINVAL;
+		goto unlock_bdg_free;
+	}
+
+	b->bdg_flags &= ~(NM_BDG_EXCLUSIVE | NM_BDG_ACTIVE);
+	ret = netmap_bdg_free(b);
+	if (ret) {
+		b->bdg_flags |= NM_BDG_EXCLUSIVE | NM_BDG_ACTIVE;
 	}
+
+unlock_bdg_free:
+	NMG_UNLOCK();
+	return ret;
 }
 
-/* nm_bdg_ctl callback for VALE ports */
-static int
-netmap_vp_bdg_ctl(struct netmap_adapter *na, struct nmreq *nmr, int attach)
+/* Process NETMAP_REQ_VALE_LIST. */
+int
+netmap_vale_list(struct nmreq_header *hdr)
 {
-	struct netmap_vp_adapter *vpna = (struct netmap_vp_adapter *)na;
-	struct nm_bridge *b = vpna->na_bdg;
+	struct nmreq_vale_list *req =
+		(struct nmreq_vale_list *)(uintptr_t)hdr->nr_body;
+	int namelen = strlen(hdr->nr_name);
+	struct nm_bridge *b, *bridges;
+	struct netmap_vp_adapter *vpna;
+	int error = 0, i, j;
+	u_int num_bridges;
 
-	(void)nmr;	// XXX merge ?
-	if (attach)
-		return 0; /* nothing to do */
-	if (b) {
-		netmap_set_all_rings(na, 0 /* disable */);
-		netmap_bdg_detach_common(b, vpna->bdg_port, -1);
-		vpna->na_bdg = NULL;
-		netmap_set_all_rings(na, 1 /* enable */);
+	netmap_bns_getbridges(&bridges, &num_bridges);
+
+	/* this is used to enumerate bridges and ports */
+	if (namelen) { /* look up indexes of bridge and port */
+		if (strncmp(hdr->nr_name, NM_BDG_NAME,
+					strlen(NM_BDG_NAME))) {
+			return EINVAL;
+		}
+		NMG_LOCK();
+		b = nm_find_bridge(hdr->nr_name, 0 /* don't create */, NULL);
+		if (!b) {
+			NMG_UNLOCK();
+			return ENOENT;
+		}
+
+		req->nr_bridge_idx = b - bridges; /* bridge index */
+		req->nr_port_idx = NM_BDG_NOPORT;
+		for (j = 0; j < b->bdg_active_ports; j++) {
+			i = b->bdg_port_index[j];
+			vpna = b->bdg_ports[i];
+			if (vpna == NULL) {
+				nm_prerr("This should not happen");
+				continue;
+			}
+			/* the former and the latter identify a
+			 * virtual port and a NIC, respectively
+			 */
+			if (!strcmp(vpna->up.name, hdr->nr_name)) {
+				req->nr_port_idx = i; /* port index */
+				break;
+			}
+		}
+		NMG_UNLOCK();
+	} else {
+		/* return the first non-empty entry starting from
+		 * bridge nr_arg1 and port nr_arg2.
+		 *
+		 * Users can detect the end of the same bridge by
+		 * seeing the new and old value of nr_arg1, and can
+		 * detect the end of all the bridge by error != 0
+		 */
+		i = req->nr_bridge_idx;
+		j = req->nr_port_idx;
+
+		NMG_LOCK();
+		for (error = ENOENT; i < vale_max_bridges; i++) {
+			b = bridges + i;
+			for ( ; j < NM_BDG_MAXPORTS; j++) {
+				if (b->bdg_ports[j] == NULL)
+					continue;
+				vpna = b->bdg_ports[j];
+				/* write back the VALE switch name */
+				strlcpy(hdr->nr_name, vpna->up.name,
+					sizeof(hdr->nr_name));
+				error = 0;
+				goto out;
+			}
+			j = 0; /* following bridges scan from 0 */
+		}
+	out:
+		req->nr_bridge_idx = i;
+		req->nr_port_idx = j;
+		NMG_UNLOCK();
 	}
-	/* I have took reference just for attach */
-	netmap_adapter_put(na);
-	return 0;
+
+	return error;
 }
 
+
 /* nm_dtor callback for ephemeral VALE ports */
 static void
-netmap_vp_dtor(struct netmap_adapter *na)
+netmap_vale_vp_dtor(struct netmap_adapter *na)
 {
 	struct netmap_vp_adapter *vpna = (struct netmap_vp_adapter*)na;
 	struct nm_bridge *b = vpna->na_bdg;
 
-	ND("%s has %d references", na->name, na->na_refcount);
+	nm_prdis("%s has %d references", na->name, na->na_refcount);
 
 	if (b) {
 		netmap_bdg_detach_common(b, vpna->bdg_port, -1);
 	}
 
-	if (vpna->autodelete && na->ifp != NULL) {
-		ND("releasing %s", na->ifp->if_xname);
-		NMG_UNLOCK();
-		nm_os_vi_detach(na->ifp);
-		NMG_LOCK();
+	if (na->ifp != NULL && !nm_iszombie(na)) {
+		NM_DETACH_NA(na->ifp);
+		if (vpna->autodelete) {
+			nm_prdis("releasing %s", if_name(na->ifp));
+			NMG_UNLOCK();
+			nm_os_vi_detach(na->ifp);
+			NMG_LOCK();
+		}
 	}
 }
 
-/* remove a persistent VALE port from the system */
+
+
+/* nm_krings_create callback for VALE ports.
+ * Calls the standard netmap_krings_create, then adds leases on rx
+ * rings and bdgfwd on tx rings.
+ */
 static int
-nm_vi_destroy(const char *name)
+netmap_vale_vp_krings_create(struct netmap_adapter *na)
 {
-	struct ifnet *ifp;
-	struct netmap_vp_adapter *vpna;
-	int error;
+	u_int tailroom;
+	int error, i;
+	uint32_t *leases;
+	u_int nrx = netmap_real_rings(na, NR_RX);
 
-	ifp = ifunit_ref(name);
-	if (!ifp)
-		return ENXIO;
-	NMG_LOCK();
-	/* make sure this is actually a VALE port */
-	if (!NM_NA_VALID(ifp) || NA(ifp)->nm_register != netmap_vp_reg) {
-		error = EINVAL;
-		goto err;
-	}
+	/*
+	 * Leases are attached to RX rings on vale ports
+	 */
+	tailroom = sizeof(uint32_t) * na->num_rx_desc * nrx;
 
-	vpna = (struct netmap_vp_adapter *)NA(ifp);
+	error = netmap_krings_create(na, tailroom);
+	if (error)
+		return error;
 
-	/* we can only destroy ports that were created via NETMAP_BDG_NEWIF */
-	if (vpna->autodelete) {
-		error = EINVAL;
-		goto err;
-	}
+	leases = na->tailroom;
 
-	/* also make sure that nobody is using the inferface */
-	if (NETMAP_OWNED_BY_ANY(&vpna->up) ||
-	    vpna->up.na_refcount > 1 /* any ref besides the one in nm_vi_create()? */) {
-		error = EBUSY;
-		goto err;
+	for (i = 0; i < nrx; i++) { /* Receive rings */
+		na->rx_rings[i]->nkr_leases = leases;
+		leases += na->num_rx_desc;
 	}
 
-	NMG_UNLOCK();
+	error = nm_alloc_bdgfwd(na);
+	if (error) {
+		netmap_krings_delete(na);
+		return error;
+	}
 
-	D("destroying a persistent vale interface %s", ifp->if_xname);
-	/* Linux requires all the references are released
-	 * before unregister
-	 */
-	netmap_detach(ifp);
-	if_rele(ifp);
-	nm_os_vi_detach(ifp);
 	return 0;
-
-err:
-	NMG_UNLOCK();
-	if_rele(ifp);
-	return error;
 }
 
-static int
-nm_update_info(struct nmreq *nmr, struct netmap_adapter *na)
+
+/* nm_krings_delete callback for VALE ports. */
+static void
+netmap_vale_vp_krings_delete(struct netmap_adapter *na)
 {
-	nmr->nr_rx_rings = na->num_rx_rings;
-	nmr->nr_tx_rings = na->num_tx_rings;
-	nmr->nr_rx_slots = na->num_rx_desc;
-	nmr->nr_tx_slots = na->num_tx_desc;
-	return netmap_mem_get_info(na->nm_mem, &nmr->nr_memsize, NULL, &nmr->nr_arg2);
+	nm_free_bdgfwd(na);
+	netmap_krings_delete(na);
 }
 
+
+static int
+nm_vale_flush(struct nm_bdg_fwd *ft, u_int n,
+	struct netmap_vp_adapter *na, u_int ring_nr);
+
+
 /*
- * Create a virtual interface registered to the system.
- * The interface will be attached to a bridge later.
+ * main dispatch routine for the bridge.
+ * Grab packets from a kring, move them into the ft structure
+ * associated to the tx (input) port. Max one instance per port,
+ * filtered on input (ioctl, poll or XXX).
+ * Returns the next position in the ring.
  */
-int
-netmap_vi_create(struct nmreq *nmr, int autodelete)
+static int
+nm_vale_preflush(struct netmap_kring *kring, u_int end)
 {
-	struct ifnet *ifp;
-	struct netmap_vp_adapter *vpna;
-	struct netmap_mem_d *nmd = NULL;
-	int error;
-
-	/* don't include VALE prefix */
-	if (!strncmp(nmr->nr_name, NM_BDG_NAME, strlen(NM_BDG_NAME)))
-		return EINVAL;
-	ifp = ifunit_ref(nmr->nr_name);
-	if (ifp) { /* already exist, cannot create new one */
-		error = EEXIST;
-		NMG_LOCK();
-		if (NM_NA_VALID(ifp)) {
-			int update_err = nm_update_info(nmr, NA(ifp));
-			if (update_err)
-				error = update_err;
-		}
-		NMG_UNLOCK();
-		if_rele(ifp);
-		return error;
-	}
-	error = nm_os_vi_persist(nmr->nr_name, &ifp);
-	if (error)
-		return error;
-
-	NMG_LOCK();
-	if (nmr->nr_arg2) {
-		nmd = netmap_mem_find(nmr->nr_arg2);
-		if (nmd == NULL) {
-			error = EINVAL;
-			goto err_1;
-		}
-	}
-	/* netmap_vp_create creates a struct netmap_vp_adapter */
-	error = netmap_vp_create(nmr, ifp, nmd, &vpna);
-	if (error) {
-		D("error %d", error);
-		goto err_1;
-	}
-	/* persist-specific routines */
-	vpna->up.nm_bdg_ctl = netmap_vp_bdg_ctl;
-	if (!autodelete) {
-		netmap_adapter_get(&vpna->up);
-	} else {
-		vpna->autodelete = 1;
-	}
-	NM_ATTACH_NA(ifp, &vpna->up);
-	/* return the updated info */
-	error = nm_update_info(nmr, &vpna->up);
-	if (error) {
-		goto err_2;
-	}
-	D("returning nr_arg2 %d", nmr->nr_arg2);
-	if (nmd)
-		netmap_mem_put(nmd);
-	NMG_UNLOCK();
-	D("created %s", ifp->if_xname);
-	return 0;
-
-err_2:
-	netmap_detach(ifp);
-err_1:
-	if (nmd)
-		netmap_mem_put(nmd);
-	NMG_UNLOCK();
-	nm_os_vi_detach(ifp);
-
-	return error;
-}
-
-/* Try to get a reference to a netmap adapter attached to a VALE switch.
- * If the adapter is found (or is created), this function returns 0, a
- * non NULL pointer is returned into *na, and the caller holds a
- * reference to the adapter.
- * If an adapter is not found, then no reference is grabbed and the
- * function returns an error code, or 0 if there is just a VALE prefix
- * mismatch. Therefore the caller holds a reference when
- * (*na != NULL && return == 0).
- */
-int
-netmap_get_bdg_na(struct nmreq *nmr, struct netmap_adapter **na,
-		struct netmap_mem_d *nmd, int create)
-{
-	char *nr_name = nmr->nr_name;
-	const char *ifname;
-	struct ifnet *ifp = NULL;
-	int error = 0;
-	struct netmap_vp_adapter *vpna, *hostna = NULL;
-	struct nm_bridge *b;
-	int i, j, cand = -1, cand2 = -1;
-	int needed;
-
-	*na = NULL;     /* default return value */
-
-	/* first try to see if this is a bridge port. */
-	NMG_LOCK_ASSERT();
-	if (strncmp(nr_name, NM_BDG_NAME, sizeof(NM_BDG_NAME) - 1)) {
-		return 0;  /* no error, but no VALE prefix */
-	}
-
-	b = nm_find_bridge(nr_name, create);
-	if (b == NULL) {
-		D("no bridges available for '%s'", nr_name);
-		return (create ? ENOMEM : ENXIO);
-	}
-	if (strlen(nr_name) < b->bdg_namelen) /* impossible */
-		panic("x");
-
-	/* Now we are sure that name starts with the bridge's name,
-	 * lookup the port in the bridge. We need to scan the entire
-	 * list. It is not important to hold a WLOCK on the bridge
-	 * during the search because NMG_LOCK already guarantees
-	 * that there are no other possible writers.
-	 */
-
-	/* lookup in the local list of ports */
-	for (j = 0; j < b->bdg_active_ports; j++) {
-		i = b->bdg_port_index[j];
-		vpna = b->bdg_ports[i];
-		// KASSERT(na != NULL);
-		ND("checking %s", vpna->up.name);
-		if (!strcmp(vpna->up.name, nr_name)) {
-			netmap_adapter_get(&vpna->up);
-			ND("found existing if %s refs %d", nr_name)
-			*na = &vpna->up;
-			return 0;
-		}
-	}
-	/* not found, should we create it? */
-	if (!create)
-		return ENXIO;
-	/* yes we should, see if we have space to attach entries */
-	needed = 2; /* in some cases we only need 1 */
-	if (b->bdg_active_ports + needed >= NM_BDG_MAXPORTS) {
-		D("bridge full %d, cannot create new port", b->bdg_active_ports);
-		return ENOMEM;
-	}
-	/* record the next two ports available, but do not allocate yet */
-	cand = b->bdg_port_index[b->bdg_active_ports];
-	cand2 = b->bdg_port_index[b->bdg_active_ports + 1];
-	ND("+++ bridge %s port %s used %d avail %d %d",
-		b->bdg_basename, ifname, b->bdg_active_ports, cand, cand2);
-
-	/*
-	 * try see if there is a matching NIC with this name
-	 * (after the bridge's name)
-	 */
-	ifname = nr_name + b->bdg_namelen + 1;
-	ifp = ifunit_ref(ifname);
-	if (!ifp) {
-		/* Create an ephemeral virtual port
-		 * This block contains all the ephemeral-specific logics
-		 */
-		if (nmr->nr_cmd) {
-			/* nr_cmd must be 0 for a virtual port */
-			error = EINVAL;
-			goto out;
-		}
-
-		/* bdg_netmap_attach creates a struct netmap_adapter */
-		error = netmap_vp_create(nmr, NULL, nmd, &vpna);
-		if (error) {
-			D("error %d", error);
-			goto out;
-		}
-		/* shortcut - we can skip get_hw_na(),
-		 * ownership check and nm_bdg_attach()
-		 */
-	} else {
-		struct netmap_adapter *hw;
-
-		error = netmap_get_hw_na(ifp, nmd, &hw);
-		if (error || hw == NULL)
-			goto out;
-
-		/* host adapter might not be created */
-		error = hw->nm_bdg_attach(nr_name, hw);
-		if (error)
-			goto out;
-		vpna = hw->na_vp;
-		hostna = hw->na_hostvp;
-		if (nmr->nr_arg1 != NETMAP_BDG_HOST)
-			hostna = NULL;
-	}
-
-	BDG_WLOCK(b);
-	vpna->bdg_port = cand;
-	ND("NIC  %p to bridge port %d", vpna, cand);
-	/* bind the port to the bridge (virtual ports are not active) */
-	b->bdg_ports[cand] = vpna;
-	vpna->na_bdg = b;
-	b->bdg_active_ports++;
-	if (hostna != NULL) {
-		/* also bind the host stack to the bridge */
-		b->bdg_ports[cand2] = hostna;
-		hostna->bdg_port = cand2;
-		hostna->na_bdg = b;
-		b->bdg_active_ports++;
-		ND("host %p to bridge port %d", hostna, cand2);
-	}
-	ND("if %s refs %d", ifname, vpna->up.na_refcount);
-	BDG_WUNLOCK(b);
-	*na = &vpna->up;
-	netmap_adapter_get(*na);
-
-out:
-	if (ifp)
-		if_rele(ifp);
-
-	return error;
-}
-
-
-/* Process NETMAP_BDG_ATTACH */
-static int
-nm_bdg_ctl_attach(struct nmreq *nmr)
-{
-	struct netmap_adapter *na;
-	struct netmap_mem_d *nmd = NULL;
-	int error;
-
-	NMG_LOCK();
-
-	if (nmr->nr_arg2) {
-		nmd = netmap_mem_find(nmr->nr_arg2);
-		if (nmd == NULL) {
-			error = EINVAL;
-			goto unlock_exit;
-		}
-	}
-
-	error = netmap_get_bdg_na(nmr, &na, nmd, 1 /* create if not exists */);
-	if (error) /* no device */
-		goto unlock_exit;
-
-	if (na == NULL) { /* VALE prefix missing */
-		error = EINVAL;
-		goto unlock_exit;
-	}
-
-	if (NETMAP_OWNED_BY_ANY(na)) {
-		error = EBUSY;
-		goto unref_exit;
-	}
-
-	if (na->nm_bdg_ctl) {
-		/* nop for VALE ports. The bwrap needs to put the hwna
-		 * in netmap mode (see netmap_bwrap_bdg_ctl)
-		 */
-		error = na->nm_bdg_ctl(na, nmr, 1);
-		if (error)
-			goto unref_exit;
-		ND("registered %s to netmap-mode", na->name);
-	}
-	NMG_UNLOCK();
-	return 0;
-
-unref_exit:
-	netmap_adapter_put(na);
-unlock_exit:
-	NMG_UNLOCK();
-	return error;
-}
-
-static inline int
-nm_is_bwrap(struct netmap_adapter *na)
-{
-	return na->nm_register == netmap_bwrap_reg;
-}
-
-/* process NETMAP_BDG_DETACH */
-static int
-nm_bdg_ctl_detach(struct nmreq *nmr)
-{
-	struct netmap_adapter *na;
-	int error;
-
-	NMG_LOCK();
-	error = netmap_get_bdg_na(nmr, &na, NULL, 0 /* don't create */);
-	if (error) { /* no device, or another bridge or user owns the device */
-		goto unlock_exit;
-	}
-
-	if (na == NULL) { /* VALE prefix missing */
-		error = EINVAL;
-		goto unlock_exit;
-	} else if (nm_is_bwrap(na) &&
-		   ((struct netmap_bwrap_adapter *)na)->na_polling_state) {
-		/* Don't detach a NIC with polling */
-		error = EBUSY;
-		netmap_adapter_put(na);
-		goto unlock_exit;
-	}
-	if (na->nm_bdg_ctl) {
-		/* remove the port from bridge. The bwrap
-		 * also needs to put the hwna in normal mode
-		 */
-		error = na->nm_bdg_ctl(na, nmr, 0);
-	}
-
-	netmap_adapter_put(na);
-unlock_exit:
-	NMG_UNLOCK();
-	return error;
-
-}
-
-struct nm_bdg_polling_state;
-struct
-nm_bdg_kthread {
-	struct nm_kthread *nmk;
-	u_int qfirst;
-	u_int qlast;
-	struct nm_bdg_polling_state *bps;
-};
-
-struct nm_bdg_polling_state {
-	bool configured;
-	bool stopped;
-	struct netmap_bwrap_adapter *bna;
-	u_int reg;
-	u_int qfirst;
-	u_int qlast;
-	u_int cpu_from;
-	u_int ncpus;
-	struct nm_bdg_kthread *kthreads;
-};
-
-static void
-netmap_bwrap_polling(void *data)
-{
-	struct nm_bdg_kthread *nbk = data;
-	struct netmap_bwrap_adapter *bna;
-	u_int qfirst, qlast, i;
-	struct netmap_kring *kring0, *kring;
-
-	if (!nbk)
-		return;
-	qfirst = nbk->qfirst;
-	qlast = nbk->qlast;
-	bna = nbk->bps->bna;
-	kring0 = NMR(bna->hwna, NR_RX);
-
-	for (i = qfirst; i < qlast; i++) {
-		kring = kring0 + i;
-		kring->nm_notify(kring, 0);
-	}
-}
-
-static int
-nm_bdg_create_kthreads(struct nm_bdg_polling_state *bps)
-{
-	struct nm_kthread_cfg kcfg;
-	int i, j;
-
-	bps->kthreads = nm_os_malloc(sizeof(struct nm_bdg_kthread) * bps->ncpus);
-	if (bps->kthreads == NULL)
-		return ENOMEM;
-
-	bzero(&kcfg, sizeof(kcfg));
-	kcfg.worker_fn = netmap_bwrap_polling;
-	for (i = 0; i < bps->ncpus; i++) {
-		struct nm_bdg_kthread *t = bps->kthreads + i;
-		int all = (bps->ncpus == 1 && bps->reg == NR_REG_ALL_NIC);
-		int affinity = bps->cpu_from + i;
-
-		t->bps = bps;
-		t->qfirst = all ? bps->qfirst /* must be 0 */: affinity;
-		t->qlast = all ? bps->qlast : t->qfirst + 1;
-		D("kthread %d a:%u qf:%u ql:%u", i, affinity, t->qfirst,
-			t->qlast);
-
-		kcfg.type = i;
-		kcfg.worker_private = t;
-		t->nmk = nm_os_kthread_create(&kcfg, 0, NULL);
-		if (t->nmk == NULL) {
-			goto cleanup;
-		}
-		nm_os_kthread_set_affinity(t->nmk, affinity);
-	}
-	return 0;
-
-cleanup:
-	for (j = 0; j < i; j++) {
-		struct nm_bdg_kthread *t = bps->kthreads + i;
-		nm_os_kthread_delete(t->nmk);
-	}
-	nm_os_free(bps->kthreads);
-	return EFAULT;
-}
-
-/* a version of ptnetmap_start_kthreads() */
-static int
-nm_bdg_polling_start_kthreads(struct nm_bdg_polling_state *bps)
-{
-	int error, i, j;
-
-	if (!bps) {
-		D("polling is not configured");
-		return EFAULT;
-	}
-	bps->stopped = false;
-
-	for (i = 0; i < bps->ncpus; i++) {
-		struct nm_bdg_kthread *t = bps->kthreads + i;
-		error = nm_os_kthread_start(t->nmk);
-		if (error) {
-			D("error in nm_kthread_start()");
-			goto cleanup;
-		}
-	}
-	return 0;
-
-cleanup:
-	for (j = 0; j < i; j++) {
-		struct nm_bdg_kthread *t = bps->kthreads + i;
-		nm_os_kthread_stop(t->nmk);
-	}
-	bps->stopped = true;
-	return error;
-}
-
-static void
-nm_bdg_polling_stop_delete_kthreads(struct nm_bdg_polling_state *bps)
-{
-	int i;
-
-	if (!bps)
-		return;
-
-	for (i = 0; i < bps->ncpus; i++) {
-		struct nm_bdg_kthread *t = bps->kthreads + i;
-		nm_os_kthread_stop(t->nmk);
-		nm_os_kthread_delete(t->nmk);
-	}
-	bps->stopped = true;
-}
-
-static int
-get_polling_cfg(struct nmreq *nmr, struct netmap_adapter *na,
-			struct nm_bdg_polling_state *bps)
-{
-	int req_cpus, avail_cpus, core_from;
-	u_int reg, i, qfirst, qlast;
-
-	avail_cpus = nm_os_ncpus();
-	req_cpus = nmr->nr_arg1;
-
-	if (req_cpus == 0) {
-		D("req_cpus must be > 0");
-		return EINVAL;
-	} else if (req_cpus >= avail_cpus) {
-		D("for safety, we need at least one core left in the system");
-		return EINVAL;
-	}
-	reg = nmr->nr_flags & NR_REG_MASK;
-	i = nmr->nr_ringid & NETMAP_RING_MASK;
-	/*
-	 * ONE_NIC: dedicate one core to one ring. If multiple cores
-	 *          are specified, consecutive rings are also polled.
-	 *          For example, if ringid=2 and 2 cores are given,
-	 *          ring 2 and 3 are polled by core 2 and 3, respectively.
-	 * ALL_NIC: poll all the rings using a core specified by ringid.
-	 *          the number of cores must be 1.
-	 */
-	if (reg == NR_REG_ONE_NIC) {
-		if (i + req_cpus > nma_get_nrings(na, NR_RX)) {
-			D("only %d rings exist (ring %u-%u is given)",
-				nma_get_nrings(na, NR_RX), i, i+req_cpus);
-			return EINVAL;
-		}
-		qfirst = i;
-		qlast = qfirst + req_cpus;
-		core_from = qfirst;
-	} else if (reg == NR_REG_ALL_NIC) {
-		if (req_cpus != 1) {
-			D("ncpus must be 1 not %d for REG_ALL_NIC", req_cpus);
-			return EINVAL;
-		}
-		qfirst = 0;
-		qlast = nma_get_nrings(na, NR_RX);
-		core_from = i;
-	} else {
-		D("reg must be ALL_NIC or ONE_NIC");
-		return EINVAL;
-	}
-
-	bps->reg = reg;
-	bps->qfirst = qfirst;
-	bps->qlast = qlast;
-	bps->cpu_from = core_from;
-	bps->ncpus = req_cpus;
-	D("%s qfirst %u qlast %u cpu_from %u ncpus %u",
-		reg == NR_REG_ALL_NIC ? "REG_ALL_NIC" : "REG_ONE_NIC",
-		qfirst, qlast, core_from, req_cpus);
-	return 0;
-}
-
-static int
-nm_bdg_ctl_polling_start(struct nmreq *nmr, struct netmap_adapter *na)
-{
-	struct nm_bdg_polling_state *bps;
-	struct netmap_bwrap_adapter *bna;
-	int error;
-
-	bna = (struct netmap_bwrap_adapter *)na;
-	if (bna->na_polling_state) {
-		D("ERROR adapter already in polling mode");
-		return EFAULT;
-	}
-
-	bps = nm_os_malloc(sizeof(*bps));
-	if (!bps)
-		return ENOMEM;
-	bps->configured = false;
-	bps->stopped = true;
-
-	if (get_polling_cfg(nmr, na, bps)) {
-		nm_os_free(bps);
-		return EINVAL;
-	}
-
-	if (nm_bdg_create_kthreads(bps)) {
-		nm_os_free(bps);
-		return EFAULT;
-	}
-
-	bps->configured = true;
-	bna->na_polling_state = bps;
-	bps->bna = bna;
-
-	/* disable interrupt if possible */
-	if (bna->hwna->nm_intr)
-		bna->hwna->nm_intr(bna->hwna, 0);
-	/* start kthread now */
-	error = nm_bdg_polling_start_kthreads(bps);
-	if (error) {
-		D("ERROR nm_bdg_polling_start_kthread()");
-		nm_os_free(bps->kthreads);
-		nm_os_free(bps);
-		bna->na_polling_state = NULL;
-		if (bna->hwna->nm_intr)
-			bna->hwna->nm_intr(bna->hwna, 1);
-	}
-	return error;
-}
-
-static int
-nm_bdg_ctl_polling_stop(struct nmreq *nmr, struct netmap_adapter *na)
-{
-	struct netmap_bwrap_adapter *bna = (struct netmap_bwrap_adapter *)na;
-	struct nm_bdg_polling_state *bps;
-
-	if (!bna->na_polling_state) {
-		D("ERROR adapter is not in polling mode");
-		return EFAULT;
-	}
-	bps = bna->na_polling_state;
-	nm_bdg_polling_stop_delete_kthreads(bna->na_polling_state);
-	bps->configured = false;
-	nm_os_free(bps);
-	bna->na_polling_state = NULL;
-	/* reenable interrupt */
-	if (bna->hwna->nm_intr)
-		bna->hwna->nm_intr(bna->hwna, 1);
-	return 0;
-}
-
-/* Called by either user's context (netmap_ioctl())
- * or external kernel modules (e.g., Openvswitch).
- * Operation is indicated in nmr->nr_cmd.
- * NETMAP_BDG_OPS that sets configure/lookup/dtor functions to the bridge
- * requires bdg_ops argument; the other commands ignore this argument.
- *
- * Called without NMG_LOCK.
- */
-int
-netmap_bdg_ctl(struct nmreq *nmr, struct netmap_bdg_ops *bdg_ops)
-{
-	struct nm_bridge *b, *bridges;
-	struct netmap_adapter *na;
-	struct netmap_vp_adapter *vpna;
-	char *name = nmr->nr_name;
-	int cmd = nmr->nr_cmd, namelen = strlen(name);
-	int error = 0, i, j;
-	u_int num_bridges;
-
-	netmap_bns_getbridges(&bridges, &num_bridges);
-
-	switch (cmd) {
-	case NETMAP_BDG_NEWIF:
-		error = netmap_vi_create(nmr, 0 /* no autodelete */);
-		break;
-
-	case NETMAP_BDG_DELIF:
-		error = nm_vi_destroy(nmr->nr_name);
-		break;
-
-	case NETMAP_BDG_ATTACH:
-		error = nm_bdg_ctl_attach(nmr);
-		break;
-
-	case NETMAP_BDG_DETACH:
-		error = nm_bdg_ctl_detach(nmr);
-		break;
-
-	case NETMAP_BDG_LIST:
-		/* this is used to enumerate bridges and ports */
-		if (namelen) { /* look up indexes of bridge and port */
-			if (strncmp(name, NM_BDG_NAME, strlen(NM_BDG_NAME))) {
-				error = EINVAL;
-				break;
-			}
-			NMG_LOCK();
-			b = nm_find_bridge(name, 0 /* don't create */);
-			if (!b) {
-				error = ENOENT;
-				NMG_UNLOCK();
-				break;
-			}
-
-			error = 0;
-			nmr->nr_arg1 = b - bridges; /* bridge index */
-			nmr->nr_arg2 = NM_BDG_NOPORT;
-			for (j = 0; j < b->bdg_active_ports; j++) {
-				i = b->bdg_port_index[j];
-				vpna = b->bdg_ports[i];
-				if (vpna == NULL) {
-					D("---AAAAAAAAARGH-------");
-					continue;
-				}
-				/* the former and the latter identify a
-				 * virtual port and a NIC, respectively
-				 */
-				if (!strcmp(vpna->up.name, name)) {
-					nmr->nr_arg2 = i; /* port index */
-					break;
-				}
-			}
-			NMG_UNLOCK();
-		} else {
-			/* return the first non-empty entry starting from
-			 * bridge nr_arg1 and port nr_arg2.
-			 *
-			 * Users can detect the end of the same bridge by
-			 * seeing the new and old value of nr_arg1, and can
-			 * detect the end of all the bridge by error != 0
-			 */
-			i = nmr->nr_arg1;
-			j = nmr->nr_arg2;
-
-			NMG_LOCK();
-			for (error = ENOENT; i < NM_BRIDGES; i++) {
-				b = bridges + i;
-				for ( ; j < NM_BDG_MAXPORTS; j++) {
-					if (b->bdg_ports[j] == NULL)
-						continue;
-					vpna = b->bdg_ports[j];
-					strncpy(name, vpna->up.name, (size_t)IFNAMSIZ);
-					error = 0;
-					goto out;
-				}
-				j = 0; /* following bridges scan from 0 */
-			}
-		out:
-			nmr->nr_arg1 = i;
-			nmr->nr_arg2 = j;
-			NMG_UNLOCK();
-		}
-		break;
-
-	case NETMAP_BDG_REGOPS: /* XXX this should not be available from userspace */
-		/* register callbacks to the given bridge.
-		 * nmr->nr_name may be just bridge's name (including ':'
-		 * if it is not just NM_NAME).
-		 */
-		if (!bdg_ops) {
-			error = EINVAL;
-			break;
-		}
-		NMG_LOCK();
-		b = nm_find_bridge(name, 0 /* don't create */);
-		if (!b) {
-			error = EINVAL;
-		} else {
-			b->bdg_ops = *bdg_ops;
-		}
-		NMG_UNLOCK();
-		break;
-
-	case NETMAP_BDG_VNET_HDR:
-		/* Valid lengths for the virtio-net header are 0 (no header),
-		   10 and 12. */
-		if (nmr->nr_arg1 != 0 &&
-			nmr->nr_arg1 != sizeof(struct nm_vnet_hdr) &&
-				nmr->nr_arg1 != 12) {
-			error = EINVAL;
-			break;
-		}
-		NMG_LOCK();
-		error = netmap_get_bdg_na(nmr, &na, NULL, 0);
-		if (na && !error) {
-			vpna = (struct netmap_vp_adapter *)na;
-			na->virt_hdr_len = nmr->nr_arg1;
-			if (na->virt_hdr_len) {
-				vpna->mfs = NETMAP_BUF_SIZE(na);
-			}
-			D("Using vnet_hdr_len %d for %p", na->virt_hdr_len, na);
-			netmap_adapter_put(na);
-		} else if (!na) {
-			error = ENXIO;
-		}
-		NMG_UNLOCK();
-		break;
-
-	case NETMAP_BDG_POLLING_ON:
-	case NETMAP_BDG_POLLING_OFF:
-		NMG_LOCK();
-		error = netmap_get_bdg_na(nmr, &na, NULL, 0);
-		if (na && !error) {
-			if (!nm_is_bwrap(na)) {
-				error = EOPNOTSUPP;
-			} else if (cmd == NETMAP_BDG_POLLING_ON) {
-				error = nm_bdg_ctl_polling_start(nmr, na);
-				if (!error)
-					netmap_adapter_get(na);
-			} else {
-				error = nm_bdg_ctl_polling_stop(nmr, na);
-				if (!error)
-					netmap_adapter_put(na);
-			}
-			netmap_adapter_put(na);
-		}
-		NMG_UNLOCK();
-		break;
-
-	default:
-		D("invalid cmd (nmr->nr_cmd) (0x%x)", cmd);
-		error = EINVAL;
-		break;
-	}
-	return error;
-}
-
-int
-netmap_bdg_config(struct nmreq *nmr)
-{
-	struct nm_bridge *b;
-	int error = EINVAL;
-
-	NMG_LOCK();
-	b = nm_find_bridge(nmr->nr_name, 0);
-	if (!b) {
-		NMG_UNLOCK();
-		return error;
-	}
-	NMG_UNLOCK();
-	/* Don't call config() with NMG_LOCK() held */
-	BDG_RLOCK(b);
-	if (b->bdg_ops.config != NULL)
-		error = b->bdg_ops.config((struct nm_ifreq *)nmr);
-	BDG_RUNLOCK(b);
-	return error;
-}
-
-
-/* nm_krings_create callback for VALE ports.
- * Calls the standard netmap_krings_create, then adds leases on rx
- * rings and bdgfwd on tx rings.
- */
-static int
-netmap_vp_krings_create(struct netmap_adapter *na)
-{
-	u_int tailroom;
-	int error, i;
-	uint32_t *leases;
-	u_int nrx = netmap_real_rings(na, NR_RX);
-
-	/*
-	 * Leases are attached to RX rings on vale ports
-	 */
-	tailroom = sizeof(uint32_t) * na->num_rx_desc * nrx;
-
-	error = netmap_krings_create(na, tailroom);
-	if (error)
-		return error;
-
-	leases = na->tailroom;
-
-	for (i = 0; i < nrx; i++) { /* Receive rings */
-		na->rx_rings[i].nkr_leases = leases;
-		leases += na->num_rx_desc;
-	}
-
-	error = nm_alloc_bdgfwd(na);
-	if (error) {
-		netmap_krings_delete(na);
-		return error;
-	}
-
-	return 0;
-}
-
-
-/* nm_krings_delete callback for VALE ports. */
-static void
-netmap_vp_krings_delete(struct netmap_adapter *na)
-{
-	nm_free_bdgfwd(na);
-	netmap_krings_delete(na);
-}
-
-
-static int
-nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n,
-	struct netmap_vp_adapter *na, u_int ring_nr);
-
-
-/*
- * main dispatch routine for the bridge.
- * Grab packets from a kring, move them into the ft structure
- * associated to the tx (input) port. Max one instance per port,
- * filtered on input (ioctl, poll or XXX).
- * Returns the next position in the ring.
- */
-static int
-nm_bdg_preflush(struct netmap_kring *kring, u_int end)
-{
-	struct netmap_vp_adapter *na =
-		(struct netmap_vp_adapter*)kring->na;
-	struct netmap_ring *ring = kring->ring;
-	struct nm_bdg_fwd *ft;
-	u_int ring_nr = kring->ring_id;
-	u_int j = kring->nr_hwcur, lim = kring->nkr_num_slots - 1;
-	u_int ft_i = 0;	/* start from 0 */
-	u_int frags = 1; /* how many frags ? */
-	struct nm_bridge *b = na->na_bdg;
+	struct netmap_vp_adapter *na =
+		(struct netmap_vp_adapter*)kring->na;
+	struct netmap_ring *ring = kring->ring;
+	struct nm_bdg_fwd *ft;
+	u_int ring_nr = kring->ring_id;
+	u_int j = kring->nr_hwcur, lim = kring->nkr_num_slots - 1;
+	u_int ft_i = 0;	/* start from 0 */
+	u_int frags = 1; /* how many frags ? */
+	struct nm_bridge *b = na->na_bdg;
 
 	/* To protect against modifications to the bridge we acquire a
 	 * shared lock, waiting if we can sleep (if the source port is
 	 * attached to a user process) or with a trylock otherwise (NICs).
 	 */
-	ND("wait rlock for %d packets", ((j > end ? lim+1 : 0) + end) - j);
+	nm_prdis("wait rlock for %d packets", ((j > end ? lim+1 : 0) + end) - j);
 	if (na->up.na_flags & NAF_BDG_MAYSLEEP)
 		BDG_RLOCK(b);
 	else if (!BDG_RTRYLOCK(b))
-		return 0;
-	ND(5, "rlock acquired for %d packets", ((j > end ? lim+1 : 0) + end) - j);
+		return j;
+	nm_prdis(5, "rlock acquired for %d packets", ((j > end ? lim+1 : 0) + end) - j);
 	ft = kring->nkr_ft;
 
 	for (; likely(j != end); j = nm_next(j, lim)) {
@@ -1471,17 +509,19 @@ nm_bdg_preflush(struct netmap_kring *kring, u_int end)
 
 		ft[ft_i].ft_len = slot->len;
 		ft[ft_i].ft_flags = slot->flags;
+		ft[ft_i].ft_offset = 0;
 
-		ND("flags is 0x%x", slot->flags);
+		nm_prdis("flags is 0x%x", slot->flags);
 		/* we do not use the buf changed flag, but we still need to reset it */
 		slot->flags &= ~NS_BUF_CHANGED;
 
 		/* this slot goes into a list so initialize the link field */
 		ft[ft_i].ft_next = NM_FT_NULL;
 		buf = ft[ft_i].ft_buf = (slot->flags & NS_INDIRECT) ?
-			(void *)(uintptr_t)slot->ptr : NMB(&na->up, slot);
-		if (unlikely(buf == NULL)) {
-			RD(5, "NULL %s buffer pointer from %s slot %d len %d",
+			(void *)(uintptr_t)slot->ptr : NMB_O(kring, slot);
+		if (unlikely(buf == NULL ||
+		     slot->len > NETMAP_BUF_SIZE(&na->up) - nm_get_offset(kring, slot))) {
+			nm_prlim(5, "NULL %s buffer pointer from %s slot %d len %d",
 				(slot->flags & NS_INDIRECT) ? "INDIRECT" : "DIRECT",
 				kring->name, j, ft[ft_i].ft_len);
 			buf = ft[ft_i].ft_buf = NETMAP_BUF_BASE(&na->up);
@@ -1495,11 +535,11 @@ nm_bdg_preflush(struct netmap_kring *kring, u_int end)
 			continue;
 		}
 		if (unlikely(netmap_verbose && frags > 1))
-			RD(5, "%d frags at %d", frags, ft_i - frags);
+			nm_prlim(5, "%d frags at %d", frags, ft_i - frags);
 		ft[ft_i - frags].ft_frags = frags;
 		frags = 1;
 		if (unlikely((int)ft_i >= bridge_batch))
-			ft_i = nm_bdg_flush(ft, ft_i, na, ring_nr);
+			ft_i = nm_vale_flush(ft, ft_i, na, ring_nr);
 	}
 	if (frags > 1) {
 		/* Here ft_i > 0, ft[ft_i-1].flags has NS_MOREFRAG, and we
@@ -1507,10 +547,10 @@ nm_bdg_preflush(struct netmap_kring *kring, u_int end)
 		frags--;
 		ft[ft_i - 1].ft_flags &= ~NS_MOREFRAG;
 		ft[ft_i - frags].ft_frags = frags;
-		D("Truncate incomplete fragment at %d (%d frags)", ft_i, frags);
+		nm_prlim(5, "Truncate incomplete fragment at %d (%d frags)", ft_i, frags);
 	}
 	if (ft_i)
-		ft_i = nm_bdg_flush(ft, ft_i, na, ring_nr);
+		ft_i = nm_vale_flush(ft, ft_i, na, ring_nr);
 	BDG_RUNLOCK(b);
 	return j;
 }
@@ -1526,114 +566,57 @@ nm_bdg_preflush(struct netmap_kring *kring, u_int end)
  */
 #define mix(a, b, c)                                                    \
 do {                                                                    \
-        a -= b; a -= c; a ^= (c >> 13);                                 \
-        b -= c; b -= a; b ^= (a << 8);                                  \
-        c -= a; c -= b; c ^= (b >> 13);                                 \
-        a -= b; a -= c; a ^= (c >> 12);                                 \
-        b -= c; b -= a; b ^= (a << 16);                                 \
-        c -= a; c -= b; c ^= (b >> 5);                                  \
-        a -= b; a -= c; a ^= (c >> 3);                                  \
-        b -= c; b -= a; b ^= (a << 10);                                 \
-        c -= a; c -= b; c ^= (b >> 15);                                 \
+	a -= b; a -= c; a ^= (c >> 13);                                 \
+	b -= c; b -= a; b ^= (a << 8);                                  \
+	c -= a; c -= b; c ^= (b >> 13);                                 \
+	a -= b; a -= c; a ^= (c >> 12);                                 \
+	b -= c; b -= a; b ^= (a << 16);                                 \
+	c -= a; c -= b; c ^= (b >> 5);                                  \
+	a -= b; a -= c; a ^= (c >> 3);                                  \
+	b -= c; b -= a; b ^= (a << 10);                                 \
+	c -= a; c -= b; c ^= (b >> 15);                                 \
 } while (/*CONSTCOND*/0)
 
 
 static __inline uint32_t
-nm_bridge_rthash(const uint8_t *addr)
+nm_vale_rthash(const uint8_t *addr)
 {
-        uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = 0; // hask key
+	uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = 0; // hash key
 
-        b += addr[5] << 8;
-        b += addr[4];
-        a += addr[3] << 24;
-        a += addr[2] << 16;
-        a += addr[1] << 8;
-        a += addr[0];
+	b += addr[5] << 8;
+	b += addr[4];
+	a += addr[3] << 24;
+	a += addr[2] << 16;
+	a += addr[1] << 8;
+	a += addr[0];
 
-        mix(a, b, c);
+	mix(a, b, c);
 #define BRIDGE_RTHASH_MASK	(NM_BDG_HASH-1)
-        return (c & BRIDGE_RTHASH_MASK);
+	return (c & BRIDGE_RTHASH_MASK);
 }
 
 #undef mix
 
 
-/* nm_register callback for VALE ports */
-static int
-netmap_vp_reg(struct netmap_adapter *na, int onoff)
-{
-	struct netmap_vp_adapter *vpna =
-		(struct netmap_vp_adapter*)na;
-	enum txrx t;
-	int i;
-
-	/* persistent ports may be put in netmap mode
-	 * before being attached to a bridge
-	 */
-	if (vpna->na_bdg)
-		BDG_WLOCK(vpna->na_bdg);
-	if (onoff) {
-		for_rx_tx(t) {
-			for (i = 0; i < nma_get_nrings(na, t) + 1; i++) {
-				struct netmap_kring *kring = &NMR(na, t)[i];
-
-				if (nm_kring_pending_on(kring))
-					kring->nr_mode = NKR_NETMAP_ON;
-			}
-		}
-		if (na->active_fds == 0)
-			na->na_flags |= NAF_NETMAP_ON;
-		 /* XXX on FreeBSD, persistent VALE ports should also
-		 * toggle IFCAP_NETMAP in na->ifp (2014-03-16)
-		 */
-	} else {
-		if (na->active_fds == 0)
-			na->na_flags &= ~NAF_NETMAP_ON;
-		for_rx_tx(t) {
-			for (i = 0; i < nma_get_nrings(na, t) + 1; i++) {
-				struct netmap_kring *kring = &NMR(na, t)[i];
-
-				if (nm_kring_pending_off(kring))
-					kring->nr_mode = NKR_NETMAP_OFF;
-			}
-		}
-	}
-	if (vpna->na_bdg)
-		BDG_WUNLOCK(vpna->na_bdg);
-	return 0;
-}
-
-
 /*
  * Lookup function for a learning bridge.
  * Update the hash table with the source address,
  * and then returns the destination port index, and the
  * ring in *dst_ring (at the moment, always use ring 0)
  */
-u_int
-netmap_bdg_learning(struct nm_bdg_fwd *ft, uint8_t *dst_ring,
-		struct netmap_vp_adapter *na)
+uint32_t
+netmap_vale_learning(struct nm_bdg_fwd *ft, uint8_t *dst_ring,
+		struct netmap_vp_adapter *na, void *private_data)
 {
-	uint8_t *buf = ft->ft_buf;
-	u_int buf_len = ft->ft_len;
-	struct nm_hash_ent *ht = na->na_bdg->ht;
+	uint8_t *buf = ((uint8_t *)ft->ft_buf) + ft->ft_offset;
+	u_int buf_len = ft->ft_len - ft->ft_offset;
+	struct nm_hash_ent *ht = private_data;
 	uint32_t sh, dh;
 	u_int dst, mysrc = na->bdg_port;
 	uint64_t smac, dmac;
 	uint8_t indbuf[12];
 
-	/* safety check, unfortunately we have many cases */
-	if (buf_len >= 14 + na->up.virt_hdr_len) {
-		/* virthdr + mac_hdr in the same slot */
-		buf += na->up.virt_hdr_len;
-		buf_len -= na->up.virt_hdr_len;
-	} else if (buf_len == na->up.virt_hdr_len && ft->ft_flags & NS_MOREFRAG) {
-		/* only header in first fragment */
-		ft++;
-		buf = ft->ft_buf;
-		buf_len = ft->ft_len;
-	} else {
-		RD(5, "invalid buf format, length %d", buf_len);
+	if (buf_len < 14) {
 		return NM_BDG_NOPORT;
 	}
 
@@ -1654,21 +637,20 @@ netmap_bdg_learning(struct nm_bdg_fwd *ft, uint8_t *dst_ring,
 	 */
 	if (((buf[6] & 1) == 0) && (na->last_smac != smac)) { /* valid src */
 		uint8_t *s = buf+6;
-		sh = nm_bridge_rthash(s); // XXX hash of source
+		sh = nm_vale_rthash(s); /* hash of source */
 		/* update source port forwarding entry */
 		na->last_smac = ht[sh].mac = smac;	/* XXX expire ? */
 		ht[sh].ports = mysrc;
-		if (netmap_verbose)
-		    D("src %02x:%02x:%02x:%02x:%02x:%02x on port %d",
+		if (netmap_debug & NM_DEBUG_VALE)
+		    nm_prinf("src %02x:%02x:%02x:%02x:%02x:%02x on port %d",
 			s[0], s[1], s[2], s[3], s[4], s[5], mysrc);
 	}
 	dst = NM_BDG_BROADCAST;
 	if ((buf[0] & 1) == 0) { /* unicast */
-		dh = nm_bridge_rthash(buf); // XXX hash of dst
+		dh = nm_vale_rthash(buf); /* hash of dst */
 		if (ht[dh].mac == dmac) {	/* found dst */
 			dst = ht[dh].ports;
 		}
-		/* XXX otherwise return NM_BDG_UNKNOWN ? */
 	}
 	return dst;
 }
@@ -1701,8 +683,9 @@ nm_kr_space(struct netmap_kring *k, int is_rx)
 		k->nr_tail >= k->nkr_num_slots ||
 		busy < 0 ||
 		busy >= k->nkr_num_slots) {
-		D("invalid kring, cur %d tail %d lease %d lease_idx %d lim %d",			k->nr_hwcur, k->nr_hwtail, k->nkr_hwlease,
-			k->nkr_lease_idx, k->nkr_num_slots);
+		nm_prerr("invalid kring, cur %d tail %d lease %d lease_idx %d lim %d",
+		    k->nr_hwcur, k->nr_hwtail, k->nkr_hwlease,
+		    k->nkr_lease_idx, k->nkr_num_slots);
 	}
 #endif
 	return space;
@@ -1724,24 +707,28 @@ nm_kr_lease(struct netmap_kring *k, u_int n, int is_rx)
 	k->nkr_leases[lease_idx] = NR_NOSLOT;
 	k->nkr_lease_idx = nm_next(lease_idx, lim);
 
+#ifdef CONFIG_NETMAP_DEBUG
 	if (n > nm_kr_space(k, is_rx)) {
-		D("invalid request for %d slots", n);
+		nm_prerr("invalid request for %d slots", n);
 		panic("x");
 	}
+#endif /* CONFIG NETMAP_DEBUG */
 	/* XXX verify that there are n slots */
 	k->nkr_hwlease += n;
 	if (k->nkr_hwlease > lim)
 		k->nkr_hwlease -= lim + 1;
 
+#ifdef CONFIG_NETMAP_DEBUG
 	if (k->nkr_hwlease >= k->nkr_num_slots ||
 		k->nr_hwcur >= k->nkr_num_slots ||
 		k->nr_hwtail >= k->nkr_num_slots ||
 		k->nkr_lease_idx >= k->nkr_num_slots) {
-		D("invalid kring %s, cur %d tail %d lease %d lease_idx %d lim %d",
+		nm_prerr("invalid kring %s, cur %d tail %d lease %d lease_idx %d lim %d",
 			k->na->name,
 			k->nr_hwcur, k->nr_hwtail, k->nkr_hwlease,
 			k->nkr_lease_idx, k->nkr_num_slots);
 	}
+#endif /* CONFIG_NETMAP_DEBUG */
 	return lease_idx;
 }
 
@@ -1751,10 +738,10 @@ nm_kr_lease(struct netmap_kring *k, u_int n, int is_rx)
  * number of ports, and lets us replace the learn and dispatch functions.
  */
 int
-nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
+nm_vale_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 		u_int ring_nr)
 {
-	struct nm_bdg_q *dst_ents, *brddst;
+	struct nm_vale_q *dst_ents, *brddst;
 	uint16_t num_dsts = 0, *dsts;
 	struct nm_bridge *b = na->na_bdg;
 	u_int i, me = na->bdg_port;
@@ -1765,27 +752,35 @@ nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 	 * queues per port plus one for the broadcast traffic.
 	 * Then we have an array of destination indexes.
 	 */
-	dst_ents = (struct nm_bdg_q *)(ft + NM_BDG_BATCH_MAX);
+	dst_ents = (struct nm_vale_q *)(ft + NM_BDG_BATCH_MAX);
 	dsts = (uint16_t *)(dst_ents + NM_BDG_MAXPORTS * NM_BDG_MAXRINGS + 1);
 
 	/* first pass: find a destination for each packet in the batch */
 	for (i = 0; likely(i < n); i += ft[i].ft_frags) {
 		uint8_t dst_ring = ring_nr; /* default, same ring as origin */
 		uint16_t dst_port, d_i;
-		struct nm_bdg_q *d;
+		struct nm_vale_q *d;
+		struct nm_bdg_fwd *start_ft = NULL;
+
+		nm_prdis("slot %d frags %d", i, ft[i].ft_frags);
 
-		ND("slot %d frags %d", i, ft[i].ft_frags);
-		/* Drop the packet if the virtio-net header is not into the first
-		   fragment nor at the very beginning of the second. */
-		if (unlikely(na->up.virt_hdr_len > ft[i].ft_len))
+		if (na->up.virt_hdr_len < ft[i].ft_len) {
+			ft[i].ft_offset = na->up.virt_hdr_len;
+			start_ft = &ft[i];
+		} else if (na->up.virt_hdr_len == ft[i].ft_len && ft[i].ft_flags & NS_MOREFRAG) {
+			ft[i].ft_offset = ft[i].ft_len;
+			start_ft = &ft[i+1];
+		} else {
+			/* Drop the packet if the virtio-net header is not into the first
+			 * fragment nor at the very beginning of the second.
+			 */
 			continue;
-		dst_port = b->bdg_ops.lookup(&ft[i], &dst_ring, na);
+		}
+		dst_port = b->bdg_ops.lookup(start_ft, &dst_ring, na, b->private_data);
 		if (netmap_verbose > 255)
-			RD(5, "slot %d port %d -> %d", i, me, dst_port);
-		if (dst_port == NM_BDG_NOPORT)
+			nm_prlim(5, "slot %d port %d -> %d", i, me, dst_port);
+		if (dst_port >= NM_BDG_NOPORT)
 			continue; /* this packet is identified to be dropped */
-		else if (unlikely(dst_port > NM_BDG_MAXPORTS))
-			continue;
 		else if (dst_port == NM_BDG_BROADCAST)
 			dst_ring = 0; /* broadcasts always go to ring 0 */
 		else if (unlikely(dst_port == me ||
@@ -1812,9 +807,6 @@ nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 	/*
 	 * Broadcast traffic goes to ring 0 on all destinations.
 	 * So we need to add these rings to the list of ports to scan.
-	 * XXX at the moment we scan all NM_BDG_MAXPORTS ports, which is
-	 * expensive. We should keep a compact list of active destinations
-	 * so we could shorten this loop.
 	 */
 	brddst = dst_ents + NM_BDG_BROADCAST * NM_BDG_MAXRINGS;
 	if (brddst->bq_head != NM_FT_NULL) {
@@ -1830,7 +822,7 @@ nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 		}
 	}
 
-	ND(5, "pass 1 done %d pkts %d dsts", n, num_dsts);
+	nm_prdis(5, "pass 1 done %d pkts %d dsts", n, num_dsts);
 	/* second pass: scan destinations */
 	for (i = 0; i < num_dsts; i++) {
 		struct netmap_vp_adapter *dst_na;
@@ -1839,13 +831,13 @@ nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 		u_int dst_nr, lim, j, d_i, next, brd_next;
 		u_int needed, howmany;
 		int retry = netmap_txsync_retry;
-		struct nm_bdg_q *d;
+		struct nm_vale_q *d;
 		uint32_t my_start = 0, lease_idx = 0;
 		int nrings;
 		int virt_hdr_mismatch = 0;
 
 		d_i = dsts[i];
-		ND("second pass %d port %d", i, d_i);
+		nm_prdis("second pass %d port %d", i, d_i);
 		d = dst_ents + d_i;
 		// XXX fix the division
 		dst_na = b->bdg_ports[d_i/NM_BDG_MAXRINGS];
@@ -1862,7 +854,7 @@ nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 		 * - when na is being deactivated but is still attached.
 		 */
 		if (unlikely(!nm_netmap_on(&dst_na->up))) {
-			ND("not in netmap mode!");
+			nm_prdis("not in netmap mode!");
 			goto cleanup;
 		}
 
@@ -1871,7 +863,7 @@ nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 		next = d->bq_head;
 		/* we need to reserve this many slots. If fewer are
 		 * available, some packets will be dropped.
-		 * Packets may have multiple fragments, so we may not use
+		 * Packets may have multiple fragments, so
 		 * there is a chance that we may not use all of the slots
 		 * we have claimed, so we will need to handle the leftover
 		 * ones when we regain the lock.
@@ -1879,10 +871,10 @@ nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 		needed = d->bq_len + brddst->bq_len;
 
 		if (unlikely(dst_na->up.virt_hdr_len != na->up.virt_hdr_len)) {
-                        if (netmap_verbose) {
-                            RD(3, "virt_hdr_mismatch, src %d dst %d", na->up.virt_hdr_len,
-                                  dst_na->up.virt_hdr_len);
-                        }
+			if (netmap_verbose) {
+				nm_prlim(3, "virt_hdr_mismatch, src %d dst %d", na->up.virt_hdr_len,
+						dst_na->up.virt_hdr_len);
+			}
 			/* There is a virtio-net header/offloadings mismatch between
 			 * source and destination. The slower mismatch datapath will
 			 * be used to cope with all the mismatches.
@@ -1899,27 +891,31 @@ nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 				 * TCPv4 we must account for ethernet header, IP header
 				 * and TCPv4 header).
 				 */
+				KASSERT(dst_na->mfs > 0, ("vpna->mfs is 0"));
 				needed = (needed * na->mfs) /
 						(dst_na->mfs - WORST_CASE_GSO_HEADER) + 1;
-				ND(3, "srcmtu=%u, dstmtu=%u, x=%u", na->mfs, dst_na->mfs, needed);
+				nm_prdis(3, "srcmtu=%u, dstmtu=%u, x=%u", na->mfs, dst_na->mfs, needed);
 			}
 		}
 
-		ND(5, "pass 2 dst %d is %x %s",
-			i, d_i, is_vp ? "virtual" : "nic/host");
+		nm_prdis(5, "pass 2 dst %d is %x %s",
+			i, d_i, nm_is_bwrap(&dst_na->up) ? "nic/host" : "virtual");
 		dst_nr = d_i & (NM_BDG_MAXRINGS-1);
 		nrings = dst_na->up.num_rx_rings;
 		if (dst_nr >= nrings)
 			dst_nr = dst_nr % nrings;
-		kring = &dst_na->up.rx_rings[dst_nr];
+		kring = dst_na->up.rx_rings[dst_nr];
 		ring = kring->ring;
+		/* the destination ring may have not been opened for RX */
+		if (unlikely(ring == NULL || kring->nr_mode != NKR_NETMAP_ON))
+			goto cleanup;
 		lim = kring->nkr_num_slots - 1;
 
 retry:
 
 		if (dst_na->retry && retry) {
 			/* try to get some free slot from the previous run */
-			kring->nm_notify(kring, 0);
+			kring->nm_notify(kring, NAF_FORCE_RECLAIM);
 			/* actually useful only for bwraps, since there
 			 * the notify will trigger a txsync on the hwna. VALE ports
 			 * have dst_na->retry == 0
@@ -1968,7 +964,7 @@ nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 			if (unlikely(cnt > howmany))
 			    break; /* no more space */
 			if (netmap_verbose && cnt > 1)
-				RD(5, "rx %d frags to %d", cnt, j);
+				nm_prlim(5, "rx %d frags to %d", cnt, j);
 			ft_end = ft_p + cnt;
 			if (unlikely(virt_hdr_mismatch)) {
 				bdg_mismatch_datapath(na, dst_na, ft_p, ring, &j, lim, &howmany);
@@ -1977,29 +973,47 @@ nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 				do {
 					char *dst, *src = ft_p->ft_buf;
 					size_t copy_len = ft_p->ft_len, dst_len = copy_len;
+					uintptr_t src_cb;
+					uint64_t dstoff = 0, dstoff_cb = 0;
+					int src_co, dst_co;
+					const uintptr_t mask = NM_BUF_ALIGN - 1;
 
 					slot = &ring->slot[j];
 					dst = NMB(&dst_na->up, slot);
+					src_cb = ((uintptr_t)src) & ~mask;
+					src_co = ((uintptr_t)src) & mask;
+					dstoff = nm_get_offset(kring, slot);
+					dstoff_cb = dstoff & ~mask;
+					dst_co = ((uintptr_t)(dst + dstoff)) & mask;
+					if (src_co < dst_co) {
+						dstoff_cb += NM_BUF_ALIGN;
+					}
+					dstoff = dstoff_cb + src_co;
+					copy_len += src_co;
 
-					ND("send [%d] %d(%d) bytes at %s:%d",
+					nm_prdis("send [%d] %d(%d) bytes at %s:%d",
 							i, (int)copy_len, (int)dst_len,
 							NM_IFPNAME(dst_ifp), j);
-					/* round to a multiple of 64 */
-					copy_len = (copy_len + 63) & ~63;
 
-					if (unlikely(copy_len > NETMAP_BUF_SIZE(&dst_na->up) ||
-						     copy_len > NETMAP_BUF_SIZE(&na->up))) {
-						RD(5, "invalid len %d, down to 64", (int)copy_len);
-						copy_len = dst_len = 64; // XXX
+					if (unlikely(dstoff > NETMAP_BUF_SIZE(&dst_na->up) ||
+				                     dst_len > NETMAP_BUF_SIZE(&dst_na->up) - dstoff)) {
+						nm_prlim(5, "dropping packet/fragment of len %zu, dest offset %llu",
+								dst_len, (unsigned long long)dstoff);
+						copy_len = dst_len = 0;
+						dstoff = nm_get_offset(kring, slot);
 					}
+
 					if (ft_p->ft_flags & NS_INDIRECT) {
 						if (copyin(src, dst, copy_len)) {
 							// invalid user pointer, pretend len is 0
 							dst_len = 0;
 						}
-					} else {
+					} else if (!src_co || kring->offset_mask) {
 						//memcpy(dst, src, copy_len);
-						pkt_copy(src, dst, (int)copy_len);
+						pkt_copy((char *)src_cb, dst + dstoff_cb, (int)copy_len);
+						nm_write_offset(kring, slot, dstoff);
+					} else {
+						memcpy(dst, src, copy_len);
 					}
 					slot->len = dst_len;
 					slot->flags = (cnt << 8)| NS_MOREFRAG;
@@ -2025,10 +1039,10 @@ nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 			 * i can recover the slots, otherwise must
 			 * fill them with 0 to mark empty packets.
 			 */
-			ND("leftover %d bufs", howmany);
+			nm_prdis("leftover %d bufs", howmany);
 			if (nm_next(lease_idx, lim) == kring->nkr_lease_idx) {
 			    /* yes i am the last one */
-			    ND("roll back nkr_hwlease to %d", j);
+			    nm_prdis("roll back nkr_hwlease to %d", j);
 			    kring->nkr_hwlease = j;
 			} else {
 			    while (howmany-- > 0) {
@@ -2087,7 +1101,7 @@ nm_bdg_flush(struct nm_bdg_fwd *ft, u_int n, struct netmap_vp_adapter *na,
 
 /* nm_txsync callback for VALE ports */
 static int
-netmap_vp_txsync(struct netmap_kring *kring, int flags)
+netmap_vale_vp_txsync(struct netmap_kring *kring, int flags)
 {
 	struct netmap_vp_adapter *na =
 		(struct netmap_vp_adapter *)kring->na;
@@ -2106,759 +1120,381 @@ netmap_vp_txsync(struct netmap_kring *kring, int flags)
 	if (bridge_batch > NM_BDG_BATCH)
 		bridge_batch = NM_BDG_BATCH;
 
-	done = nm_bdg_preflush(kring, head);
+	done = nm_vale_preflush(kring, head);
 done:
 	if (done != head)
-		D("early break at %d/ %d, tail %d", done, head, kring->nr_hwtail);
+		nm_prerr("early break at %d/ %d, tail %d", done, head, kring->nr_hwtail);
 	/*
 	 * packets between 'done' and 'cur' are left unsent.
 	 */
 	kring->nr_hwcur = done;
 	kring->nr_hwtail = nm_prev(done, lim);
-	if (netmap_verbose)
-		D("%s ring %d flags %d", na->up.name, kring->ring_id, flags);
-	return 0;
-}
-
-
-/* rxsync code used by VALE ports nm_rxsync callback and also
- * internally by the brwap
- */
-static int
-netmap_vp_rxsync_locked(struct netmap_kring *kring, int flags)
-{
-	struct netmap_adapter *na = kring->na;
-	struct netmap_ring *ring = kring->ring;
-	u_int nm_i, lim = kring->nkr_num_slots - 1;
-	u_int head = kring->rhead;
-	int n;
-
-	if (head > lim) {
-		D("ouch dangerous reset!!!");
-		n = netmap_ring_reinit(kring);
-		goto done;
-	}
-
-	/* First part, import newly received packets. */
-	/* actually nothing to do here, they are already in the kring */
-
-	/* Second part, skip past packets that userspace has released. */
-	nm_i = kring->nr_hwcur;
-	if (nm_i != head) {
-		/* consistency check, but nothing really important here */
-		for (n = 0; likely(nm_i != head); n++) {
-			struct netmap_slot *slot = &ring->slot[nm_i];
-			void *addr = NMB(na, slot);
-
-			if (addr == NETMAP_BUF_BASE(kring->na)) { /* bad buf */
-				D("bad buffer index %d, ignore ?",
-					slot->buf_idx);
-			}
-			slot->flags &= ~NS_BUF_CHANGED;
-			nm_i = nm_next(nm_i, lim);
-		}
-		kring->nr_hwcur = head;
-	}
-
-	n = 0;
-done:
-	return n;
-}
-
-/*
- * nm_rxsync callback for VALE ports
- * user process reading from a VALE switch.
- * Already protected against concurrent calls from userspace,
- * but we must acquire the queue's lock to protect against
- * writers on the same queue.
- */
-static int
-netmap_vp_rxsync(struct netmap_kring *kring, int flags)
-{
-	int n;
-
-	mtx_lock(&kring->q_lock);
-	n = netmap_vp_rxsync_locked(kring, flags);
-	mtx_unlock(&kring->q_lock);
-	return n;
-}
-
-
-/* nm_bdg_attach callback for VALE ports
- * The na_vp port is this same netmap_adapter. There is no host port.
- */
-static int
-netmap_vp_bdg_attach(const char *name, struct netmap_adapter *na)
-{
-	struct netmap_vp_adapter *vpna = (struct netmap_vp_adapter *)na;
-
-	if (vpna->na_bdg)
-		return EBUSY;
-	na->na_vp = vpna;
-	strncpy(na->name, name, sizeof(na->name));
-	na->na_hostvp = NULL;
-	return 0;
-}
-
-/* create a netmap_vp_adapter that describes a VALE port.
- * Only persistent VALE ports have a non-null ifp.
- */
-static int
-netmap_vp_create(struct nmreq *nmr, struct ifnet *ifp,
-		struct netmap_mem_d *nmd,
-		struct netmap_vp_adapter **ret)
-{
-	struct netmap_vp_adapter *vpna;
-	struct netmap_adapter *na;
-	int error = 0;
-	u_int npipes = 0;
-
-	vpna = nm_os_malloc(sizeof(*vpna));
-	if (vpna == NULL)
-		return ENOMEM;
-
- 	na = &vpna->up;
-
-	na->ifp = ifp;
-	strncpy(na->name, nmr->nr_name, sizeof(na->name));
-
-	/* bound checking */
-	na->num_tx_rings = nmr->nr_tx_rings;
-	nm_bound_var(&na->num_tx_rings, 1, 1, NM_BDG_MAXRINGS, NULL);
-	nmr->nr_tx_rings = na->num_tx_rings; // write back
-	na->num_rx_rings = nmr->nr_rx_rings;
-	nm_bound_var(&na->num_rx_rings, 1, 1, NM_BDG_MAXRINGS, NULL);
-	nmr->nr_rx_rings = na->num_rx_rings; // write back
-	nm_bound_var(&nmr->nr_tx_slots, NM_BRIDGE_RINGSIZE,
-			1, NM_BDG_MAXSLOTS, NULL);
-	na->num_tx_desc = nmr->nr_tx_slots;
-	nm_bound_var(&nmr->nr_rx_slots, NM_BRIDGE_RINGSIZE,
-			1, NM_BDG_MAXSLOTS, NULL);
-	/* validate number of pipes. We want at least 1,
-	 * but probably can do with some more.
-	 * So let's use 2 as default (when 0 is supplied)
-	 */
-	npipes = nmr->nr_arg1;
-	nm_bound_var(&npipes, 2, 1, NM_MAXPIPES, NULL);
-	nmr->nr_arg1 = npipes;	/* write back */
-	/* validate extra bufs */
-	nm_bound_var(&nmr->nr_arg3, 0, 0,
-			128*NM_BDG_MAXSLOTS, NULL);
-	na->num_rx_desc = nmr->nr_rx_slots;
-	vpna->mfs = 1514;
-	vpna->last_smac = ~0llu;
-	/*if (vpna->mfs > netmap_buf_size)  TODO netmap_buf_size is zero??
-		vpna->mfs = netmap_buf_size; */
-        if (netmap_verbose)
-		D("max frame size %u", vpna->mfs);
-
-	na->na_flags |= NAF_BDG_MAYSLEEP;
-	/* persistent VALE ports look like hw devices
-	 * with a native netmap adapter
-	 */
-	if (ifp)
-		na->na_flags |= NAF_NATIVE;
-	na->nm_txsync = netmap_vp_txsync;
-	na->nm_rxsync = netmap_vp_rxsync;
-	na->nm_register = netmap_vp_reg;
-	na->nm_krings_create = netmap_vp_krings_create;
-	na->nm_krings_delete = netmap_vp_krings_delete;
-	na->nm_dtor = netmap_vp_dtor;
-	D("nr_arg2 %d", nmr->nr_arg2);
-	na->nm_mem = nmd ?
-		netmap_mem_get(nmd):
-		netmap_mem_private_new(
-			na->num_tx_rings, na->num_tx_desc,
-			na->num_rx_rings, na->num_rx_desc,
-			nmr->nr_arg3, npipes, &error);
-	if (na->nm_mem == NULL)
-		goto err;
-	na->nm_bdg_attach = netmap_vp_bdg_attach;
-	/* other nmd fields are set in the common routine */
-	error = netmap_attach_common(na);
-	if (error)
-		goto err;
-	*ret = vpna;
-	return 0;
-
-err:
-	if (na->nm_mem != NULL)
-		netmap_mem_put(na->nm_mem);
-	nm_os_free(vpna);
-	return error;
-}
-
-/* Bridge wrapper code (bwrap).
- * This is used to connect a non-VALE-port netmap_adapter (hwna) to a
- * VALE switch.
- * The main task is to swap the meaning of tx and rx rings to match the
- * expectations of the VALE switch code (see nm_bdg_flush).
- *
- * The bwrap works by interposing a netmap_bwrap_adapter between the
- * rest of the system and the hwna. The netmap_bwrap_adapter looks like
- * a netmap_vp_adapter to the rest the system, but, internally, it
- * translates all callbacks to what the hwna expects.
- *
- * Note that we have to intercept callbacks coming from two sides:
- *
- *  - callbacks coming from the netmap module are intercepted by
- *    passing around the netmap_bwrap_adapter instead of the hwna
- *
- *  - callbacks coming from outside of the netmap module only know
- *    about the hwna. This, however, only happens in interrupt
- *    handlers, where only the hwna->nm_notify callback is called.
- *    What the bwrap does is to overwrite the hwna->nm_notify callback
- *    with its own netmap_bwrap_intr_notify.
- *    XXX This assumes that the hwna->nm_notify callback was the
- *    standard netmap_notify(), as it is the case for nic adapters.
- *    Any additional action performed by hwna->nm_notify will not be
- *    performed by netmap_bwrap_intr_notify.
- *
- * Additionally, the bwrap can optionally attach the host rings pair
- * of the wrapped adapter to a different port of the switch.
- */
-
-
-static void
-netmap_bwrap_dtor(struct netmap_adapter *na)
-{
-	struct netmap_bwrap_adapter *bna = (struct netmap_bwrap_adapter*)na;
-	struct netmap_adapter *hwna = bna->hwna;
-	struct nm_bridge *b = bna->up.na_bdg,
-		*bh = bna->host.na_bdg;
-
-	netmap_mem_put(bna->host.up.nm_mem);
-
-	if (b) {
-		netmap_bdg_detach_common(b, bna->up.bdg_port,
-			    (bh ? bna->host.bdg_port : -1));
-	}
-
-	ND("na %p", na);
-	na->ifp = NULL;
-	bna->host.up.ifp = NULL;
-	hwna->na_private = NULL;
-	hwna->na_vp = hwna->na_hostvp = NULL;
-	hwna->na_flags &= ~NAF_BUSY;
-	netmap_adapter_put(hwna);
-
-}
-
-
-/*
- * Intr callback for NICs connected to a bridge.
- * Simply ignore tx interrupts (maybe we could try to recover space ?)
- * and pass received packets from nic to the bridge.
- *
- * XXX TODO check locking: this is called from the interrupt
- * handler so we should make sure that the interface is not
- * disconnected while passing down an interrupt.
- *
- * Note, no user process can access this NIC or the host stack.
- * The only part of the ring that is significant are the slots,
- * and head/cur/tail are set from the kring as needed
- * (part as a receive ring, part as a transmit ring).
- *
- * callback that overwrites the hwna notify callback.
- * Packets come from the outside or from the host stack and are put on an
- * hwna rx ring.
- * The bridge wrapper then sends the packets through the bridge.
- */
-static int
-netmap_bwrap_intr_notify(struct netmap_kring *kring, int flags)
-{
-	struct netmap_adapter *na = kring->na;
-	struct netmap_bwrap_adapter *bna = na->na_private;
-	struct netmap_kring *bkring;
-	struct netmap_vp_adapter *vpna = &bna->up;
-	u_int ring_nr = kring->ring_id;
-	int ret = NM_IRQ_COMPLETED;
-	int error;
-
-	if (netmap_verbose)
-	    D("%s %s 0x%x", na->name, kring->name, flags);
-
-	bkring = &vpna->up.tx_rings[ring_nr];
-
-	/* make sure the ring is not disabled */
-	if (nm_kr_tryget(kring, 0 /* can't sleep */, NULL)) {
-		return EIO;
-	}
-
-	if (netmap_verbose)
-	    D("%s head %d cur %d tail %d",  na->name,
-		kring->rhead, kring->rcur, kring->rtail);
-
-	/* simulate a user wakeup on the rx ring
-	 * fetch packets that have arrived.
-	 */
-	error = kring->nm_sync(kring, 0);
-	if (error)
-		goto put_out;
-	if (kring->nr_hwcur == kring->nr_hwtail) {
-		if (netmap_verbose)
-			D("how strange, interrupt with no packets on %s",
-			    na->name);
-		goto put_out;
-	}
-
-	/* new packets are kring->rcur to kring->nr_hwtail, and the bkring
-	 * had hwcur == bkring->rhead. So advance bkring->rhead to kring->nr_hwtail
-	 * to push all packets out.
-	 */
-	bkring->rhead = bkring->rcur = kring->nr_hwtail;
-
-	netmap_vp_txsync(bkring, flags);
-
-	/* mark all buffers as released on this ring */
-	kring->rhead = kring->rcur = kring->rtail = kring->nr_hwtail;
-	/* another call to actually release the buffers */
-	error = kring->nm_sync(kring, 0);
-
-	/* The second rxsync may have further advanced hwtail. If this happens,
-	 *  return NM_IRQ_RESCHED, otherwise just return NM_IRQ_COMPLETED. */
-	if (kring->rcur != kring->nr_hwtail) {
-		ret = NM_IRQ_RESCHED;
-	}
-put_out:
-	nm_kr_put(kring);
-
-	return error ? error : ret;
-}
-
-
-/* nm_register callback for bwrap */
-static int
-netmap_bwrap_reg(struct netmap_adapter *na, int onoff)
-{
-	struct netmap_bwrap_adapter *bna =
-		(struct netmap_bwrap_adapter *)na;
-	struct netmap_adapter *hwna = bna->hwna;
-	struct netmap_vp_adapter *hostna = &bna->host;
-	int error, i;
-	enum txrx t;
-
-	ND("%s %s", na->name, onoff ? "on" : "off");
-
-	if (onoff) {
-		/* netmap_do_regif has been called on the bwrap na.
-		 * We need to pass the information about the
-		 * memory allocator down to the hwna before
-		 * putting it in netmap mode
-		 */
-		hwna->na_lut = na->na_lut;
-
-		if (hostna->na_bdg) {
-			/* if the host rings have been attached to switch,
-			 * we need to copy the memory allocator information
-			 * in the hostna also
-			 */
-			hostna->up.na_lut = na->na_lut;
-		}
-
-		/* cross-link the netmap rings
-		 * The original number of rings comes from hwna,
-		 * rx rings on one side equals tx rings on the other.
-		 */
-		for_rx_tx(t) {
-			enum txrx r = nm_txrx_swap(t); /* swap NR_TX <-> NR_RX */
-			for (i = 0; i < nma_get_nrings(hwna, r) + 1; i++) {
-				NMR(hwna, r)[i].ring = NMR(na, t)[i].ring;
-			}
-		}
-
-		if (na->na_flags & NAF_HOST_RINGS) {
-			struct netmap_adapter *hna = &hostna->up;
-			/* the hostna rings are the host rings of the bwrap.
-			 * The corresponding krings must point back to the
-			 * hostna
-			 */
-			hna->tx_rings = &na->tx_rings[na->num_tx_rings];
-			hna->tx_rings[0].na = hna;
-			hna->rx_rings = &na->rx_rings[na->num_rx_rings];
-			hna->rx_rings[0].na = hna;
-		}
-	}
+	if (netmap_debug & NM_DEBUG_TXSYNC)
+		nm_prinf("%s ring %d flags %d", na->up.name, kring->ring_id, flags);
+	return 0;
+}
 
-	/* pass down the pending ring state information */
-	for_rx_tx(t) {
-		for (i = 0; i < nma_get_nrings(na, t) + 1; i++)
-			NMR(hwna, t)[i].nr_pending_mode =
-				NMR(na, t)[i].nr_pending_mode;
-	}
 
-	/* forward the request to the hwna */
-	error = hwna->nm_register(hwna, onoff);
-	if (error)
-		return error;
+/* create a netmap_vp_adapter that describes a VALE port.
+ * Only persistent VALE ports have a non-null ifp.
+ */
+static int
+netmap_vale_vp_create(struct nmreq_header *hdr, if_t ifp,
+		struct netmap_mem_d *nmd, struct netmap_vp_adapter **ret)
+{
+	struct nmreq_register *req = (struct nmreq_register *)(uintptr_t)hdr->nr_body;
+	struct netmap_vp_adapter *vpna;
+	struct netmap_adapter *na;
+	int error = 0;
+	u_int npipes = 0;
+	u_int extrabufs = 0;
 
-	/* copy up the current ring state information */
-	for_rx_tx(t) {
-		for (i = 0; i < nma_get_nrings(na, t) + 1; i++)
-			NMR(na, t)[i].nr_mode =
-				NMR(hwna, t)[i].nr_mode;
+	if (hdr->nr_reqtype != NETMAP_REQ_REGISTER) {
+		return EINVAL;
 	}
 
-	/* impersonate a netmap_vp_adapter */
-	netmap_vp_reg(na, onoff);
-	if (hostna->na_bdg)
-		netmap_vp_reg(&hostna->up, onoff);
-
-	if (onoff) {
-		u_int i;
-		/* intercept the hwna nm_nofify callback on the hw rings */
-		for (i = 0; i < hwna->num_rx_rings; i++) {
-			hwna->rx_rings[i].save_notify = hwna->rx_rings[i].nm_notify;
-			hwna->rx_rings[i].nm_notify = netmap_bwrap_intr_notify;
-		}
-		i = hwna->num_rx_rings; /* for safety */
-		/* save the host ring notify unconditionally */
-		hwna->rx_rings[i].save_notify = hwna->rx_rings[i].nm_notify;
-		if (hostna->na_bdg) {
-			/* also intercept the host ring notify */
-			hwna->rx_rings[i].nm_notify = netmap_bwrap_intr_notify;
-		}
-		if (na->active_fds == 0)
-			na->na_flags |= NAF_NETMAP_ON;
-	} else {
-		u_int i;
+	vpna = nm_os_malloc(sizeof(*vpna));
+	if (vpna == NULL)
+		return ENOMEM;
 
-		if (na->active_fds == 0)
-			na->na_flags &= ~NAF_NETMAP_ON;
+ 	na = &vpna->up;
 
-		/* reset all notify callbacks (including host ring) */
-		for (i = 0; i <= hwna->num_rx_rings; i++) {
-			hwna->rx_rings[i].nm_notify = hwna->rx_rings[i].save_notify;
-			hwna->rx_rings[i].save_notify = NULL;
-		}
-		hwna->na_lut.lut = NULL;
-		hwna->na_lut.objtotal = 0;
-		hwna->na_lut.objsize = 0;
-	}
+	na->ifp = ifp;
+	strlcpy(na->name, hdr->nr_name, sizeof(na->name));
+
+	/* bound checking */
+	na->num_tx_rings = req->nr_tx_rings;
+	nm_bound_var(&na->num_tx_rings, 1, 1, NM_BDG_MAXRINGS, NULL);
+	req->nr_tx_rings = na->num_tx_rings; /* write back */
+	na->num_rx_rings = req->nr_rx_rings;
+	nm_bound_var(&na->num_rx_rings, 1, 1, NM_BDG_MAXRINGS, NULL);
+	req->nr_rx_rings = na->num_rx_rings; /* write back */
+	nm_bound_var(&req->nr_tx_slots, NM_BRIDGE_RINGSIZE,
+			1, NM_BDG_MAXSLOTS, NULL);
+	na->num_tx_desc = req->nr_tx_slots;
+	nm_bound_var(&req->nr_rx_slots, NM_BRIDGE_RINGSIZE,
+			1, NM_BDG_MAXSLOTS, NULL);
+	/* validate number of pipes. We want at least 1,
+	 * but probably can do with some more.
+	 * So let's use 2 as default (when 0 is supplied)
+	 */
+	nm_bound_var(&npipes, 2, 1, NM_MAXPIPES, NULL);
+	/* validate extra bufs */
+	extrabufs = req->nr_extra_bufs;
+	nm_bound_var(&extrabufs, 0, 0,
+			128*NM_BDG_MAXSLOTS, NULL);
+	req->nr_extra_bufs = extrabufs; /* write back */
+	na->num_rx_desc = req->nr_rx_slots;
+	/* Set the mfs to a default value, as it is needed on the VALE
+	 * mismatch datapath. XXX We should set it according to the MTU
+	 * known to the kernel. */
+	vpna->mfs = NM_BDG_MFS_DEFAULT;
+	vpna->last_smac = ~0llu;
+	/*if (vpna->mfs > netmap_buf_size)  TODO netmap_buf_size is zero??
+		vpna->mfs = netmap_buf_size; */
+	if (netmap_verbose)
+		nm_prinf("max frame size %u", vpna->mfs);
 
+	na->na_flags |= (NAF_BDG_MAYSLEEP | NAF_OFFSETS);
+	/* persistent VALE ports look like hw devices
+	 * with a native netmap adapter
+	 */
+	if (ifp)
+		na->na_flags |= NAF_NATIVE;
+	na->nm_txsync = netmap_vale_vp_txsync;
+	na->nm_rxsync = netmap_vp_rxsync; /* use the one provided by bdg */
+	na->nm_register = netmap_vp_reg;  /* use the one provided by bdg */
+	na->nm_krings_create = netmap_vale_vp_krings_create;
+	na->nm_krings_delete = netmap_vale_vp_krings_delete;
+	na->nm_dtor = netmap_vale_vp_dtor;
+	nm_prdis("nr_mem_id %d", req->nr_mem_id);
+	na->nm_mem = nmd ?
+		netmap_mem_get(nmd):
+		netmap_mem_private_new(
+			na->num_tx_rings, na->num_tx_desc,
+			na->num_rx_rings, na->num_rx_desc,
+			req->nr_extra_bufs, npipes, &error);
+	if (na->nm_mem == NULL)
+		goto err;
+	na->nm_bdg_attach = netmap_vale_vp_bdg_attach;
+	/* other nmd fields are set in the common routine */
+	error = netmap_attach_common(na);
+	if (error)
+		goto err;
+	*ret = vpna;
 	return 0;
+
+err:
+	if (na->nm_mem != NULL)
+		netmap_mem_put(na->nm_mem);
+	nm_os_free(vpna);
+	return error;
 }
 
-/* nm_config callback for bwrap */
+/* nm_bdg_attach callback for VALE ports
+ * The na_vp port is this same netmap_adapter. There is no host port.
+ */
 static int
-netmap_bwrap_config(struct netmap_adapter *na, u_int *txr, u_int *txd,
-				    u_int *rxr, u_int *rxd)
+netmap_vale_vp_bdg_attach(const char *name, struct netmap_adapter *na,
+		struct nm_bridge *b)
 {
-	struct netmap_bwrap_adapter *bna =
-		(struct netmap_bwrap_adapter *)na;
-	struct netmap_adapter *hwna = bna->hwna;
-
-	/* forward the request */
-	netmap_update_config(hwna);
-	/* swap the results */
-	*txr = hwna->num_rx_rings;
-	*txd = hwna->num_rx_desc;
-	*rxr = hwna->num_tx_rings;
-	*rxd = hwna->num_rx_desc;
+	struct netmap_vp_adapter *vpna = (struct netmap_vp_adapter *)na;
 
+	if ((b->bdg_flags & NM_BDG_NEED_BWRAP) || vpna->na_bdg) {
+		return NM_NEED_BWRAP;
+	}
+	na->na_vp = vpna;
+	strlcpy(na->name, name, sizeof(na->name));
+	na->na_hostvp = NULL;
 	return 0;
 }
 
-
-/* nm_krings_create callback for bwrap */
 static int
-netmap_bwrap_krings_create(struct netmap_adapter *na)
+netmap_vale_bwrap_krings_create(struct netmap_adapter *na)
 {
-	struct netmap_bwrap_adapter *bna =
-		(struct netmap_bwrap_adapter *)na;
-	struct netmap_adapter *hwna = bna->hwna;
-	int i, error = 0;
-	enum txrx t;
-
-	ND("%s", na->name);
+	int error;
 
 	/* impersonate a netmap_vp_adapter */
-	error = netmap_vp_krings_create(na);
+	error = netmap_vale_vp_krings_create(na);
 	if (error)
 		return error;
-
-	/* also create the hwna krings */
-	error = hwna->nm_krings_create(hwna);
+	error = netmap_bwrap_krings_create_common(na);
 	if (error) {
-		goto err_del_vp_rings;
-	}
-
-	/* get each ring slot number from the corresponding hwna ring */
-	for_rx_tx(t) {
-		enum txrx r = nm_txrx_swap(t); /* swap NR_TX <-> NR_RX */
-		for (i = 0; i < nma_get_nrings(hwna, r) + 1; i++) {
-			NMR(na, t)[i].nkr_num_slots = NMR(hwna, r)[i].nkr_num_slots;
-		}
+		netmap_vale_vp_krings_delete(na);
 	}
-
-	return 0;
-
-err_del_vp_rings:
-	netmap_vp_krings_delete(na);
-
 	return error;
 }
 
-
 static void
-netmap_bwrap_krings_delete(struct netmap_adapter *na)
+netmap_vale_bwrap_krings_delete(struct netmap_adapter *na)
 {
-	struct netmap_bwrap_adapter *bna =
-		(struct netmap_bwrap_adapter *)na;
-	struct netmap_adapter *hwna = bna->hwna;
-
-	ND("%s", na->name);
-
-	hwna->nm_krings_delete(hwna);
-	netmap_vp_krings_delete(na);
+	netmap_bwrap_krings_delete_common(na);
+	netmap_vale_vp_krings_delete(na);
 }
 
-
-/* notify method for the bridge-->hwna direction */
 static int
-netmap_bwrap_notify(struct netmap_kring *kring, int flags)
+netmap_vale_bwrap_attach(const char *nr_name, struct netmap_adapter *hwna)
 {
-	struct netmap_adapter *na = kring->na;
-	struct netmap_bwrap_adapter *bna = na->na_private;
-	struct netmap_adapter *hwna = bna->hwna;
-	u_int ring_n = kring->ring_id;
-	u_int lim = kring->nkr_num_slots - 1;
-	struct netmap_kring *hw_kring;
+	struct netmap_bwrap_adapter *bna;
+	struct netmap_adapter *na = NULL;
+	struct netmap_adapter *hostna = NULL;
 	int error;
 
-	ND("%s: na %s hwna %s",
-			(kring ? kring->name : "NULL!"),
-			(na ? na->name : "NULL!"),
-			(hwna ? hwna->name : "NULL!"));
-	hw_kring = &hwna->tx_rings[ring_n];
+	bna = nm_os_malloc(sizeof(*bna));
+	if (bna == NULL) {
+		return ENOMEM;
+	}
+	na = &bna->up.up;
+	strlcpy(na->name, nr_name, sizeof(na->name));
+	na->nm_register = netmap_bwrap_reg;
+	na->nm_txsync = netmap_vale_vp_txsync;
+	// na->nm_rxsync = netmap_bwrap_rxsync;
+	na->nm_krings_create = netmap_vale_bwrap_krings_create;
+	na->nm_krings_delete = netmap_vale_bwrap_krings_delete;
+	na->nm_notify = netmap_bwrap_notify;
+	bna->nm_intr_notify = netmap_bwrap_intr_notify;
+	bna->up.retry = 1; /* XXX maybe this should depend on the hwna */
+	/* Set the mfs, needed on the VALE mismatch datapath. */
+	bna->up.mfs = NM_BDG_MFS_DEFAULT;
+
+	if (hwna->na_flags & NAF_HOST_RINGS) {
+		hostna = &bna->host.up;
+		hostna->nm_notify = netmap_bwrap_notify;
+		bna->host.mfs = NM_BDG_MFS_DEFAULT;
+	}
 
-	if (nm_kr_tryget(hw_kring, 0, NULL)) {
-		return ENXIO;
+	error = netmap_bwrap_attach_common(na, hwna);
+	if (error) {
+		nm_os_free(bna);
 	}
+	return error;
+}
 
-	/* first step: simulate a user wakeup on the rx ring */
-	netmap_vp_rxsync(kring, flags);
-	ND("%s[%d] PRE rx(c%3d t%3d l%3d) ring(h%3d c%3d t%3d) tx(c%3d ht%3d t%3d)",
-		na->name, ring_n,
-		kring->nr_hwcur, kring->nr_hwtail, kring->nkr_hwlease,
-		ring->head, ring->cur, ring->tail,
-		hw_kring->nr_hwcur, hw_kring->nr_hwtail, hw_ring->rtail);
-	/* second step: the new packets are sent on the tx ring
-	 * (which is actually the same ring)
-	 */
-	hw_kring->rhead = hw_kring->rcur = kring->nr_hwtail;
-	error = hw_kring->nm_sync(hw_kring, flags);
-	if (error)
-		goto put_out;
-
-	/* third step: now we are back the rx ring */
-	/* claim ownership on all hw owned bufs */
-	kring->rhead = kring->rcur = nm_next(hw_kring->nr_hwtail, lim); /* skip past reserved slot */
-
-	/* fourth step: the user goes to sleep again, causing another rxsync */
-	netmap_vp_rxsync(kring, flags);
-	ND("%s[%d] PST rx(c%3d t%3d l%3d) ring(h%3d c%3d t%3d) tx(c%3d ht%3d t%3d)",
-		na->name, ring_n,
-		kring->nr_hwcur, kring->nr_hwtail, kring->nkr_hwlease,
-		ring->head, ring->cur, ring->tail,
-		hw_kring->nr_hwcur, hw_kring->nr_hwtail, hw_kring->rtail);
-put_out:
-	nm_kr_put(hw_kring);
-
-	return error ? error : NM_IRQ_COMPLETED;
+int
+netmap_get_vale_na(struct nmreq_header *hdr, struct netmap_adapter **na,
+		struct netmap_mem_d *nmd, int create)
+{
+	return netmap_get_bdg_na(hdr, na, nmd, create, &vale_bdg_ops);
 }
 
 
-/* nm_bdg_ctl callback for the bwrap.
- * Called on bridge-attach and detach, as an effect of vale-ctl -[ahd].
- * On attach, it needs to provide a fake netmap_priv_d structure and
- * perform a netmap_do_regif() on the bwrap. This will put both the
- * bwrap and the hwna in netmap mode, with the netmap rings shared
- * and cross linked. Moroever, it will start intercepting interrupts
- * directed to hwna.
- */
-static int
-netmap_bwrap_bdg_ctl(struct netmap_adapter *na, struct nmreq *nmr, int attach)
+/* creates a persistent VALE port */
+int
+nm_vi_create(struct nmreq_header *hdr)
 {
-	struct netmap_priv_d *npriv;
-	struct netmap_bwrap_adapter *bna = (struct netmap_bwrap_adapter*)na;
+	struct nmreq_vale_newif *req =
+		(struct nmreq_vale_newif *)(uintptr_t)hdr->nr_body;
 	int error = 0;
-
-	if (attach) {
-		if (NETMAP_OWNED_BY_ANY(na)) {
-			return EBUSY;
-		}
-		if (bna->na_kpriv) {
-			/* nothing to do */
-			return 0;
-		}
-		npriv = netmap_priv_new();
-		if (npriv == NULL)
-			return ENOMEM;
-		npriv->np_ifp = na->ifp; /* let the priv destructor release the ref */
-		error = netmap_do_regif(npriv, na, 0, NR_REG_NIC_SW);
-		if (error) {
-			netmap_priv_delete(npriv);
-			return error;
-		}
-		bna->na_kpriv = npriv;
-		na->na_flags |= NAF_BUSY;
-	} else {
-		if (na->active_fds == 0) /* not registered */
-			return EINVAL;
-		netmap_priv_delete(bna->na_kpriv);
-		bna->na_kpriv = NULL;
-		na->na_flags &= ~NAF_BUSY;
-	}
+	/* Build a nmreq_register out of the nmreq_vale_newif,
+	 * so that we can call netmap_get_bdg_na(). */
+	struct nmreq_register regreq;
+	bzero(®req, sizeof(regreq));
+	regreq.nr_tx_slots = req->nr_tx_slots;
+	regreq.nr_rx_slots = req->nr_rx_slots;
+	regreq.nr_tx_rings = req->nr_tx_rings;
+	regreq.nr_rx_rings = req->nr_rx_rings;
+	regreq.nr_mem_id = req->nr_mem_id;
+	hdr->nr_reqtype = NETMAP_REQ_REGISTER;
+	hdr->nr_body = (uintptr_t)®req;
+	error = netmap_vi_create(hdr, 0 /* no autodelete */);
+	hdr->nr_reqtype = NETMAP_REQ_VALE_NEWIF;
+	hdr->nr_body = (uintptr_t)req;
+	/* Write back to the original struct. */
+	req->nr_tx_slots = regreq.nr_tx_slots;
+	req->nr_rx_slots = regreq.nr_rx_slots;
+	req->nr_tx_rings = regreq.nr_tx_rings;
+	req->nr_rx_rings = regreq.nr_rx_rings;
+	req->nr_mem_id = regreq.nr_mem_id;
 	return error;
-
 }
 
-/* attach a bridge wrapper to the 'real' device */
+/* remove a persistent VALE port from the system */
 int
-netmap_bwrap_attach(const char *nr_name, struct netmap_adapter *hwna)
+nm_vi_destroy(const char *name)
 {
-	struct netmap_bwrap_adapter *bna;
-	struct netmap_adapter *na = NULL;
-	struct netmap_adapter *hostna = NULL;
-	int error = 0;
-	enum txrx t;
+	if_t ifp;
+	struct netmap_vp_adapter *vpna;
+	int error;
 
-	/* make sure the NIC is not already in use */
-	if (NETMAP_OWNED_BY_ANY(hwna)) {
-		D("NIC %s busy, cannot attach to bridge", hwna->name);
-		return EBUSY;
+	ifp = ifunit_ref(name);
+	if (!ifp)
+		return ENXIO;
+	NMG_LOCK();
+	/* make sure this is actually a VALE port */
+	if (!NM_NA_VALID(ifp) || NA(ifp)->nm_register != netmap_vp_reg) {
+		error = EINVAL;
+		goto err;
 	}
 
-	bna = nm_os_malloc(sizeof(*bna));
-	if (bna == NULL) {
-		return ENOMEM;
-	}
+	vpna = (struct netmap_vp_adapter *)NA(ifp);
 
-	na = &bna->up.up;
-	/* make bwrap ifp point to the real ifp */
-	na->ifp = hwna->ifp;
-	if_ref(na->ifp);
-	na->na_private = bna;
-	strncpy(na->name, nr_name, sizeof(na->name));
-	/* fill the ring data for the bwrap adapter with rx/tx meanings
-	 * swapped. The real cross-linking will be done during register,
-	 * when all the krings will have been created.
-	 */
-	for_rx_tx(t) {
-		enum txrx r = nm_txrx_swap(t); /* swap NR_TX <-> NR_RX */
-		nma_set_nrings(na, t, nma_get_nrings(hwna, r));
-		nma_set_ndesc(na, t, nma_get_ndesc(hwna, r));
+	/* we can only destroy ports that were created via NETMAP_BDG_NEWIF */
+	if (vpna->autodelete) {
+		error = EINVAL;
+		goto err;
 	}
-	na->nm_dtor = netmap_bwrap_dtor;
-	na->nm_register = netmap_bwrap_reg;
-	// na->nm_txsync = netmap_bwrap_txsync;
-	// na->nm_rxsync = netmap_bwrap_rxsync;
-	na->nm_config = netmap_bwrap_config;
-	na->nm_krings_create = netmap_bwrap_krings_create;
-	na->nm_krings_delete = netmap_bwrap_krings_delete;
-	na->nm_notify = netmap_bwrap_notify;
-	na->nm_bdg_ctl = netmap_bwrap_bdg_ctl;
-	na->pdev = hwna->pdev;
-	na->nm_mem = netmap_mem_get(hwna->nm_mem);
-	na->virt_hdr_len = hwna->virt_hdr_len;
-	bna->up.retry = 1; /* XXX maybe this should depend on the hwna */
-
-	bna->hwna = hwna;
-	netmap_adapter_get(hwna);
-	hwna->na_private = bna; /* weak reference */
-	hwna->na_vp = &bna->up;
 
-	if (hwna->na_flags & NAF_HOST_RINGS) {
-		if (hwna->na_flags & NAF_SW_ONLY)
-			na->na_flags |= NAF_SW_ONLY;
-		na->na_flags |= NAF_HOST_RINGS;
-		hostna = &bna->host.up;
-		snprintf(hostna->name, sizeof(hostna->name), "%s^", nr_name);
-		hostna->ifp = hwna->ifp;
-		for_rx_tx(t) {
-			enum txrx r = nm_txrx_swap(t);
-			nma_set_nrings(hostna, t, 1);
-			nma_set_ndesc(hostna, t, nma_get_ndesc(hwna, r));
-		}
-		// hostna->nm_txsync = netmap_bwrap_host_txsync;
-		// hostna->nm_rxsync = netmap_bwrap_host_rxsync;
-		hostna->nm_notify = netmap_bwrap_notify;
-		hostna->nm_mem = netmap_mem_get(na->nm_mem);
-		hostna->na_private = bna;
-		hostna->na_vp = &bna->up;
-		na->na_hostvp = hwna->na_hostvp =
-			hostna->na_hostvp = &bna->host;
-		hostna->na_flags = NAF_BUSY; /* prevent NIOCREGIF */
+	/* also make sure that nobody is using the interface */
+	if (NETMAP_OWNED_BY_ANY(&vpna->up) ||
+	    vpna->up.na_refcount > 1 /* any ref besides the one in nm_vi_create()? */) {
+		error = EBUSY;
+		goto err;
 	}
 
-	ND("%s<->%s txr %d txd %d rxr %d rxd %d",
-		na->name, ifp->if_xname,
-		na->num_tx_rings, na->num_tx_desc,
-		na->num_rx_rings, na->num_rx_desc);
+	NMG_UNLOCK();
 
-	error = netmap_attach_common(na);
-	if (error) {
-		goto err_free;
-	}
-	hwna->na_flags |= NAF_BUSY;
+	if (netmap_verbose)
+		nm_prinf("destroying a persistent vale interface %s", if_name(ifp));
+	/* Linux requires all the references are released
+	 * before unregister
+	 */
+	netmap_detach(ifp);
+	if_rele(ifp);
+	nm_os_vi_detach(ifp);
 	return 0;
 
-err_free:
-	hwna->na_vp = hwna->na_hostvp = NULL;
-	netmap_adapter_put(hwna);
-	nm_os_free(bna);
+err:
+	NMG_UNLOCK();
+	if_rele(ifp);
 	return error;
-
 }
 
-struct nm_bridge *
-netmap_init_bridges2(u_int n)
+static int
+nm_update_info(struct nmreq_register *req, struct netmap_adapter *na)
 {
-	int i;
-	struct nm_bridge *b;
-
-	b = nm_os_malloc(sizeof(struct nm_bridge) * n);
-	if (b == NULL)
-		return NULL;
-	for (i = 0; i < n; i++)
-		BDG_RWINIT(&b[i]);
-	return b;
+	req->nr_rx_rings = na->num_rx_rings;
+	req->nr_tx_rings = na->num_tx_rings;
+	req->nr_rx_slots = na->num_rx_desc;
+	req->nr_tx_slots = na->num_tx_desc;
+	return netmap_mem_get_info(na->nm_mem, &req->nr_memsize, NULL,
+					&req->nr_mem_id);
 }
 
-void
-netmap_uninit_bridges2(struct nm_bridge *b, u_int n)
+
+/*
+ * Create a virtual interface registered to the system.
+ * The interface will be attached to a bridge later.
+ */
+int
+netmap_vi_create(struct nmreq_header *hdr, int autodelete)
 {
-	int i;
+	struct nmreq_register *req = (struct nmreq_register *)(uintptr_t)hdr->nr_body;
+	if_t ifp;
+	struct netmap_vp_adapter *vpna;
+	struct netmap_mem_d *nmd = NULL;
+	int error;
 
-	if (b == NULL)
-		return;
+	if (hdr->nr_reqtype != NETMAP_REQ_REGISTER) {
+		return EINVAL;
+	}
 
-	for (i = 0; i < n; i++)
-		BDG_RWDESTROY(&b[i]);
-	nm_os_free(b);
-}
+	/* don't include VALE prefix */
+	if (!strncmp(hdr->nr_name, NM_BDG_NAME, strlen(NM_BDG_NAME)))
+		return EINVAL;
+	if (strlen(hdr->nr_name) >= IFNAMSIZ) {
+		return EINVAL;
+	}
+	ifp = ifunit_ref(hdr->nr_name);
+	if (ifp) { /* already exist, cannot create new one */
+		error = EEXIST;
+		NMG_LOCK();
+		if (NM_NA_VALID(ifp)) {
+			int update_err = nm_update_info(req, NA(ifp));
+			if (update_err)
+				error = update_err;
+		}
+		NMG_UNLOCK();
+		if_rele(ifp);
+		return error;
+	}
+	error = nm_os_vi_persist(hdr->nr_name, &ifp);
+	if (error)
+		return error;
 
-int
-netmap_init_bridges(void)
-{
-#ifdef CONFIG_NET_NS
-	return netmap_bns_register();
-#else
-	nm_bridges = netmap_init_bridges2(NM_BRIDGES);
-	if (nm_bridges == NULL)
-		return ENOMEM;
+	NMG_LOCK();
+	if (req->nr_mem_id) {
+		nmd = netmap_mem_find(req->nr_mem_id);
+		if (nmd == NULL) {
+			error = EINVAL;
+			goto err_1;
+		}
+	}
+	/* netmap_vp_create creates a struct netmap_vp_adapter */
+	error = netmap_vale_vp_create(hdr, ifp, nmd, &vpna);
+	if (error) {
+		if (netmap_debug & NM_DEBUG_VALE)
+			nm_prerr("error %d", error);
+		goto err_1;
+	}
+	/* persist-specific routines */
+	vpna->up.nm_bdg_ctl = netmap_vp_bdg_ctl;
+	if (!autodelete) {
+		netmap_adapter_get(&vpna->up);
+	} else {
+		vpna->autodelete = 1;
+	}
+	NM_ATTACH_NA(ifp, &vpna->up);
+	/* return the updated info */
+	error = nm_update_info(req, &vpna->up);
+	if (error) {
+		goto err_2;
+	}
+	nm_prdis("returning nr_mem_id %d", req->nr_mem_id);
+	if (nmd)
+		netmap_mem_put(nmd);
+	NMG_UNLOCK();
+	nm_prdis("created %s", if_name(ifp));
 	return 0;
-#endif
-}
 
-void
-netmap_uninit_bridges(void)
-{
-#ifdef CONFIG_NET_NS
-	netmap_bns_unregister();
-#else
-	netmap_uninit_bridges2(nm_bridges, NM_BRIDGES);
-#endif
+err_2:
+	netmap_detach(ifp);
+err_1:
+	if (nmd)
+		netmap_mem_put(nmd);
+	NMG_UNLOCK();
+	nm_os_vi_detach(ifp);
+
+	return error;
 }
+
 #endif /* WITH_VALE */
diff --git a/sys/modules/netmap/Makefile b/sys/modules/netmap/Makefile
index 978a4858e..06212aa5f 100644
--- a/sys/modules/netmap/Makefile
+++ b/sys/modules/netmap/Makefile
@@ -1,5 +1,3 @@
-# $FreeBSD$
-#
 # Compile netmap as a module, useful if you want a netmap bridge
 # or loadable drivers.
 
@@ -8,7 +6,7 @@
 
 .PATH: ${.CURDIR}/../../dev/netmap
 .PATH.h: ${.CURDIR}/../../net
-CFLAGS += -I${.CURDIR}/../../ -D INET
+CFLAGS += -I${.CURDIR}/../../ -D INET -D VIMAGE
 KMOD	= netmap
 SRCS	= device_if.h bus_if.h pci_if.h opt_netmap.h
 SRCS	+= netmap.c netmap.h netmap_kern.h
@@ -20,7 +18,10 @@ SRCS	+= netmap_freebsd.c
 SRCS	+= netmap_offloadings.c
 SRCS	+= netmap_pipe.c
 SRCS	+= netmap_monitor.c
-SRCS	+= netmap_pt.c
+SRCS	+= netmap_kloop.c
+SRCS	+= netmap_legacy.c
+SRCS	+= netmap_bdg.c
+SRCS	+= netmap_null.c
 SRCS	+= if_ptnet.c
 SRCS	+= opt_inet.h opt_inet6.h
 
diff --git a/sys/net/netmap.h b/sys/net/netmap.h
index 3543426b6..998e3153c 100644
--- a/sys/net/netmap.h
+++ b/sys/net/netmap.h
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,8 +27,6 @@
  */
 
 /*
- * $FreeBSD: head/sys/net/netmap.h 251139 2013-05-30 14:07:14Z luigi $
- *
  * Definitions of constants and the structures used by the netmap
  * framework, for the part visible to both kernel and userspace.
  * Detailed info on netmap is available with "man netmap" or at
@@ -39,9 +39,9 @@
 #ifndef _NET_NETMAP_H_
 #define _NET_NETMAP_H_
 
-#define	NETMAP_API	11		/* current API version */
+#define	NETMAP_API	14		/* current API version */
 
-#define	NETMAP_MIN_API	11		/* min and max versions accepted */
+#define	NETMAP_MIN_API	14		/* min and max versions accepted */
 #define	NETMAP_MAX_API	15
 /*
  * Some fields should be cache-aligned to reduce contention.
@@ -62,34 +62,34 @@
    KERNEL (opaque, obviously)
 
   ====================================================================
-                                         |
-   USERSPACE                             |      struct netmap_ring
-                                         +---->+---------------+
-                                             / | head,cur,tail |
-   struct netmap_if (nifp, 1 per fd)        /  | buf_ofs       |
-    +---------------+                      /   | other fields  |
-    | ni_tx_rings   |                     /    +===============+
-    | ni_rx_rings   |                    /     | buf_idx, len  | slot[0]
-    |               |                   /      | flags, ptr    |
-    |               |                  /       +---------------+
-    +===============+                 /        | buf_idx, len  | slot[1]
-    | txring_ofs[0] | (rel.to nifp)--'         | flags, ptr    |
-    | txring_ofs[1] |                          +---------------+
-     (tx+1 entries)                           (num_slots entries)
-    | txring_ofs[t] |                          | buf_idx, len  | slot[n-1]
-    +---------------+                          | flags, ptr    |
-    | rxring_ofs[0] |                          +---------------+
-    | rxring_ofs[1] |
-     (rx+1 entries)
-    | rxring_ofs[r] |
-    +---------------+
+                                          |
+   USERSPACE                              |      struct netmap_ring
+                                          +---->+---------------+
+                                              / | head,cur,tail |
+   struct netmap_if (nifp, 1 per fd)         /  | buf_ofs       |
+    +----------------+                      /   | other fields  |
+    | ni_tx_rings    |                     /    +===============+
+    | ni_rx_rings    |                    /     | buf_idx, len  | slot[0]
+    |                |                   /      | flags, ptr    |
+    |                |                  /       +---------------+
+    +================+                 /        | buf_idx, len  | slot[1]
+    | txring_ofs[0]  | (rel.to nifp)--'         | flags, ptr    |
+    | txring_ofs[1]  |                          +---------------+
+     (tx+htx entries)                           (num_slots entries)
+    | txring_ofs[t]  |                          | buf_idx, len  | slot[n-1]
+    +----------------+                          | flags, ptr    |
+    | rxring_ofs[0]  |                          +---------------+
+    | rxring_ofs[1]  |
+     (rx+hrx entries)
+    | rxring_ofs[r]  |
+    +----------------+
 
  * For each "interface" (NIC, host stack, PIPE, VALE switch port) bound to
  * a file descriptor, the mmap()ed region contains a (logically readonly)
  * struct netmap_if pointing to struct netmap_ring's.
  *
- * There is one netmap_ring per physical NIC ring, plus one tx/rx ring
- * pair attached to the host stack (this pair is unused for non-NIC ports).
+ * There is one netmap_ring per physical NIC ring, plus at least one tx/rx ring
+ * pair attached to the host stack (these pairs are unused for non-NIC ports).
  *
  * All physical/host stack ports share the same memory region,
  * so that zero-copy can be implemented between them.
@@ -115,11 +115,6 @@
  *   as the index. On close, ni_bufs_head must point to the list of
  *   buffers to be released.
  *
- * + NIOCREGIF can request space for extra rings (and buffers)
- *   allocated in the same memory space. The number of extra rings
- *   is in nr_arg1, and is advisory. This is a no-op on NICs where
- *   the size of the memory space is fixed.
- *
  * + NIOCREGIF can attach to PIPE rings sharing the same memory
  *   space with a parent device. The ifname indicates the parent device,
  *   which must already exist. Flags in nr_flags indicate if we want to
@@ -131,31 +126,32 @@
  *
  *   Extra flags in nr_flags support the above functions.
  *   Application libraries may use the following naming scheme:
- *	netmap:foo			all NIC ring pairs
- *	netmap:foo^			only host ring pair
- *	netmap:foo+			all NIC ring + host ring pairs
- *	netmap:foo-k			the k-th NIC ring pair
- *	netmap:foo{k			PIPE ring pair k, master side
- *	netmap:foo}k			PIPE ring pair k, slave side
+ *	netmap:foo			all NIC rings pairs
+ *	netmap:foo^			only host rings pairs
+ *	netmap:foo^k			the k-th host rings pair
+ *	netmap:foo+			all NIC rings + host rings pairs
+ *	netmap:foo-k			the k-th NIC rings pair
+ *	netmap:foo{k			PIPE rings pair k, master side
+ *	netmap:foo}k			PIPE rings pair k, slave side
  *
  * Some notes about host rings:
  *
- * + The RX host ring is used to store those packets that the host network
+ * + The RX host rings are used to store those packets that the host network
  *   stack is trying to transmit through a NIC queue, but only if that queue
  *   is currently in netmap mode. Netmap will not intercept host stack mbufs
  *   designated to NIC queues that are not in netmap mode. As a consequence,
  *   registering a netmap port with netmap:foo^ is not enough to intercept
- *   mbufs in the RX host ring; the netmap port should be registered with
+ *   mbufs in the RX host rings; the netmap port should be registered with
  *   netmap:foo*, or another registration should be done to open at least a
  *   NIC TX queue in netmap mode.
  *
- * + Netmap is not currently able to deal with intercepted trasmit mbufs which
+ * + Netmap is not currently able to deal with intercepted transmit mbufs which
  *   require offloadings like TSO, UFO, checksumming offloadings, etc. It is
  *   responsibility of the user to disable those offloadings (e.g. using
  *   ifconfig on FreeBSD or ethtool -K on Linux) for an interface that is being
  *   used in netmap mode. If the offloadings are not disabled, GSO and/or
  *   unchecksummed packets may be dropped immediately or end up in the host RX
- *   ring, and will be dropped as soon as the packet reaches another netmap
+ *   rings, and will be dropped as soon as the packet reaches another netmap
  *   adapter.
  */
 
@@ -215,11 +211,17 @@ struct netmap_slot {
 
 #define	NS_MOREFRAG	0x0020	/* packet has more fragments */
  	/*
-	 * (VALE ports only)
+	 * (VALE ports, ptnetmap ports and some NIC ports, e.g.
+         * ixgbe and i40e on Linux)
 	 * Set on all but the last slot of a multi-segment packet.
 	 * The 'len' field refers to the individual fragment.
 	 */
 
+#define NS_TXMON	0x0040
+	/* (monitor ports only) the packet comes from the TX
+	 * ring of the monitored port
+	 */
+
 #define	NS_PORT_SHIFT	8
 #define	NS_PORT_MASK	(0xff << NS_PORT_SHIFT)
 	/*
@@ -234,6 +236,8 @@ struct netmap_slot {
 	 *  are the number of fragments.
 	 */
 
+#define NETMAP_MAX_FRAGS	64	/* max number of fragments */
+
 
 /*
  * struct netmap_ring
@@ -296,6 +300,19 @@ struct netmap_ring {
 
 	struct timeval	ts;		/* (k) time of last *sync() */
 
+	/* offset_mask is used to isolate the part of the ptr field
+	 * in the slots used to contain an offset in the buffer.
+	 * It is zero if the ring has not be opened using the
+	 * NETMAP_REQ_OPT_OFFSETS option.
+	 */
+	const uint64_t	offset_mask;
+	/* the alignment requirement, in bytes, for the start
+	 * of the packets inside the buffers.
+	 * User programs should take this alignment into
+	 * account when specifying buffer-offsets in TX slots.
+	 */
+	const uint64_t	buf_align;
+
 	/* opaque room for a mutex or similar object */
 #if !defined(_WIN32) || defined(__CYGWIN__)
 	uint8_t	__attribute__((__aligned__(NM_CACHE_ALIGN))) sem[128];
@@ -304,7 +321,7 @@ struct netmap_ring {
 #endif
 
 	/* the slots follow. This struct has variable size */
-	struct netmap_slot slot[0];	/* array of slots. */
+	struct netmap_slot slot[];	/* array of slots. */
 };
 
 
@@ -323,6 +340,23 @@ struct netmap_ring {
 	 * Enables the NS_FORWARD slot flag for the ring.
 	 */
 
+/*
+ * Helper functions for kernel and userspace
+ */
+
+/*
+ * Check if space is available in the ring. We use ring->head, which
+ * points to the next netmap slot to be published to netmap. It is
+ * possible that the applications moves ring->cur ahead of ring->tail
+ * (e.g., by setting ring->cur <== ring->tail), if it wants more slots
+ * than the ones currently available, and it wants to be notified when
+ * more arrive. See netmap(4) for more details and examples.
+ */
+static inline int
+nm_ring_empty(struct netmap_ring *ring)
+{
+	return (ring->head == ring->tail);
+}
 
 /*
  * Netmap representation of an interface and its queue(s).
@@ -344,7 +378,7 @@ struct netmap_if {
 	/*
 	 * The number of packet rings available in netmap mode.
 	 * Physical NICs can have different numbers of tx and rx rings.
-	 * Physical NICs also have a 'host' ring pair.
+	 * Physical NICs also have at least a 'host' rings pair.
 	 * Additionally, clients can request additional ring pairs to
 	 * be used for internal communication.
 	 */
@@ -352,99 +386,93 @@ struct netmap_if {
 	const uint32_t	ni_rx_rings;	/* number of HW rx rings */
 
 	uint32_t	ni_bufs_head;	/* head index for extra bufs */
-	uint32_t	ni_spare1[5];
+	const uint32_t	ni_host_tx_rings; /* number of SW tx rings */
+	const uint32_t	ni_host_rx_rings; /* number of SW rx rings */
+	uint32_t	ni_spare1[3];
 	/*
 	 * The following array contains the offset of each netmap ring
 	 * from this structure, in the following order:
-	 * NIC tx rings (ni_tx_rings); host tx ring (1); extra tx rings;
-	 * NIC rx rings (ni_rx_rings); host tx ring (1); extra rx rings.
+	 *     - NIC tx rings (ni_tx_rings);
+	 *     - host tx rings (ni_host_tx_rings);
+	 *     - NIC rx rings (ni_rx_rings);
+	 *     - host rx ring (ni_host_rx_rings);
 	 *
-	 * The area is filled up by the kernel on NIOCREGIF,
+	 * The area is filled up by the kernel on NETMAP_REQ_REGISTER,
 	 * and then only read by userspace code.
 	 */
-	const ssize_t	ring_ofs[0];
+	const ssize_t	ring_ofs[];
 };
 
+/* Legacy interface to interact with a netmap control device.
+ * Included for backward compatibility. The user should not include this
+ * file directly. */
+#include "netmap_legacy.h"
 
-#ifndef NIOCREGIF
 /*
- * ioctl names and related fields
- *
- * NIOCTXSYNC, NIOCRXSYNC synchronize tx or rx queues,
- *	whose identity is set in NIOCREGIF through nr_ringid.
- *	These are non blocking and take no argument.
- *
- * NIOCGINFO takes a struct ifreq, the interface name is the input,
- *	the outputs are number of queues and number of descriptor
- *	for each queue (useful to set number of threads etc.).
- *	The info returned is only advisory and may change before
- *	the interface is bound to a file descriptor.
- *
- * NIOCREGIF takes an interface name within a struct nmre,
- *	and activates netmap mode on the interface (if possible).
- *
- * The argument to NIOCGINFO/NIOCREGIF overlays struct ifreq so we
- * can pass it down to other NIC-related ioctls.
- *
- * The actual argument (struct nmreq) has a number of options to request
- * different functions.
- * The following are used in NIOCREGIF when nr_cmd == 0:
- *
- * nr_name	(in)
- *	The name of the port (em0, valeXXX:YYY, etc.)
- *	limited to IFNAMSIZ for backward compatibility.
- *
- * nr_version	(in/out)
- *	Must match NETMAP_API as used in the kernel, error otherwise.
- *	Always returns the desired value on output.
- *
- * nr_tx_slots, nr_tx_slots, nr_tx_rings, nr_rx_rings (in/out)
- *	On input, non-zero values may be used to reconfigure the port
- *	according to the requested values, but this is not guaranteed.
- *	On output the actual values in use are reported.
- *
- * nr_ringid (in)
- *	Indicates how rings should be bound to the file descriptors.
- *	If nr_flags != 0, then the low bits (in NETMAP_RING_MASK)
- *	are used to indicate the ring number, and nr_flags specifies
- *	the actual rings to bind. NETMAP_NO_TX_POLL is unaffected.
- *
- *	NOTE: THE FOLLOWING (nr_flags == 0) IS DEPRECATED:
- *	If nr_flags == 0, NETMAP_HW_RING and NETMAP_SW_RING control
- *	the binding as follows:
- *	0 (default)			binds all physical rings
- *	NETMAP_HW_RING | ring number	binds a single ring pair
- *	NETMAP_SW_RING			binds only the host tx/rx rings
- *
- *	NETMAP_NO_TX_POLL can be OR-ed to make select()/poll() push
- *		packets on tx rings only if POLLOUT is set.
- *		The default is to push any pending packet.
- *
- *	NETMAP_DO_RX_POLL can be OR-ed to make select()/poll() release
- *		packets on rx rings also when POLLIN is NOT set.
- *		The default is to touch the rx ring only with POLLIN.
- *		Note that this is the opposite of TX because it
- *		reflects the common usage.
- *
- *	NOTE: NETMAP_PRIV_MEM IS DEPRECATED, use nr_arg2 instead.
- *	NETMAP_PRIV_MEM is set on return for ports that do not use
- *		the global memory allocator.
- *		This information is not significant and applications
- *		should look at the region id in nr_arg2
- *
- * nr_flags	is the recommended mode to indicate which rings should
- *		be bound to a file descriptor. Values are NR_REG_*
- *
- * nr_arg1 (in)	The number of extra rings to be reserved.
- *		Especially when allocating a VALE port the system only
- *		allocates the amount of memory needed for the port.
- *		If more shared memory rings are desired (e.g. for pipes),
- *		the first invocation for the same basename/allocator
- *		should specify a suitable number. Memory cannot be
- *		extended after the first allocation without closing
- *		all ports on the same region.
- *
- * nr_arg2 (in/out) The identity of the memory region used.
+ * New API to control netmap control devices. New applications should only use
+ * nmreq_xyz structs with the NIOCCTRL ioctl() command.
+ *
+ * NIOCCTRL takes a nmreq_header struct, which contains the required
+ * API version, the name of a netmap port, a command type, and pointers
+ * to request body and options.
+ *
+ *	nr_name	(in)
+ *		The name of the port (em0, valeXXX:YYY, eth0{pn1 etc.)
+ *
+ *	nr_version (in/out)
+ *		Must match NETMAP_API as used in the kernel, error otherwise.
+ *		Always returns the desired value on output.
+ *
+ *	nr_reqtype (in)
+ *		One of the NETMAP_REQ_* command types below
+ *
+ *	nr_body (in)
+ *		Pointer to a command-specific struct, described by one
+ *		of the struct nmreq_xyz below.
+ *
+ *	nr_options (in)
+ *		Command specific options, if any.
+ *
+ * A NETMAP_REQ_REGISTER command activates netmap mode on the netmap
+ * port (e.g. physical interface) specified by nmreq_header.nr_name.
+ * The request body (struct nmreq_register) has several arguments to
+ * specify how the port is to be registered.
+ *
+ *	nr_tx_slots, nr_tx_slots, nr_tx_rings, nr_rx_rings,
+ *	nr_host_tx_rings, nr_host_rx_rings (in/out)
+ *		On input, non-zero values may be used to reconfigure the port
+ *		according to the requested values, but this is not guaranteed.
+ *		On output the actual values in use are reported.
+ *
+ *	nr_mode (in)
+ *		Indicate what set of rings must be bound to the netmap
+ *		device (e.g. all NIC rings, host rings only, NIC and
+ *		host rings, ...). Values are in NR_REG_*.
+ *
+ *	nr_ringid (in)
+ *		If nr_mode == NR_REG_ONE_NIC (only a single couple of TX/RX
+ *		rings), indicate which NIC TX and/or RX ring is to be bound
+ *		(0..nr_*x_rings-1).
+ *
+ *	nr_flags (in)
+ *		Indicate special options for how to open the port.
+ *
+ *		NR_NO_TX_POLL can be OR-ed to make select()/poll() push
+ *			packets on tx rings only if POLLOUT is set.
+ *			The default is to push any pending packet.
+ *
+ *		NR_DO_RX_POLL can be OR-ed to make select()/poll() release
+ *			packets on rx rings also when POLLIN is NOT set.
+ *			The default is to touch the rx ring only with POLLIN.
+ *			Note that this is the opposite of TX because it
+ *			reflects the common usage.
+ *
+ *		Other options are NR_MONITOR_TX, NR_MONITOR_RX, NR_ZCOPY_MON,
+ *		NR_EXCLUSIVE, NR_RX_RINGS_ONLY, NR_TX_RINGS_ONLY and
+ *		NR_ACCEPT_VNET_HDR.
+ *
+ *	nr_mem_id (in/out)
+ *		The identity of the memory region used.
  *		On input, 0 means the system decides autonomously,
  *		other values may try to select a specific region.
  *		On return the actual value is reported.
@@ -452,197 +480,515 @@ struct netmap_if {
  *		by all interfaces. Other values are private regions.
  *		If two ports the same region zero-copy is possible.
  *
- * nr_arg3 (in/out)	number of extra buffers to be allocated.
- *
- *
- *
- * nr_cmd (in)	if non-zero indicates a special command:
- *	NETMAP_BDG_ATTACH	 and nr_name = vale*:ifname
- *		attaches the NIC to the switch; nr_ringid specifies
- *		which rings to use. Used by vale-ctl -a ...
- *	    nr_arg1 = NETMAP_BDG_HOST also attaches the host port
- *		as in vale-ctl -h ...
- *
- *	NETMAP_BDG_DETACH	and nr_name = vale*:ifname
- *		disconnects a previously attached NIC.
- *		Used by vale-ctl -d ...
- *
- *	NETMAP_BDG_LIST
- *		list the configuration of VALE switches.
- *
- *	NETMAP_BDG_VNET_HDR
- *		Set the virtio-net header length used by the client
- *		of a VALE switch port.
- *
- *	NETMAP_BDG_NEWIF
- *		create a persistent VALE port with name nr_name.
- *		Used by vale-ctl -n ...
- *
- *	NETMAP_BDG_DELIF
- *		delete a persistent VALE port. Used by vale-ctl -d ...
- *
- * nr_arg1, nr_arg2, nr_arg3  (in/out)		command specific
- *
+ *	nr_extra_bufs (in/out)
+ *		Number of extra buffers to be allocated.
  *
+ * The other NETMAP_REQ_* commands are described below.
  *
  */
 
+/* maximum size of a request, including all options */
+#define NETMAP_REQ_MAXSIZE	4096
+
+/* Header common to all request options. */
+struct nmreq_option {
+	/* Pointer to the next option. */
+	uint64_t		nro_next;
+	/* Option type. */
+	uint32_t		nro_reqtype;
+	/* (out) status of the option:
+	 * 0: recognized and processed
+	 * !=0: errno value
+	 */
+	uint32_t		nro_status;
+	/* Option size, used only for options that can have variable size
+	 * (e.g. because they contain arrays). For fixed-size options this
+	 * field should be set to zero. */
+	uint64_t		nro_size;
+};
+
+/* Header common to all requests. Do not reorder these fields, as we need
+ * the second one (nr_reqtype) to know how much to copy from/to userspace. */
+struct nmreq_header {
+	uint16_t		nr_version;	/* API version */
+	uint16_t		nr_reqtype;	/* nmreq type (NETMAP_REQ_*) */
+	uint32_t		nr_reserved;	/* must be zero */
+#define NETMAP_REQ_IFNAMSIZ	64
+	char			nr_name[NETMAP_REQ_IFNAMSIZ]; /* port name */
+	uint64_t		nr_options;	/* command-specific options */
+	uint64_t		nr_body;	/* ptr to nmreq_xyz struct */
+};
+
+enum {
+	/* Register a netmap port with the device. */
+	NETMAP_REQ_REGISTER = 1,
+	/* Get information from a netmap port. */
+	NETMAP_REQ_PORT_INFO_GET,
+	/* Attach a netmap port to a VALE switch. */
+	NETMAP_REQ_VALE_ATTACH,
+	/* Detach a netmap port from a VALE switch. */
+	NETMAP_REQ_VALE_DETACH,
+	/* List the ports attached to a VALE switch. */
+	NETMAP_REQ_VALE_LIST,
+	/* Set the port header length (was virtio-net header length). */
+	NETMAP_REQ_PORT_HDR_SET,
+	/* Get the port header length (was virtio-net header length). */
+	NETMAP_REQ_PORT_HDR_GET,
+	/* Create a new persistent VALE port. */
+	NETMAP_REQ_VALE_NEWIF,
+	/* Delete a persistent VALE port. */
+	NETMAP_REQ_VALE_DELIF,
+	/* Enable polling kernel thread(s) on an attached VALE port. */
+	NETMAP_REQ_VALE_POLLING_ENABLE,
+	/* Disable polling kernel thread(s) on an attached VALE port. */
+	NETMAP_REQ_VALE_POLLING_DISABLE,
+	/* Get info about the pools of a memory allocator. */
+	NETMAP_REQ_POOLS_INFO_GET,
+	/* Start an in-kernel loop that syncs the rings periodically or
+	 * on notifications. The loop runs in the context of the ioctl
+	 * syscall, and only stops on NETMAP_REQ_SYNC_KLOOP_STOP. */
+	NETMAP_REQ_SYNC_KLOOP_START,
+	/* Stops the thread executing the in-kernel loop. The thread
+	 * returns from the ioctl syscall. */
+	NETMAP_REQ_SYNC_KLOOP_STOP,
+	/* Enable CSB mode on a registered netmap control device. */
+	NETMAP_REQ_CSB_ENABLE,
+};
+
+enum {
+	/* On NETMAP_REQ_REGISTER, ask netmap to use memory allocated
+	 * from user-space allocated memory pools (e.g. hugepages).
+	 */
+	NETMAP_REQ_OPT_EXTMEM = 1,
+
+	/* ON NETMAP_REQ_SYNC_KLOOP_START, ask netmap to use eventfd-based
+	 * notifications to synchronize the kernel loop with the application.
+	 */
+	NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS,
+
+	/* On NETMAP_REQ_REGISTER, ask netmap to work in CSB mode, where
+	 * head, cur and tail pointers are not exchanged through the
+	 * struct netmap_ring header, but rather using an user-provided
+	 * memory area (see struct nm_csb_atok and struct nm_csb_ktoa).
+	 */
+	NETMAP_REQ_OPT_CSB,
+
+	/* An extension to NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS, which specifies
+	 * if the TX and/or RX rings are synced in the context of the VM exit.
+	 * This requires the 'ioeventfd' fields to be valid (cannot be < 0).
+	 */
+	NETMAP_REQ_OPT_SYNC_KLOOP_MODE,
+
+	/* On NETMAP_REQ_REGISTER, ask for (part of) the ptr field in the
+	 * slots of the registered rings to be used as an offset field
+	 * for the start of the packets inside the netmap buffer.
+	 */
+	NETMAP_REQ_OPT_OFFSETS,
+
+	/* This is a marker to count the number of available options.
+	 * New options must be added above it. */
+	NETMAP_REQ_OPT_MAX,
+};
 
 /*
- * struct nmreq overlays a struct ifreq (just the name)
+ * nr_reqtype: NETMAP_REQ_REGISTER
+ * Bind (register) a netmap port to this control device.
  */
-struct nmreq {
-	char		nr_name[IFNAMSIZ];
-	uint32_t	nr_version;	/* API version */
-	uint32_t	nr_offset;	/* nifp offset in the shared region */
-	uint32_t	nr_memsize;	/* size of the shared region */
+struct nmreq_register {
+	uint64_t	nr_offset;	/* nifp offset in the shared region */
+	uint64_t	nr_memsize;	/* size of the shared region */
 	uint32_t	nr_tx_slots;	/* slots in tx rings */
 	uint32_t	nr_rx_slots;	/* slots in rx rings */
 	uint16_t	nr_tx_rings;	/* number of tx rings */
 	uint16_t	nr_rx_rings;	/* number of rx rings */
+	uint16_t	nr_host_tx_rings; /* number of host tx rings */
+	uint16_t	nr_host_rx_rings; /* number of host rx rings */
 
+	uint16_t	nr_mem_id;	/* id of the memory allocator */
 	uint16_t	nr_ringid;	/* ring(s) we care about */
-#define NETMAP_HW_RING		0x4000	/* single NIC ring pair */
-#define NETMAP_SW_RING		0x2000	/* only host ring pair */
-
-#define NETMAP_RING_MASK	0x0fff	/* the ring number */
-
-#define NETMAP_NO_TX_POLL	0x1000	/* no automatic txsync on poll */
-
-#define NETMAP_DO_RX_POLL	0x8000	/* DO automatic rxsync on poll */
-
-	uint16_t	nr_cmd;
-#define NETMAP_BDG_ATTACH	1	/* attach the NIC */
-#define NETMAP_BDG_DETACH	2	/* detach the NIC */
-#define NETMAP_BDG_REGOPS	3	/* register bridge callbacks */
-#define NETMAP_BDG_LIST		4	/* get bridge's info */
-#define NETMAP_BDG_VNET_HDR     5       /* set the port virtio-net-hdr length */
-#define NETMAP_BDG_OFFSET	NETMAP_BDG_VNET_HDR	/* deprecated alias */
-#define NETMAP_BDG_NEWIF	6	/* create a virtual port */
-#define NETMAP_BDG_DELIF	7	/* destroy a virtual port */
-#define NETMAP_PT_HOST_CREATE	8	/* create ptnetmap kthreads */
-#define NETMAP_PT_HOST_DELETE	9	/* delete ptnetmap kthreads */
-#define NETMAP_BDG_POLLING_ON	10	/* delete polling kthread */
-#define NETMAP_BDG_POLLING_OFF	11	/* delete polling kthread */
-#define NETMAP_VNET_HDR_GET	12      /* get the port virtio-net-hdr length */
-#define NETMAP_POOLS_INFO_GET	13	/* get memory allocator pools info */
-	uint16_t	nr_arg1;	/* reserve extra rings in NIOCREGIF */
-#define NETMAP_BDG_HOST		1	/* attach the host stack on ATTACH */
-
-	uint16_t	nr_arg2;
-	uint32_t	nr_arg3;	/* req. extra buffers in NIOCREGIF */
-	uint32_t	nr_flags;
-	/* various modes, extends nr_ringid */
-	uint32_t	spare2[1];
-};
+	uint32_t	nr_mode;	/* specify NR_REG_* modes */
+	uint32_t	nr_extra_bufs;	/* number of requested extra buffers */
 
-#define NR_REG_MASK		0xf /* values for nr_flags */
-enum {	NR_REG_DEFAULT	= 0,	/* backward compat, should not be used. */
-	NR_REG_ALL_NIC	= 1,
-	NR_REG_SW	= 2,
-	NR_REG_NIC_SW	= 3,
-	NR_REG_ONE_NIC	= 4,
-	NR_REG_PIPE_MASTER = 5,
-	NR_REG_PIPE_SLAVE = 6,
-};
-/* monitor uses the NR_REG to select the rings to monitor */
+	uint64_t	nr_flags;	/* additional flags (see below) */
+/* monitors use nr_ringid and nr_mode to select the rings to monitor */
 #define NR_MONITOR_TX	0x100
 #define NR_MONITOR_RX	0x200
 #define NR_ZCOPY_MON	0x400
 /* request exclusive access to the selected rings */
 #define NR_EXCLUSIVE	0x800
-/* request ptnetmap host support */
-#define NR_PASSTHROUGH_HOST	NR_PTNETMAP_HOST /* deprecated */
-#define NR_PTNETMAP_HOST	0x1000
+/* 0x1000 unused */
 #define NR_RX_RINGS_ONLY	0x2000
 #define NR_TX_RINGS_ONLY	0x4000
 /* Applications set this flag if they are able to deal with virtio-net headers,
  * that is send/receive frames that start with a virtio-net header.
- * If not set, NIOCREGIF will fail with netmap ports that require applications
- * to use those headers. If the flag is set, the application can use the
- * NETMAP_VNET_HDR_GET command to figure out the header length. */
+ * If not set, NETMAP_REQ_REGISTER will fail with netmap ports that require
+ * applications to use those headers. If the flag is set, the application can
+ * use the NETMAP_VNET_HDR_GET command to figure out the header length. */
 #define NR_ACCEPT_VNET_HDR	0x8000
+/* The following two have the same meaning of NETMAP_NO_TX_POLL and
+ * NETMAP_DO_RX_POLL. */
+#define NR_DO_RX_POLL		0x10000
+#define NR_NO_TX_POLL		0x20000
+};
 
-#define	NM_BDG_NAME		"vale"	/* prefix for bridge port name */
-
-/*
- * Windows does not have _IOWR(). _IO(), _IOW() and _IOR() are defined
- * in ws2def.h but not sure if they are in the form we need.
- * XXX so we redefine them
- * in a convenient way to use for DeviceIoControl signatures
- */
-#ifdef _WIN32
-#undef _IO	// ws2def.h
-#define _WIN_NM_IOCTL_TYPE 40000
-#define _IO(_c, _n)	CTL_CODE(_WIN_NM_IOCTL_TYPE, ((_n) + 0x800) , \
-		METHOD_BUFFERED, FILE_ANY_ACCESS  )
-#define _IO_direct(_c, _n)	CTL_CODE(_WIN_NM_IOCTL_TYPE, ((_n) + 0x800) , \
-		METHOD_OUT_DIRECT, FILE_ANY_ACCESS  )
+/* Valid values for nmreq_register.nr_mode (see above). */
+enum {	NR_REG_DEFAULT	= 0,	/* backward compat, should not be used. */
+	NR_REG_ALL_NIC	= 1,
+	NR_REG_SW	= 2,
+	NR_REG_NIC_SW	= 3,
+	NR_REG_ONE_NIC	= 4,
+	NR_REG_PIPE_MASTER = 5, /* deprecated, use "x{y" port name syntax */
+	NR_REG_PIPE_SLAVE = 6,  /* deprecated, use "x}y" port name syntax */
+	NR_REG_NULL     = 7,
+	NR_REG_ONE_SW	= 8,
+};
 
-#define _IOWR(_c, _n, _s)	_IO(_c, _n)
+/* A single ioctl number is shared by all the new API command.
+ * Demultiplexing is done using the hdr.nr_reqtype field.
+ * FreeBSD uses the size value embedded in the _IOWR to determine
+ * how much to copy in/out, so we define the ioctl() command
+ * specifying only nmreq_header, and copyin/copyout the rest. */
+#define NIOCCTRL	_IOWR('i', 151, struct nmreq_header)
 
-/* We havesome internal sysctl in addition to the externally visible ones */
-#define NETMAP_MMAP _IO_direct('i', 160)	// note METHOD_OUT_DIRECT
-#define NETMAP_POLL _IO('i', 162)
+/* The ioctl commands to sync TX/RX netmap rings.
+ * NIOCTXSYNC, NIOCRXSYNC synchronize tx or rx queues,
+ *	whose identity is set in NETMAP_REQ_REGISTER through nr_ringid.
+ *	These are non blocking and take no argument. */
+#define NIOCTXSYNC	_IO('i', 148) /* sync tx queues */
+#define NIOCRXSYNC	_IO('i', 149) /* sync rx queues */
 
-/* and also two setsockopt for sysctl emulation */
-#define NETMAP_SETSOCKOPT _IO('i', 140)
-#define NETMAP_GETSOCKOPT _IO('i', 141)
+/*
+ * nr_reqtype: NETMAP_REQ_PORT_INFO_GET
+ * Get information about a netmap port, including number of rings.
+ * slots per ring, id of the memory allocator, etc. The netmap
+ * control device used for this operation does not need to be bound
+ * to a netmap port.
+ */
+struct nmreq_port_info_get {
+	uint64_t	nr_memsize;	/* size of the shared region */
+	uint32_t	nr_tx_slots;	/* slots in tx rings */
+	uint32_t	nr_rx_slots;	/* slots in rx rings */
+	uint16_t	nr_tx_rings;	/* number of tx rings */
+	uint16_t	nr_rx_rings;	/* number of rx rings */
+	uint16_t	nr_host_tx_rings; /* number of host tx rings */
+	uint16_t	nr_host_rx_rings; /* number of host rx rings */
+	uint16_t	nr_mem_id;	/* memory allocator id (in/out) */
+	uint16_t	pad[3];
+};
 
+#define	NM_BDG_NAME		"vale"	/* prefix for bridge port name */
 
-//These linknames are for the Netmap Core Driver
-#define NETMAP_NT_DEVICE_NAME			L"\\Device\\NETMAP"
-#define NETMAP_DOS_DEVICE_NAME			L"\\DosDevices\\netmap"
+/*
+ * nr_reqtype: NETMAP_REQ_VALE_ATTACH
+ * Attach a netmap port to a VALE switch. Both the name of the netmap
+ * port and the VALE switch are specified through the nr_name argument.
+ * The attach operation could need to register a port, so at least
+ * the same arguments are available.
+ * port_index will contain the index where the port has been attached.
+ */
+struct nmreq_vale_attach {
+	struct nmreq_register reg;
+	uint32_t port_index;
+	uint32_t pad1;
+};
 
-//Definition of a structure used to pass a virtual address within an IOCTL
-typedef struct _MEMORY_ENTRY {
-	PVOID       pUsermodeVirtualAddress;
-} MEMORY_ENTRY, *PMEMORY_ENTRY;
+/*
+ * nr_reqtype: NETMAP_REQ_VALE_DETACH
+ * Detach a netmap port from a VALE switch. Both the name of the netmap
+ * port and the VALE switch are specified through the nr_name argument.
+ * port_index will contain the index where the port was attached.
+ */
+struct nmreq_vale_detach {
+	uint32_t port_index;
+	uint32_t pad1;
+};
 
-typedef struct _POLL_REQUEST_DATA {
-	int events;
-	int timeout;
-	int revents;
-} POLL_REQUEST_DATA;
+/*
+ * nr_reqtype: NETMAP_REQ_VALE_LIST
+ * List the ports of a VALE switch.
+ */
+struct nmreq_vale_list {
+	/* Name of the VALE port (valeXXX:YYY) or empty. */
+	uint16_t	nr_bridge_idx;
+	uint16_t	pad1;
+	uint32_t	nr_port_idx;
+};
 
-#endif /* _WIN32 */
+/*
+ * nr_reqtype: NETMAP_REQ_PORT_HDR_SET or NETMAP_REQ_PORT_HDR_GET
+ * Set or get the port header length of the port identified by hdr.nr_name.
+ * The control device does not need to be bound to a netmap port.
+ */
+struct nmreq_port_hdr {
+	uint32_t	nr_hdr_len;
+	uint32_t	pad1;
+};
 
 /*
- * FreeBSD uses the size value embedded in the _IOWR to determine
- * how much to copy in/out. So we need it to match the actual
- * data structure we pass. We put some spares in the structure
- * to ease compatibility with other versions
+ * nr_reqtype: NETMAP_REQ_VALE_NEWIF
+ * Create a new persistent VALE port.
  */
-#define NIOCGINFO	_IOWR('i', 145, struct nmreq) /* return IF info */
-#define NIOCREGIF	_IOWR('i', 146, struct nmreq) /* interface register */
-#define NIOCTXSYNC	_IO('i', 148) /* sync tx queues */
-#define NIOCRXSYNC	_IO('i', 149) /* sync rx queues */
-#define NIOCCONFIG	_IOWR('i',150, struct nm_ifreq) /* for ext. modules */
-#endif /* !NIOCREGIF */
+struct nmreq_vale_newif {
+	uint32_t	nr_tx_slots;	/* slots in tx rings */
+	uint32_t	nr_rx_slots;	/* slots in rx rings */
+	uint16_t	nr_tx_rings;	/* number of tx rings */
+	uint16_t	nr_rx_rings;	/* number of rx rings */
+	uint16_t	nr_mem_id;	/* id of the memory allocator */
+	uint16_t	pad1;
+};
 
+/*
+ * nr_reqtype: NETMAP_REQ_VALE_POLLING_ENABLE or NETMAP_REQ_VALE_POLLING_DISABLE
+ * Enable or disable polling kthreads on a VALE port.
+ */
+struct nmreq_vale_polling {
+	uint32_t	nr_mode;
+#define NETMAP_POLLING_MODE_SINGLE_CPU 1
+#define NETMAP_POLLING_MODE_MULTI_CPU 2
+	uint32_t	nr_first_cpu_id;
+	uint32_t	nr_num_polling_cpus;
+	uint32_t	pad1;
+};
 
 /*
- * Helper functions for kernel and userspace
+ * nr_reqtype: NETMAP_REQ_POOLS_INFO_GET
+ * Get info about the pools of the memory allocator of the netmap
+ * port specified by hdr.nr_name and nr_mem_id. The netmap control
+ * device used for this operation does not need to be bound to a netmap
+ * port.
  */
+struct nmreq_pools_info {
+	uint64_t	nr_memsize;
+	uint16_t	nr_mem_id; /* in/out argument */
+	uint16_t	pad1[3];
+	uint64_t	nr_if_pool_offset;
+	uint32_t	nr_if_pool_objtotal;
+	uint32_t	nr_if_pool_objsize;
+	uint64_t	nr_ring_pool_offset;
+	uint32_t	nr_ring_pool_objtotal;
+	uint32_t	nr_ring_pool_objsize;
+	uint64_t	nr_buf_pool_offset;
+	uint32_t	nr_buf_pool_objtotal;
+	uint32_t	nr_buf_pool_objsize;
+};
 
 /*
- * check if space is available in the ring.
+ * nr_reqtype: NETMAP_REQ_SYNC_KLOOP_START
+ * Start an in-kernel loop that syncs the rings periodically or on
+ * notifications. The loop runs in the context of the ioctl syscall,
+ * and only stops on NETMAP_REQ_SYNC_KLOOP_STOP.
+ * The registered netmap port must be open in CSB mode.
  */
-static inline int
-nm_ring_empty(struct netmap_ring *ring)
+struct nmreq_sync_kloop_start {
+	/* Sleeping is the default synchronization method for the kloop.
+	 * The 'sleep_us' field specifies how many microseconds to sleep for
+	 * when there is no work to do, before doing another kloop iteration.
+	 */
+	uint32_t	sleep_us;
+	uint32_t	pad1;
+};
+
+/* A CSB entry for the application --> kernel direction. */
+struct nm_csb_atok {
+	uint32_t head;		  /* AW+ KR+ the head of the appl netmap_ring */
+	uint32_t cur;		  /* AW+ KR+ the cur of the appl netmap_ring */
+	uint32_t appl_need_kick;  /* AW+ KR+ kern --> appl notification enable */
+	uint32_t sync_flags;	  /* AW+ KR+ the flags of the appl [tx|rx]sync() */
+	uint32_t pad[12];	  /* pad to a 64 bytes cacheline */
+};
+
+/* A CSB entry for the application <-- kernel direction. */
+struct nm_csb_ktoa {
+	uint32_t hwcur;		  /* AR+ KW+ the hwcur of the kern netmap_kring */
+	uint32_t hwtail;	  /* AR+ KW+ the hwtail of the kern netmap_kring */
+	uint32_t kern_need_kick;  /* AR+ KW+ appl-->kern notification enable */
+	uint32_t pad[13];
+};
+
+#ifdef __linux__
+
+#ifdef __KERNEL__
+#define nm_stst_barrier smp_wmb
+#define nm_ldld_barrier smp_rmb
+#define nm_stld_barrier smp_mb
+#else  /* !__KERNEL__ */
+static inline void nm_stst_barrier(void)
 {
-	return (ring->cur == ring->tail);
+	/* A memory barrier with release semantic has the combined
+	 * effect of a store-store barrier and a load-store barrier,
+	 * which is fine for us. */
+	__atomic_thread_fence(__ATOMIC_RELEASE);
+}
+static inline void nm_ldld_barrier(void)
+{
+	/* A memory barrier with acquire semantic has the combined
+	 * effect of a load-load barrier and a store-load barrier,
+	 * which is fine for us. */
+	__atomic_thread_fence(__ATOMIC_ACQUIRE);
+}
+#endif /* !__KERNEL__ */
+
+#elif defined(__FreeBSD__)
+
+#ifdef _KERNEL
+#define nm_stst_barrier	atomic_thread_fence_rel
+#define nm_ldld_barrier	atomic_thread_fence_acq
+#define nm_stld_barrier	atomic_thread_fence_seq_cst
+#else  /* !_KERNEL */
+
+#ifdef __cplusplus
+#include 
+using std::memory_order_release;
+using std::memory_order_acquire;
+
+#else /* __cplusplus */
+#include 
+#endif /* __cplusplus */
+
+static inline void nm_stst_barrier(void)
+{
+	atomic_thread_fence(memory_order_release);
+}
+static inline void nm_ldld_barrier(void)
+{
+	atomic_thread_fence(memory_order_acquire);
+}
+#endif /* !_KERNEL */
+
+#else  /* !__linux__ && !__FreeBSD__ */
+#error "OS not supported"
+#endif /* !__linux__ && !__FreeBSD__ */
+
+/* Application side of sync-kloop: Write ring pointers (cur, head) to the CSB.
+ * This routine is coupled with sync_kloop_kernel_read(). */
+static inline void
+nm_sync_kloop_appl_write(struct nm_csb_atok *atok, uint32_t cur,
+			 uint32_t head)
+{
+	/* Issue a first store-store barrier to make sure writes to the
+	 * netmap ring do not overcome updates on atok->cur and atok->head. */
+	nm_stst_barrier();
+
+	/*
+	 * We need to write cur and head to the CSB but we cannot do it atomically.
+	 * There is no way we can prevent the host from reading the updated value
+	 * of one of the two and the old value of the other. However, if we make
+	 * sure that the host never reads a value of head more recent than the
+	 * value of cur we are safe. We can allow the host to read a value of cur
+	 * more recent than the value of head, since in the netmap ring cur can be
+	 * ahead of head and cur cannot wrap around head because it must be behind
+	 * tail. Inverting the order of writes below could instead result into the
+	 * host to think head went ahead of cur, which would cause the sync
+	 * prologue to fail.
+	 *
+	 * The following memory barrier scheme is used to make this happen:
+	 *
+	 *          Guest                Host
+	 *
+	 *          STORE(cur)           LOAD(head)
+	 *          wmb() <----------->  rmb()
+	 *          STORE(head)          LOAD(cur)
+	 *
+	 */
+	atok->cur = cur;
+	nm_stst_barrier();
+	atok->head = head;
+}
+
+/* Application side of sync-kloop: Read kring pointers (hwcur, hwtail) from
+ * the CSB. This routine is coupled with sync_kloop_kernel_write(). */
+static inline void
+nm_sync_kloop_appl_read(struct nm_csb_ktoa *ktoa, uint32_t *hwtail,
+			uint32_t *hwcur)
+{
+	/*
+	 * We place a memory barrier to make sure that the update of hwtail never
+	 * overtakes the update of hwcur.
+	 * (see explanation in sync_kloop_kernel_write).
+	 */
+	*hwtail = ktoa->hwtail;
+	nm_ldld_barrier();
+	*hwcur = ktoa->hwcur;
+
+	/* Make sure that loads from ktoa->hwtail and ktoa->hwcur are not delayed
+	 * after the loads from the netmap ring. */
+	nm_ldld_barrier();
 }
 
 /*
- * Opaque structure that is passed to an external kernel
- * module via ioctl(fd, NIOCCONFIG, req) for a user-owned
- * bridge port (at this point ephemeral VALE interface).
+ * data for NETMAP_REQ_OPT_* options
  */
-#define NM_IFRDATA_LEN 256
-struct nm_ifreq {
-	char nifr_name[IFNAMSIZ];
-	char data[NM_IFRDATA_LEN];
+
+struct nmreq_opt_sync_kloop_eventfds {
+	struct nmreq_option	nro_opt;	/* common header */
+	/* An array of N entries for bidirectional notifications between
+	 * the kernel loop and the application. The number of entries and
+	 * their order must agree with the CSB arrays passed in the
+	 * NETMAP_REQ_OPT_CSB option. Each entry contains a file descriptor
+	 * backed by an eventfd.
+	 *
+	 * If any of the 'ioeventfd' entries is < 0, the event loop uses
+	 * the sleeping synchronization strategy (according to sleep_us),
+	 * and keeps kern_need_kick always disabled.
+	 * Each 'irqfd' can be < 0, and in that case the corresponding queue
+	 * is never notified.
+	 */
+	struct {
+		/* Notifier for the application --> kernel loop direction. */
+		int32_t ioeventfd;
+		/* Notifier for the kernel loop --> application direction. */
+		int32_t irqfd;
+	} eventfds[];
+};
+
+struct nmreq_opt_sync_kloop_mode {
+	struct nmreq_option	nro_opt;	/* common header */
+#define NM_OPT_SYNC_KLOOP_DIRECT_TX (1 << 0)
+#define NM_OPT_SYNC_KLOOP_DIRECT_RX (1 << 1)
+	uint32_t mode;
+};
+
+struct nmreq_opt_extmem {
+	struct nmreq_option	nro_opt;	/* common header */
+	uint64_t		nro_usrptr;	/* (in) ptr to usr memory */
+	struct nmreq_pools_info	nro_info;	/* (in/out) */
+};
+
+struct nmreq_opt_csb {
+	struct nmreq_option	nro_opt;
+
+	/* Array of CSB entries for application --> kernel communication
+	 * (N entries). */
+	uint64_t		csb_atok;
+
+	/* Array of CSB entries for kernel --> application communication
+	 * (N entries). */
+	uint64_t		csb_ktoa;
+};
+
+/* option NETMAP_REQ_OPT_OFFSETS */
+struct nmreq_opt_offsets {
+	struct nmreq_option	nro_opt;
+	/* the user must declare the maximum offset value that she is
+	 * going to put into the offset slot-fields. Any larger value
+	 * found at runtime will be cropped. On output the (possibly
+	 * higher) effective max value is returned.
+	 */
+	uint64_t		nro_max_offset;
+	/* optional initial offset value, to be set in all slots. */
+	uint64_t		nro_initial_offset;
+	/* number of bits in the lower part of the 'ptr' field to be
+	 * used as the offset field. On output the (possibly larger)
+	 * effective number of bits is returned.
+	 * 0 means: use the whole ptr field.
+	 */
+	uint32_t		nro_offset_bits;
+	/* required alignment for the beginning of the packets
+	 * (base of the buffer plus offset) in the TX slots.
+	 */
+	uint32_t		nro_tx_align;
+	/* Reserved: set to zero. */
+	uint64_t		nro_min_gap;
 };
 
 #endif /* _NET_NETMAP_H_ */
diff --git a/sys/net/netmap_legacy.h b/sys/net/netmap_legacy.h
new file mode 100644
index 000000000..ccf9c5b3d
--- /dev/null
+++ b/sys/net/netmap_legacy.h
@@ -0,0 +1,255 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``S IS''AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NET_NETMAP_LEGACY_H_
+#define _NET_NETMAP_LEGACY_H_
+
+/*
+ * ioctl names and related fields
+ *
+ * NIOCTXSYNC, NIOCRXSYNC synchronize tx or rx queues,
+ *	whose identity is set in NIOCREGIF through nr_ringid.
+ *	These are non blocking and take no argument.
+ *
+ * NIOCGINFO takes a struct ifreq, the interface name is the input,
+ *	the outputs are number of queues and number of descriptor
+ *	for each queue (useful to set number of threads etc.).
+ *	The info returned is only advisory and may change before
+ *	the interface is bound to a file descriptor.
+ *
+ * NIOCREGIF takes an interface name within a struct nmre,
+ *	and activates netmap mode on the interface (if possible).
+ *
+ * The argument to NIOCGINFO/NIOCREGIF overlays struct ifreq so we
+ * can pass it down to other NIC-related ioctls.
+ *
+ * The actual argument (struct nmreq) has a number of options to request
+ * different functions.
+ * The following are used in NIOCREGIF when nr_cmd == 0:
+ *
+ * nr_name	(in)
+ *	The name of the port (em0, valeXXX:YYY, etc.)
+ *	limited to IFNAMSIZ for backward compatibility.
+ *
+ * nr_version	(in/out)
+ *	Must match NETMAP_API as used in the kernel, error otherwise.
+ *	Always returns the desired value on output.
+ *
+ * nr_tx_slots, nr_tx_slots, nr_tx_rings, nr_rx_rings (in/out)
+ *	On input, non-zero values may be used to reconfigure the port
+ *	according to the requested values, but this is not guaranteed.
+ *	On output the actual values in use are reported.
+ *
+ * nr_ringid (in)
+ *	Indicates how rings should be bound to the file descriptors.
+ *	If nr_flags != 0, then the low bits (in NETMAP_RING_MASK)
+ *	are used to indicate the ring number, and nr_flags specifies
+ *	the actual rings to bind. NETMAP_NO_TX_POLL is unaffected.
+ *
+ *	NOTE: THE FOLLOWING (nr_flags == 0) IS DEPRECATED:
+ *	If nr_flags == 0, NETMAP_HW_RING and NETMAP_SW_RING control
+ *	the binding as follows:
+ *	0 (default)			binds all physical rings
+ *	NETMAP_HW_RING | ring number	binds a single ring pair
+ *	NETMAP_SW_RING			binds only the host tx/rx rings
+ *
+ *	NETMAP_NO_TX_POLL can be OR-ed to make select()/poll() push
+ *		packets on tx rings only if POLLOUT is set.
+ *		The default is to push any pending packet.
+ *
+ *	NETMAP_DO_RX_POLL can be OR-ed to make select()/poll() release
+ *		packets on rx rings also when POLLIN is NOT set.
+ *		The default is to touch the rx ring only with POLLIN.
+ *		Note that this is the opposite of TX because it
+ *		reflects the common usage.
+ *
+ *	NOTE: NETMAP_PRIV_MEM IS DEPRECATED, use nr_arg2 instead.
+ *	NETMAP_PRIV_MEM is set on return for ports that do not use
+ *		the global memory allocator.
+ *		This information is not significant and applications
+ *		should look at the region id in nr_arg2
+ *
+ * nr_flags	is the recommended mode to indicate which rings should
+ *		be bound to a file descriptor. Values are NR_REG_*
+ *
+ * nr_arg1 (in)	Reserved.
+ *
+ * nr_arg2 (in/out) The identity of the memory region used.
+ *		On input, 0 means the system decides autonomously,
+ *		other values may try to select a specific region.
+ *		On return the actual value is reported.
+ *		Region '1' is the global allocator, normally shared
+ *		by all interfaces. Other values are private regions.
+ *		If two ports the same region zero-copy is possible.
+ *
+ * nr_arg3 (in/out)	number of extra buffers to be allocated.
+ *
+ *
+ *
+ * nr_cmd (in)	if non-zero indicates a special command:
+ *	NETMAP_BDG_ATTACH	 and nr_name = vale*:ifname
+ *		attaches the NIC to the switch; nr_ringid specifies
+ *		which rings to use. Used by vale-ctl -a ...
+ *	    nr_arg1 = NETMAP_BDG_HOST also attaches the host port
+ *		as in vale-ctl -h ...
+ *
+ *	NETMAP_BDG_DETACH	and nr_name = vale*:ifname
+ *		disconnects a previously attached NIC.
+ *		Used by vale-ctl -d ...
+ *
+ *	NETMAP_BDG_LIST
+ *		list the configuration of VALE switches.
+ *
+ *	NETMAP_BDG_VNET_HDR
+ *		Set the virtio-net header length used by the client
+ *		of a VALE switch port.
+ *
+ *	NETMAP_BDG_NEWIF
+ *		create a persistent VALE port with name nr_name.
+ *		Used by vale-ctl -n ...
+ *
+ *	NETMAP_BDG_DELIF
+ *		delete a persistent VALE port. Used by vale-ctl -d ...
+ *
+ * nr_arg1, nr_arg2, nr_arg3  (in/out)		command specific
+ *
+ *
+ *
+ */
+
+
+/*
+ * struct nmreq overlays a struct ifreq (just the name)
+ */
+struct nmreq {
+	char		nr_name[IFNAMSIZ];
+	uint32_t	nr_version;	/* API version */
+	uint32_t	nr_offset;	/* nifp offset in the shared region */
+	uint32_t	nr_memsize;	/* size of the shared region */
+	uint32_t	nr_tx_slots;	/* slots in tx rings */
+	uint32_t	nr_rx_slots;	/* slots in rx rings */
+	uint16_t	nr_tx_rings;	/* number of tx rings */
+	uint16_t	nr_rx_rings;	/* number of rx rings */
+
+	uint16_t	nr_ringid;	/* ring(s) we care about */
+#define NETMAP_HW_RING		0x4000	/* single NIC ring pair */
+#define NETMAP_SW_RING		0x2000	/* only host ring pair */
+
+#define NETMAP_RING_MASK	0x0fff	/* the ring number */
+
+#define NETMAP_NO_TX_POLL	0x1000	/* no automatic txsync on poll */
+
+#define NETMAP_DO_RX_POLL	0x8000	/* DO automatic rxsync on poll */
+
+	uint16_t	nr_cmd;
+#define NETMAP_BDG_ATTACH	1	/* attach the NIC */
+#define NETMAP_BDG_DETACH	2	/* detach the NIC */
+#define NETMAP_BDG_REGOPS	3	/* register bridge callbacks */
+#define NETMAP_BDG_LIST		4	/* get bridge's info */
+#define NETMAP_BDG_VNET_HDR     5       /* set the port virtio-net-hdr length */
+#define NETMAP_BDG_NEWIF	6	/* create a virtual port */
+#define NETMAP_BDG_DELIF	7	/* destroy a virtual port */
+#define NETMAP_PT_HOST_CREATE	8	/* create ptnetmap kthreads */
+#define NETMAP_PT_HOST_DELETE	9	/* delete ptnetmap kthreads */
+#define NETMAP_BDG_POLLING_ON	10	/* delete polling kthread */
+#define NETMAP_BDG_POLLING_OFF	11	/* delete polling kthread */
+#define NETMAP_VNET_HDR_GET	12      /* get the port virtio-net-hdr length */
+	uint16_t	nr_arg1;	/* extra arguments */
+#define NETMAP_BDG_HOST		1	/* nr_arg1 value for NETMAP_BDG_ATTACH */
+
+	uint16_t	nr_arg2;	/* id of the memory allocator */
+	uint32_t	nr_arg3;	/* req. extra buffers in NIOCREGIF */
+	uint32_t	nr_flags;	/* specify NR_REG_* mode and other flags */
+#define NR_REG_MASK		0xf /* to extract NR_REG_* mode from nr_flags */
+	/* various modes, extends nr_ringid */
+	uint32_t	spare2[1];
+};
+
+#ifdef _WIN32
+/*
+ * Windows does not have _IOWR(). _IO(), _IOW() and _IOR() are defined
+ * in ws2def.h but not sure if they are in the form we need.
+ * We therefore redefine them in a convenient way to use for DeviceIoControl
+ * signatures.
+ */
+#undef _IO	// ws2def.h
+#define _WIN_NM_IOCTL_TYPE 40000
+#define _IO(_c, _n)	CTL_CODE(_WIN_NM_IOCTL_TYPE, ((_n) + 0x800) , \
+		METHOD_BUFFERED, FILE_ANY_ACCESS  )
+#define _IO_direct(_c, _n)	CTL_CODE(_WIN_NM_IOCTL_TYPE, ((_n) + 0x800) , \
+		METHOD_OUT_DIRECT, FILE_ANY_ACCESS  )
+
+#define _IOWR(_c, _n, _s)	_IO(_c, _n)
+
+/* We havesome internal sysctl in addition to the externally visible ones */
+#define NETMAP_MMAP _IO_direct('i', 160)	// note METHOD_OUT_DIRECT
+#define NETMAP_POLL _IO('i', 162)
+
+/* and also two setsockopt for sysctl emulation */
+#define NETMAP_SETSOCKOPT _IO('i', 140)
+#define NETMAP_GETSOCKOPT _IO('i', 141)
+
+
+/* These linknames are for the Netmap Core Driver */
+#define NETMAP_NT_DEVICE_NAME			L"\\Device\\NETMAP"
+#define NETMAP_DOS_DEVICE_NAME			L"\\DosDevices\\netmap"
+
+/* Definition of a structure used to pass a virtual address within an IOCTL */
+typedef struct _MEMORY_ENTRY {
+	PVOID       pUsermodeVirtualAddress;
+} MEMORY_ENTRY, *PMEMORY_ENTRY;
+
+typedef struct _POLL_REQUEST_DATA {
+	int events;
+	int timeout;
+	int revents;
+} POLL_REQUEST_DATA;
+#endif /* _WIN32 */
+
+/*
+ * Opaque structure that is passed to an external kernel
+ * module via ioctl(fd, NIOCCONFIG, req) for a user-owned
+ * bridge port (at this point ephemeral VALE interface).
+ */
+#define NM_IFRDATA_LEN 256
+struct nm_ifreq {
+	char nifr_name[IFNAMSIZ];
+	char data[NM_IFRDATA_LEN];
+};
+
+/*
+ * FreeBSD uses the size value embedded in the _IOWR to determine
+ * how much to copy in/out. So we need it to match the actual
+ * data structure we pass. We put some spares in the structure
+ * to ease compatibility with other versions
+ */
+#define NIOCGINFO	_IOWR('i', 145, struct nmreq) /* return IF info */
+#define NIOCREGIF	_IOWR('i', 146, struct nmreq) /* interface register */
+#define NIOCCONFIG	_IOWR('i',150, struct nm_ifreq) /* for ext. modules */
+
+#endif /* _NET_NETMAP_LEGACY_H_ */
diff --git a/sys/net/netmap_user.h b/sys/net/netmap_user.h
index 758084c1d..0a9cbac05 100644
--- a/sys/net/netmap_user.h
+++ b/sys/net/netmap_user.h
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 2011-2016 Universita` di Pisa
  * All rights reserved.
  *
@@ -26,8 +28,6 @@
  */
 
 /*
- * $FreeBSD$
- *
  * Functions and macros to manipulate netmap structures and packets
  * in userspace. See netmap(4) for more information.
  *
@@ -53,7 +53,7 @@
  * To compute the next index in a circular ring you can use
  *	i = nm_ring_next(ring, i);
  *
- * To ease porting apps from pcap to netmap we supply a few fuctions
+ * To ease porting apps from pcap to netmap we supply a few functions
  * that can be called to open, close, read and write on netmap in a way
  * similar to libpcap. Note that the read/write function depend on
  * an ioctl()/select()/poll() being issued to refill rings or push
@@ -91,6 +91,8 @@
 #include 		/* apple needs sockaddr */
 #include 		/* IFNAMSIZ */
 #include 
+#include 	/* memset */
+#include    /* gettimeofday */
 
 #ifndef likely
 #define likely(x)	__builtin_expect(!!(x), 1)
@@ -109,15 +111,31 @@
 	nifp, (nifp)->ring_ofs[index] )
 
 #define NETMAP_RXRING(nifp, index) _NETMAP_OFFSET(struct netmap_ring *,	\
-	nifp, (nifp)->ring_ofs[index + (nifp)->ni_tx_rings + 1] )
+	nifp, (nifp)->ring_ofs[index + (nifp)->ni_tx_rings + 		\
+		(nifp)->ni_host_tx_rings] )
 
 #define NETMAP_BUF(ring, index)				\
-	((char *)(ring) + (ring)->buf_ofs + ((index)*(ring)->nr_buf_size))
+	((char *)(ring) + (ring)->buf_ofs + ((size_t)(index)*(ring)->nr_buf_size))
 
 #define NETMAP_BUF_IDX(ring, buf)			\
 	( ((char *)(buf) - ((char *)(ring) + (ring)->buf_ofs) ) / \
 		(ring)->nr_buf_size )
 
+/* read the offset field in a ring's slot */
+#define NETMAP_ROFFSET(ring, slot)			\
+	((slot)->ptr & (ring)->offset_mask)
+
+/* update the offset field in a ring's slot */
+#define NETMAP_WOFFSET(ring, slot, offset)		\
+	do { (slot)->ptr = ((slot)->ptr & ~(ring)->offset_mask) | \
+		((offset) & (ring)->offset_mask); } while (0)
+
+/* obtain the start of the buffer pointed to by  a ring's slot, taking the
+ * offset field into account
+ */
+#define NETMAP_BUF_OFFSET(ring, slot)			\
+	(NETMAP_BUF(ring, (slot)->buf_idx) + NETMAP_ROFFSET(ring, slot))
+
 
 static inline uint32_t
 nm_ring_next(struct netmap_ring *r, uint32_t i)
@@ -136,37 +154,17 @@ nm_tx_pending(struct netmap_ring *r)
 	return nm_ring_next(r, r->tail) != r->head;
 }
 
-
+/* Compute the number of slots available in the netmap ring. We use
+ * ring->head as explained in the comment above nm_ring_empty(). */
 static inline uint32_t
 nm_ring_space(struct netmap_ring *ring)
 {
-        int ret = ring->tail - ring->cur;
+        int ret = ring->tail - ring->head;
         if (ret < 0)
                 ret += ring->num_slots;
         return ret;
 }
 
-
-#ifdef NETMAP_WITH_LIBS
-/*
- * Support for simple I/O libraries.
- * Include other system headers required for compiling this.
- */
-
-#ifndef HAVE_NETMAP_WITH_LIBS
-#define HAVE_NETMAP_WITH_LIBS
-
-#include 
-#include 
-#include 
-#include 	/* memset */
-#include 
-#include 	/* EINVAL */
-#include 	/* O_RDWR */
-#include 	/* close() */
-#include 
-#include 
-
 #ifndef ND /* debug macros */
 /* debug support */
 #define ND(_fmt, ...) do {} while(0)
@@ -195,6 +193,53 @@ nm_ring_space(struct netmap_ring *ring)
     } while (0)
 #endif
 
+/*
+ * this is a slightly optimized copy routine which rounds
+ * to multiple of 64 bytes and is often faster than dealing
+ * with other odd sizes. We assume there is enough room
+ * in the source and destination buffers.
+ */
+static inline void
+nm_pkt_copy(const void *_src, void *_dst, int l)
+{
+	const uint64_t *src = (const uint64_t *)_src;
+	uint64_t *dst = (uint64_t *)_dst;
+
+	if (unlikely(l >= 1024 || l % 64)) {
+		memcpy(dst, src, l);
+		return;
+	}
+	for (; likely(l > 0); l-=64) {
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+	}
+}
+
+#ifdef NETMAP_WITH_LIBS
+/*
+ * Support for simple I/O libraries.
+ * Include other system headers required for compiling this.
+ */
+
+#ifndef HAVE_NETMAP_WITH_LIBS
+#define HAVE_NETMAP_WITH_LIBS
+
+#include 
+#include 
+#include 
+#include 
+#include 	/* EINVAL */
+#include 	/* O_RDWR */
+#include 	/* close() */
+#include 
+#include 
+
 struct nm_pkthdr {	/* first part is the same as pcap_pkthdr */
 	struct timeval	ts;
 	uint32_t	caplen;
@@ -222,7 +267,7 @@ struct nm_desc {
 	struct nm_desc *self; /* point to self if netmap. */
 	int fd;
 	void *mem;
-	uint32_t memsize;
+	size_t memsize;
 	int done_mmap;	/* set if mem is the result of mmap */
 	struct netmap_if * const nifp;
 	uint16_t first_tx_ring, last_tx_ring, cur_tx_ring;
@@ -260,42 +305,10 @@ struct nm_desc {
  * when the descriptor is open correctly, d->self == d
  * Eventually we should also use some magic number.
  */
-#define P2NMD(p)		((struct nm_desc *)(p))
+#define P2NMD(p)		((const struct nm_desc *)(p))
 #define IS_NETMAP_DESC(d)	((d) && P2NMD(d)->self == P2NMD(d))
 #define NETMAP_FD(d)		(P2NMD(d)->fd)
 
-
-/*
- * this is a slightly optimized copy routine which rounds
- * to multiple of 64 bytes and is often faster than dealing
- * with other odd sizes. We assume there is enough room
- * in the source and destination buffers.
- *
- * XXX only for multiples of 64 bytes, non overlapped.
- */
-static inline void
-nm_pkt_copy(const void *_src, void *_dst, int l)
-{
-	const uint64_t *src = (const uint64_t *)_src;
-	uint64_t *dst = (uint64_t *)_dst;
-
-	if (unlikely(l >= 1024)) {
-		memcpy(dst, src, l);
-		return;
-	}
-	for (; likely(l > 0); l-=64) {
-		*dst++ = *src++;
-		*dst++ = *src++;
-		*dst++ = *src++;
-		*dst++ = *src++;
-		*dst++ = *src++;
-		*dst++ = *src++;
-		*dst++ = *src++;
-		*dst++ = *src++;
-	}
-}
-
-
 /*
  * The callback, invoked on each received packet. Same as libpcap
  */
@@ -307,7 +320,7 @@ typedef void (*nm_cb_t)(u_char *, const struct nm_pkthdr *, const u_char *d);
  * nm_open() opens a file descriptor, binds to a port and maps memory.
  *
  * ifname	(netmap:foo or vale:foo) is the port name
- *		a suffix can indicate the follwing:
+ *		a suffix can indicate the following:
  *		^		bind the host (sw) ring pair
  *		*		bind host and NIC ring pairs
  *		-NN		bind individual NIC ring pair
@@ -611,39 +624,21 @@ nm_is_identifier(const char *s, const char *e)
 	return 1;
 }
 
-/*
- * Try to open, return descriptor if successful, NULL otherwise.
- * An invalid netmap name will return errno = 0;
- * You can pass a pointer to a pre-filled nm_desc to add special
- * parameters. Flags is used as follows
- * NM_OPEN_NO_MMAP	use the memory from arg, only XXX avoid mmap
- *			if the nr_arg2 (memory block) matches.
- * NM_OPEN_ARG1		use req.nr_arg1 from arg
- * NM_OPEN_ARG2		use req.nr_arg2 from arg
- * NM_OPEN_RING_CFG	user ring config from arg
- */
-static struct nm_desc *
-nm_open(const char *ifname, const struct nmreq *req,
-	uint64_t new_flags, const struct nm_desc *arg)
+#define MAXERRMSG 80
+static int
+nm_parse(const char *ifname, struct nm_desc *d, char *err)
 {
-	struct nm_desc *d = NULL;
-	const struct nm_desc *parent = arg;
-	u_int namelen;
-	uint32_t nr_ringid = 0, nr_flags, nr_reg;
+	int is_vale;
 	const char *port = NULL;
 	const char *vpname = NULL;
-#define MAXERRMSG 80
-	char errmsg[MAXERRMSG] = "";
-	enum { P_START, P_RNGSFXOK, P_GETNUM, P_FLAGS, P_FLAGSOK, P_MEMID } p_state;
-	int is_vale;
+	u_int namelen;
+	uint32_t nr_ringid = 0, nr_flags;
+	char errmsg[MAXERRMSG] = "", *tmp;
 	long num;
 	uint16_t nr_arg2 = 0;
+	enum { P_START, P_RNGSFXOK, P_GETNUM, P_FLAGS, P_FLAGSOK, P_MEMID } p_state;
 
-	if (strncmp(ifname, "netmap:", 7) &&
-			strncmp(ifname, NM_BDG_NAME, strlen(NM_BDG_NAME))) {
-		errno = 0; /* name not recognised, not an error */
-		return NULL;
-	}
+	errno = 0;
 
 	is_vale = (ifname[0] == 'v');
 	if (is_vale) {
@@ -679,6 +674,9 @@ nm_open(const char *ifname, const struct nmreq *req,
 		snprintf(errmsg, MAXERRMSG, "name too long");
 		goto fail;
 	}
+	memcpy(d->req.nr_name, ifname, namelen);
+	d->req.nr_name[namelen] = '\0';
+
 	p_state = P_START;
 	nr_flags = NR_REG_ALL_NIC; /* default for no suffix */
 	while (*port) {
@@ -701,7 +699,7 @@ nm_open(const char *ifname, const struct nmreq *req,
 				nr_flags = NR_REG_PIPE_MASTER;
 				p_state = P_GETNUM;
 				break;
-			case '}': /* pipe (slave endoint) */
+			case '}': /* pipe (slave endpoint) */
 				nr_flags = NR_REG_PIPE_SLAVE;
 				p_state = P_GETNUM;
 				break;
@@ -732,12 +730,13 @@ nm_open(const char *ifname, const struct nmreq *req,
 			port++;
 			break;
 		case P_GETNUM:
-			num = strtol(port, (char **)&port, 10);
+			num = strtol(port, &tmp, 10);
 			if (num < 0 || num >= NETMAP_RING_MASK) {
 				snprintf(errmsg, MAXERRMSG, "'%ld' out of range [0, %d)",
 						num, NETMAP_RING_MASK);
 				goto fail;
 			}
+			port = tmp;
 			nr_ringid = num & NETMAP_RING_MASK;
 			p_state = P_RNGSFXOK;
 			break;
@@ -779,11 +778,12 @@ nm_open(const char *ifname, const struct nmreq *req,
 				snprintf(errmsg, MAXERRMSG, "double setting of memid");
 				goto fail;
 			}
-			num = strtol(port, (char **)&port, 10);
+			num = strtol(port, &tmp, 10);
 			if (num <= 0) {
 				snprintf(errmsg, MAXERRMSG, "invalid memid %ld, must be >0", num);
 				goto fail;
 			}
+			port = tmp;
 			nr_arg2 = num;
 			p_state = P_RNGSFXOK;
 			break;
@@ -798,6 +798,48 @@ nm_open(const char *ifname, const struct nmreq *req,
 			(nr_flags & NR_ZCOPY_MON) ? "ZCOPY_MON" : "",
 			(nr_flags & NR_MONITOR_TX) ? "MONITOR_TX" : "",
 			(nr_flags & NR_MONITOR_RX) ? "MONITOR_RX" : "");
+
+	d->req.nr_flags |= nr_flags;
+	d->req.nr_ringid |= nr_ringid;
+	d->req.nr_arg2 = nr_arg2;
+
+	d->self = d;
+
+	return 0;
+fail:
+	if (!errno)
+		errno = EINVAL;
+	if (err)
+		strncpy(err, errmsg, MAXERRMSG);
+	return -1;
+}
+
+/*
+ * Try to open, return descriptor if successful, NULL otherwise.
+ * An invalid netmap name will return errno = 0;
+ * You can pass a pointer to a pre-filled nm_desc to add special
+ * parameters. Flags is used as follows
+ * NM_OPEN_NO_MMAP	use the memory from arg, only XXX avoid mmap
+ *			if the nr_arg2 (memory block) matches.
+ * NM_OPEN_ARG1		use req.nr_arg1 from arg
+ * NM_OPEN_ARG2		use req.nr_arg2 from arg
+ * NM_OPEN_RING_CFG	user ring config from arg
+ */
+static struct nm_desc *
+nm_open(const char *ifname, const struct nmreq *req,
+	uint64_t new_flags, const struct nm_desc *arg)
+{
+	struct nm_desc *d = NULL;
+	const struct nm_desc *parent = arg;
+	char errmsg[MAXERRMSG] = "";
+	uint32_t nr_reg;
+
+	if (strncmp(ifname, "netmap:", 7) &&
+			strncmp(ifname, NM_BDG_NAME, strlen(NM_BDG_NAME))) {
+		errno = 0; /* name not recognised, not an error */
+		return NULL;
+	}
+
 	d = (struct nm_desc *)calloc(1, sizeof(*d));
 	if (d == NULL) {
 		snprintf(errmsg, MAXERRMSG, "nm_desc alloc failure");
@@ -813,26 +855,25 @@ nm_open(const char *ifname, const struct nmreq *req,
 
 	if (req)
 		d->req = *req;
+
+	if (!(new_flags & NM_OPEN_IFNAME)) {
+		if (nm_parse(ifname, d, errmsg) < 0)
+			goto fail;
+	}
+
 	d->req.nr_version = NETMAP_API;
-	d->req.nr_ringid &= ~NETMAP_RING_MASK;
+	d->req.nr_ringid &= NETMAP_RING_MASK;
 
-	/* these fields are overridden by ifname and flags processing */
-	d->req.nr_ringid |= nr_ringid;
-	d->req.nr_flags |= nr_flags;
-	if (nr_arg2)
-		d->req.nr_arg2 = nr_arg2;
-	memcpy(d->req.nr_name, ifname, namelen);
-	d->req.nr_name[namelen] = '\0';
 	/* optionally import info from parent */
 	if (IS_NETMAP_DESC(parent) && new_flags) {
 		if (new_flags & NM_OPEN_ARG1)
 			D("overriding ARG1 %d", parent->req.nr_arg1);
 		d->req.nr_arg1 = new_flags & NM_OPEN_ARG1 ?
 			parent->req.nr_arg1 : 4;
-		if (new_flags & NM_OPEN_ARG2)
+		if (new_flags & NM_OPEN_ARG2) {
 			D("overriding ARG2 %d", parent->req.nr_arg2);
-		d->req.nr_arg2 = new_flags & NM_OPEN_ARG2 ?
-			parent->req.nr_arg2 : 0;
+			d->req.nr_arg2 =  parent->req.nr_arg2;
+		}
 		if (new_flags & NM_OPEN_ARG3)
 			D("overriding ARG3 %d", parent->req.nr_arg3);
 		d->req.nr_arg3 = new_flags & NM_OPEN_ARG3 ?
@@ -862,12 +903,6 @@ nm_open(const char *ifname, const struct nmreq *req,
 		goto fail;
 	}
 
-        /* if parent is defined, do nm_mmap() even if NM_OPEN_NO_MMAP is set */
-	if ((!(new_flags & NM_OPEN_NO_MMAP) || parent) && nm_mmap(d, parent)) {
-	        snprintf(errmsg, MAXERRMSG, "mmap failed: %s", strerror(errno));
-		goto fail;
-	}
-
 	nr_reg = d->req.nr_flags & NR_REG_MASK;
 
 	if (nr_reg == NR_REG_SW) { /* host stack */
@@ -892,6 +927,13 @@ nm_open(const char *ifname, const struct nmreq *req,
 		d->first_rx_ring = d->last_rx_ring = 0;
 	}
 
+        /* if parent is defined, do nm_mmap() even if NM_OPEN_NO_MMAP is set */
+	if ((!(new_flags & NM_OPEN_NO_MMAP) || parent) && nm_mmap(d, parent)) {
+	        snprintf(errmsg, MAXERRMSG, "mmap failed: %s", strerror(errno));
+		goto fail;
+	}
+
+
 #ifdef DEBUG_NETMAP_USER
     { /* debugging code */
 	int i;
@@ -942,7 +984,7 @@ nm_close(struct nm_desc *d)
 		close(d->fd);
 	}
 
-	bzero(d, sizeof(*d));
+	bzero((char *)d, sizeof(*d));
 	free(d);
 	return 0;
 }
@@ -951,7 +993,8 @@ nm_close(struct nm_desc *d)
 static int
 nm_mmap(struct nm_desc *d, const struct nm_desc *parent)
 {
-	//XXX TODO: check if mmap is already done
+	if (d->done_mmap)
+		return 0;
 
 	if (IS_NETMAP_DESC(parent) && parent->mem &&
 	    parent->req.nr_arg2 == d->req.nr_arg2) {
@@ -971,7 +1014,11 @@ nm_mmap(struct nm_desc *d, const struct nm_desc *parent)
 	}
 	{
 		struct netmap_if *nifp = NETMAP_IF(d->mem, d->req.nr_offset);
-		struct netmap_ring *r = NETMAP_RXRING(nifp, );
+		struct netmap_ring *r = NETMAP_RXRING(nifp, d->first_rx_ring);
+		if ((void *)r == (void *)nifp) {
+			/* the descriptor is open for TX only */
+			r = NETMAP_TXRING(nifp, d->first_tx_ring);
+		}
 
 		*(struct netmap_if **)(uintptr_t)&(d->nifp) = nifp;
 		*(struct netmap_ring **)(uintptr_t)&d->some_ring = r;
@@ -992,26 +1039,41 @@ nm_mmap(struct nm_desc *d, const struct nm_desc *parent)
 static int
 nm_inject(struct nm_desc *d, const void *buf, size_t size)
 {
-	u_int c, n = d->last_tx_ring - d->first_tx_ring + 1;
+	u_int c, n = d->last_tx_ring - d->first_tx_ring + 1,
+		ri = d->cur_tx_ring;
 
-	for (c = 0; c < n ; c++) {
+	for (c = 0; c < n ; c++, ri++) {
 		/* compute current ring to use */
 		struct netmap_ring *ring;
-		uint32_t i, idx;
-		uint32_t ri = d->cur_tx_ring + c;
+		uint32_t i, j, idx;
+		size_t rem;
 
 		if (ri > d->last_tx_ring)
 			ri = d->first_tx_ring;
 		ring = NETMAP_TXRING(d->nifp, ri);
-		if (nm_ring_empty(ring)) {
-			continue;
+		rem = size;
+		j = ring->cur;
+		while (rem > ring->nr_buf_size && j != ring->tail) {
+			rem -= ring->nr_buf_size;
+			j = nm_ring_next(ring, j);
 		}
+		if (j == ring->tail && rem > 0)
+			continue;
 		i = ring->cur;
+		while (i != j) {
+			idx = ring->slot[i].buf_idx;
+			ring->slot[i].len = ring->nr_buf_size;
+			ring->slot[i].flags = NS_MOREFRAG;
+			nm_pkt_copy(buf, NETMAP_BUF(ring, idx), ring->nr_buf_size);
+			i = nm_ring_next(ring, i);
+			buf = (const char *)buf + ring->nr_buf_size;
+		}
 		idx = ring->slot[i].buf_idx;
-		ring->slot[i].len = size;
-		nm_pkt_copy(buf, NETMAP_BUF(ring, idx), size);
-		d->cur_tx_ring = ri;
+		ring->slot[i].len = rem;
+		ring->slot[i].flags = 0;
+		nm_pkt_copy(buf, NETMAP_BUF(ring, idx), rem);
 		ring->head = ring->cur = nm_ring_next(ring, i);
+		d->cur_tx_ring = ri;
 		return size;
 	}
 	return 0; /* fail */
@@ -1036,25 +1098,45 @@ nm_dispatch(struct nm_desc *d, int cnt, nm_cb_t cb, u_char *arg)
 	 * of buffers and the int is large enough that we never wrap,
 	 * so we can omit checking for -1
 	 */
-	for (c=0; c < n && cnt != got; c++) {
+	for (c=0; c < n && cnt != got; c++, ri++) {
 		/* compute current ring to use */
 		struct netmap_ring *ring;
 
-		ri = d->cur_rx_ring + c;
 		if (ri > d->last_rx_ring)
 			ri = d->first_rx_ring;
 		ring = NETMAP_RXRING(d->nifp, ri);
 		for ( ; !nm_ring_empty(ring) && cnt != got; got++) {
 			u_int idx, i;
+			u_char *oldbuf;
+			struct netmap_slot *slot;
 			if (d->hdr.buf) { /* from previous round */
 				cb(arg, &d->hdr, d->hdr.buf);
 			}
 			i = ring->cur;
-			idx = ring->slot[i].buf_idx;
-			d->hdr.slot = &ring->slot[i];
-			d->hdr.buf = (u_char *)NETMAP_BUF(ring, idx);
+			slot = &ring->slot[i];
+			idx = slot->buf_idx;
+			/* d->cur_rx_ring doesn't change inside this loop, but
+			 * set it here, so it reflects d->hdr.buf's ring */
+			d->cur_rx_ring = ri;
+			d->hdr.slot = slot;
+			oldbuf = d->hdr.buf = (u_char *)NETMAP_BUF(ring, idx);
 			// __builtin_prefetch(buf);
-			d->hdr.len = d->hdr.caplen = ring->slot[i].len;
+			d->hdr.len = d->hdr.caplen = slot->len;
+			while (slot->flags & NS_MOREFRAG) {
+				u_char *nbuf;
+				u_int oldlen = slot->len;
+				i = nm_ring_next(ring, i);
+				slot = &ring->slot[i];
+				d->hdr.len += slot->len;
+				nbuf = (u_char *)NETMAP_BUF(ring, slot->buf_idx);
+				if (oldbuf != NULL && (uint32_t)(nbuf - oldbuf) == ring->nr_buf_size &&
+						oldlen == ring->nr_buf_size) {
+					d->hdr.caplen += slot->len;
+					oldbuf = nbuf;
+				} else {
+					oldbuf = NULL;
+				}
+			}
 			d->hdr.ts = ring->ts;
 			ring->head = ring->cur = nm_ring_next(ring, i);
 		}
@@ -1063,7 +1145,6 @@ nm_dispatch(struct nm_desc *d, int cnt, nm_cb_t cb, u_char *arg)
 		d->hdr.flags = 0;
 		cb(arg, &d->hdr, d->hdr.buf);
 	}
-	d->cur_rx_ring = ri;
 	return got;
 }
 
diff --git a/sys/net/netmap_virt.h b/sys/net/netmap_virt.h
index e5c823ea2..c9921d2c5 100644
--- a/sys/net/netmap_virt.h
+++ b/sys/net/netmap_virt.h
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2013-2016 Luigi Rizzo
  * Copyright (C) 2013-2016 Giuseppe Lettieri
- * Copyright (C) 2013-2016 Vincenzo Maffione
+ * Copyright (C) 2013-2018 Vincenzo Maffione
  * Copyright (C) 2015 Stefano Garzarella
  * All rights reserved.
  *
@@ -25,22 +25,21 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
- * $FreeBSD$
  */
 
 #ifndef NETMAP_VIRT_H
 #define NETMAP_VIRT_H
 
 /*
- * ptnetmap_memdev: device used to expose memory into the guest VM
+ * Register offsets and other macros for the ptnetmap paravirtual devices:
+ *   ptnetmap-memdev: device used to expose memory into the guest
+ *   ptnet: paravirtualized NIC exposing a netmap port in the guest
  *
  * These macros are used in the hypervisor frontend (QEMU, bhyve) and in the
  * guest device driver.
  */
 
-/* PCI identifiers and PCI BARs for the ptnetmap memdev
- * and ptnetmap network interface. */
+/* PCI identifiers and PCI BARs for ptnetmap-memdev and ptnet. */
 #define PTNETMAP_MEMDEV_NAME            "ptnetmap-memdev"
 #define PTNETMAP_PCI_VENDOR_ID          0x1b36  /* QEMU virtual devices */
 #define PTNETMAP_PCI_DEVICE_ID          0x000c  /* memory device */
@@ -49,7 +48,7 @@
 #define PTNETMAP_MEM_PCI_BAR            1
 #define PTNETMAP_MSIX_PCI_BAR           2
 
-/* Registers for the ptnetmap memdev */
+/* Device registers for ptnetmap-memdev */
 #define PTNET_MDEV_IO_MEMSIZE_LO	0	/* netmap memory size (low) */
 #define PTNET_MDEV_IO_MEMSIZE_HI	4	/* netmap_memory_size (high) */
 #define PTNET_MDEV_IO_MEMID		8	/* memory allocator ID in the host */
@@ -64,90 +63,16 @@
 #define PTNET_MDEV_IO_BUF_POOL_OBJSZ	96
 #define PTNET_MDEV_IO_END		100
 
-/*
- * ptnetmap configuration
- *
- * The ptnet kthreads (running in host kernel-space) need to be configured
- * in order to know how to intercept guest kicks (I/O register writes) and
- * how to inject MSI-X interrupts to the guest. The configuration may vary
- * depending on the hypervisor. Currently, we support QEMU/KVM on Linux and
- * and bhyve on FreeBSD.
- * The configuration is passed by the hypervisor to the host netmap module
- * by means of an ioctl() with nr_cmd=NETMAP_PT_HOST_CREATE, and it is
- * specified by the ptnetmap_cfg struct. This struct contains an header
- * with general informations and an array of entries whose size depends
- * on the hypervisor. The NETMAP_PT_HOST_CREATE command is issued every
- * time the kthreads are started.
- */
-struct ptnetmap_cfg {
-#define PTNETMAP_CFGTYPE_QEMU		0x1
-#define PTNETMAP_CFGTYPE_BHYVE		0x2
-	uint16_t cfgtype;	/* how to interpret the cfg entries */
-	uint16_t entry_size;	/* size of a config entry */
-	uint32_t num_rings;	/* number of config entries */
-	void *ptrings;		/* ptrings inside CSB */
-	/* Configuration entries are allocated right after the struct. */
-};
-
-/* Configuration of a ptnetmap ring for QEMU. */
-struct ptnetmap_cfgentry_qemu {
-	uint32_t ioeventfd;	/* to intercept guest register access */
-	uint32_t irqfd;		/* to inject guest interrupts */
-};
-
-/* Configuration of a ptnetmap ring for bhyve. */
-struct ptnetmap_cfgentry_bhyve {
-	uint64_t wchan;		/* tsleep() parameter, to wake up kthread */
-	uint32_t ioctl_fd;	/* ioctl fd */
-	/* ioctl parameters to send irq */
-	uint32_t ioctl_cmd;
-	/* vmm.ko MSIX parameters for IOCTL */
-	struct {
-		uint64_t        msg_data;
-		uint64_t        addr;
-	} ioctl_data;
-};
-
-/*
- * Structure filled-in by the kernel when asked for allocator info
- * through NETMAP_POOLS_INFO_GET. Used by hypervisors supporting
- * ptnetmap.
- */
-struct netmap_pools_info {
-	uint64_t memsize;	/* same as nmr->nr_memsize */
-	uint32_t memid;		/* same as nmr->nr_arg2 */
-	uint32_t if_pool_offset;
-	uint32_t if_pool_objtotal;
-	uint32_t if_pool_objsize;
-	uint32_t ring_pool_offset;
-	uint32_t ring_pool_objtotal;
-	uint32_t ring_pool_objsize;
-	uint32_t buf_pool_offset;
-	uint32_t buf_pool_objtotal;
-	uint32_t buf_pool_objsize;
-};
-
-/*
- * Pass a pointer to a userspace buffer to be passed to kernelspace for write
- * or read. Used by NETMAP_PT_HOST_CREATE and NETMAP_POOLS_INFO_GET.
- */
-static inline void
-nmreq_pointer_put(struct nmreq *nmr, void *userptr)
-{
-	uintptr_t *pp = (uintptr_t *)&nmr->nr_arg1;
-	*pp = (uintptr_t)userptr;
-}
-
 /* ptnetmap features */
 #define PTNETMAP_F_VNET_HDR        1
 
-/* I/O registers for the ptnet device. */
+/* Device registers for the ptnet network device. */
 #define PTNET_IO_PTFEAT		0
 #define PTNET_IO_PTCTL		4
 #define PTNET_IO_MAC_LO		8
 #define PTNET_IO_MAC_HI		12
-#define PTNET_IO_CSBBAH		16
-#define PTNET_IO_CSBBAL		20
+#define PTNET_IO_CSBBAH		16 /* deprecated */
+#define PTNET_IO_CSBBAL		20 /* deprecated */
 #define PTNET_IO_NIFP_OFS	24
 #define PTNET_IO_NUM_TX_RINGS	28
 #define PTNET_IO_NUM_RX_RINGS	32
@@ -155,151 +80,19 @@ nmreq_pointer_put(struct nmreq *nmr, void *userptr)
 #define PTNET_IO_NUM_RX_SLOTS	40
 #define PTNET_IO_VNET_HDR_LEN	44
 #define PTNET_IO_HOSTMEMID	48
-#define PTNET_IO_END		52
+#define PTNET_IO_CSB_GH_BAH     52
+#define PTNET_IO_CSB_GH_BAL     56
+#define PTNET_IO_CSB_HG_BAH     60
+#define PTNET_IO_CSB_HG_BAL     64
+#define PTNET_IO_END		68
 #define PTNET_IO_KICK_BASE	128
 #define PTNET_IO_MASK		0xff
 
-/* ptnetmap control commands (values for PTCTL register) */
+/* ptnet control commands (values for PTCTL register):
+ *   - CREATE starts the host sync-kloop
+ *   - DELETE stops the host sync-kloop
+ */
 #define PTNETMAP_PTCTL_CREATE		1
 #define PTNETMAP_PTCTL_DELETE		2
 
-/* If defined, CSB is allocated by the guest, not by the host. */
-#define PTNET_CSB_ALLOC
-
-/* ptnetmap ring fields shared between guest and host */
-struct ptnet_ring {
-	/* XXX revise the layout to minimize cache bounces. */
-	uint32_t head;		  /* GW+ HR+ the head of the guest netmap_ring */
-	uint32_t cur;		  /* GW+ HR+ the cur of the guest netmap_ring */
-	uint32_t guest_need_kick; /* GW+ HR+ host-->guest notification enable */
-	uint32_t sync_flags;	  /* GW+ HR+ the flags of the guest [tx|rx]sync() */
-	uint32_t hwcur;		  /* GR+ HW+ the hwcur of the host netmap_kring */
-	uint32_t hwtail;	  /* GR+ HW+ the hwtail of the host netmap_kring */
-	uint32_t host_need_kick;  /* GR+ HW+ guest-->host notification enable */
-	char pad[4];
-};
-
-/* CSB for the ptnet device. */
-struct ptnet_csb {
-#define NETMAP_VIRT_CSB_SIZE   4096
-	struct ptnet_ring rings[NETMAP_VIRT_CSB_SIZE/sizeof(struct ptnet_ring)];
-};
-
-#ifdef WITH_PTNETMAP_GUEST
-
-/* ptnetmap_memdev routines used to talk with ptnetmap_memdev device driver */
-struct ptnetmap_memdev;
-int nm_os_pt_memdev_iomap(struct ptnetmap_memdev *, vm_paddr_t *, void **,
-                          uint64_t *);
-void nm_os_pt_memdev_iounmap(struct ptnetmap_memdev *);
-uint32_t nm_os_pt_memdev_ioread(struct ptnetmap_memdev *, unsigned int);
-
-/* Guest driver: Write kring pointers (cur, head) to the CSB.
- * This routine is coupled with ptnetmap_host_read_kring_csb(). */
-static inline void
-ptnetmap_guest_write_kring_csb(struct ptnet_ring *ptr, uint32_t cur,
-			       uint32_t head)
-{
-    /*
-     * We need to write cur and head to the CSB but we cannot do it atomically.
-     * There is no way we can prevent the host from reading the updated value
-     * of one of the two and the old value of the other. However, if we make
-     * sure that the host never reads a value of head more recent than the
-     * value of cur we are safe. We can allow the host to read a value of cur
-     * more recent than the value of head, since in the netmap ring cur can be
-     * ahead of head and cur cannot wrap around head because it must be behind
-     * tail. Inverting the order of writes below could instead result into the
-     * host to think head went ahead of cur, which would cause the sync
-     * prologue to fail.
-     *
-     * The following memory barrier scheme is used to make this happen:
-     *
-     *          Guest              Host
-     *
-     *          STORE(cur)         LOAD(head)
-     *          mb() <-----------> mb()
-     *          STORE(head)        LOAD(cur)
-     */
-    ptr->cur = cur;
-    mb();
-    ptr->head = head;
-}
-
-/* Guest driver: Read kring pointers (hwcur, hwtail) from the CSB.
- * This routine is coupled with ptnetmap_host_write_kring_csb(). */
-static inline void
-ptnetmap_guest_read_kring_csb(struct ptnet_ring *ptr, struct netmap_kring *kring)
-{
-    /*
-     * We place a memory barrier to make sure that the update of hwtail never
-     * overtakes the update of hwcur.
-     * (see explanation in ptnetmap_host_write_kring_csb).
-     */
-    kring->nr_hwtail = ptr->hwtail;
-    mb();
-    kring->nr_hwcur = ptr->hwcur;
-}
-
-#endif /* WITH_PTNETMAP_GUEST */
-
-#ifdef WITH_PTNETMAP_HOST
-/*
- * ptnetmap kernel thread routines
- * */
-
-/* Functions to read and write CSB fields in the host */
-#if defined (linux)
-#define CSB_READ(csb, field, r) (get_user(r, &csb->field))
-#define CSB_WRITE(csb, field, v) (put_user(v, &csb->field))
-#else  /* ! linux */
-#define CSB_READ(csb, field, r) (r = fuword32(&csb->field))
-#define CSB_WRITE(csb, field, v) (suword32(&csb->field, v))
-#endif /* ! linux */
-
-/* Host netmap: Write kring pointers (hwcur, hwtail) to the CSB.
- * This routine is coupled with ptnetmap_guest_read_kring_csb(). */
-static inline void
-ptnetmap_host_write_kring_csb(struct ptnet_ring __user *ptr, uint32_t hwcur,
-        uint32_t hwtail)
-{
-    /*
-     * The same scheme used in ptnetmap_guest_write_kring_csb() applies here.
-     * We allow the guest to read a value of hwcur more recent than the value
-     * of hwtail, since this would anyway result in a consistent view of the
-     * ring state (and hwcur can never wraparound hwtail, since hwcur must be
-     * behind head).
-     *
-     * The following memory barrier scheme is used to make this happen:
-     *
-     *          Guest                Host
-     *
-     *          STORE(hwcur)         LOAD(hwtail)
-     *          mb() <-------------> mb()
-     *          STORE(hwtail)        LOAD(hwcur)
-     */
-    CSB_WRITE(ptr, hwcur, hwcur);
-    mb();
-    CSB_WRITE(ptr, hwtail, hwtail);
-}
-
-/* Host netmap: Read kring pointers (head, cur, sync_flags) from the CSB.
- * This routine is coupled with ptnetmap_guest_write_kring_csb(). */
-static inline void
-ptnetmap_host_read_kring_csb(struct ptnet_ring __user *ptr,
-			     struct netmap_ring *shadow_ring,
-			     uint32_t num_slots)
-{
-    /*
-     * We place a memory barrier to make sure that the update of head never
-     * overtakes the update of cur.
-     * (see explanation in ptnetmap_guest_write_kring_csb).
-     */
-    CSB_READ(ptr, head, shadow_ring->head);
-    mb();
-    CSB_READ(ptr, cur, shadow_ring->cur);
-    CSB_READ(ptr, sync_flags, shadow_ring->flags);
-}
-
-#endif /* WITH_PTNETMAP_HOST */
-
 #endif /* NETMAP_VIRT_H */
diff --git a/utils/GNUmakefile b/utils/GNUmakefile
index 8659b0bf8..2249ae7cd 100644
--- a/utils/GNUmakefile
+++ b/utils/GNUmakefile
@@ -1,19 +1,24 @@
 # For multiple programs using a single source file each,
 # we can just define 'progs' and create custom targets.
-PROGS	= test_select testmmap test_nm producer
-X86PROGS = testlock testcsum
+PROGS	  = test_select testmmap test_nm functional ctrl-api-test fd_server
+PROGS	 += functional-legacy fd_server-legacy
+PROGS    += get_avail_tx_packets get_max_tx_packets extmem-example sync_kloop_test
+X86PROGS  = testlock testcsum producer
 LIBNETMAP =
 
 CLEANFILES = $(PROGS) $(X86PROGS) *.o
 
 SRCDIR ?= ..
-VPATH = $(SRCDIR)/utils
+PREFIX ?= usr/local
+VPATH   = $(SRCDIR)/utils
 
 NO_MAN=
-CFLAGS = -O2 -pipe
+CFLAGS  = -O2 -pipe
 CFLAGS += -Werror -Wall -Wunused-function
 CFLAGS += -I $(SRCDIR)/sys # -I/home/luigi/FreeBSD/head/sys -I../sys
-CFLAGS += -Wextra
+CFLAGS += -I $(SRCDIR)/libnetmap
+CFLAGS += -Wextra -g
+CFLAGS += $(SUBSYS_FLAGS)
 ifdef WITH_PCAP
 # do not use pcap by default, as it is not always available on linux
 LDLIBS += -lpcap
@@ -21,28 +26,36 @@ else
 CFLAGS += -DNO_PCAP
 endif
 
-LDLIBS += -lpthread
+ifdef BUILDDIR
+LDFLAGS += -L $(BUILDDIR)/build-libnetmap
+else
+LDFLAGS += -L $(SRCDIR)/libnetmap
+endif
+LDLIBS += -lnetmap -lpthread -lm
 ifeq ($(shell uname),Linux)
-	LDLIBS += -lrt -lm	# on linux
+	LDLIBS += -lrt 	# on linux
 endif
 #SRCS = pkt-gen.c
 
 PREFIX ?= /usr/local
 
+.PHONY: install clean all-x86 all inttest
+
 all: $(PROGS)
 
+inttest:
+	$(shell if [ "$(shell uname)" = "Linux" ] ; then modprobe netmap ;fi)
+	./randomized_tests
+
 all-x86: $(PROGS) $(X86PROGS)
 
 kern_test: testmod/kern_test.c
 
-test_nm: test_nm.o
-
 clean:
 	-@rm -rf $(CLEANFILES)
 
 testlock: testlock.c
 
-.PHONY: install
 install: $(PROGS:%=install-%)
 
 install-%:
diff --git a/utils/README b/utils/README
index e4a1e6876..08ab2d4ed 100644
--- a/utils/README
+++ b/utils/README
@@ -1,5 +1,23 @@
-$FreeBSD: head/tools/tools/netmap/README 227614 2011-11-17 12:17:39Z luigi $
+This directory contains miscellaneous programs related to netmap
 
-This directory contains examples that use netmap
-
-	click*		various click examples
+	ctrl-api-test.c		suite of unit tests for the netmap control ABI
+	sync_kloop_test.c	example program for the CSB mode with sync-kloop
+	extmem-example.c	example program for the extmem feature
+	producer.c		transmitter example with constant per-packet
+				work
+	testmmap.c		test program for interactively test the netmap
+				control ABI (open, mmap, NIOCREGIF, NIOCGETINFO)
+	test_nm.c		example program for nm_inject and nm_dispatch
+	fd_server.[ch]		helper program for integration tests
+	get_avail_tx_packets.c	"
+	get_max_tx_packets.c	"
+	functional.c		"
+	tests/			suite of integration tests (shell scripts)
+	test_lib		helper shell functions for integration tests
+	randomized_tests	script to run all the integration tests
+	switch-modules/		(old) patches for Open VSwitch to use netmap
+	click-test.cfg		(old) simple click example
+	testcsum.c		(old) benchmarks for checksum computation
+	testlock.c		(old) benchmarks for locks and concurrency
+	test_select.c		(old) benchmarks for select() and poll()
+	testmod/		(old) benchmarks for FreeBSD kernel
diff --git a/utils/click-test.cfg b/utils/click-test.cfg
index d95ab4a6d..927ba10ba 100644
--- a/utils/click-test.cfg
+++ b/utils/click-test.cfg
@@ -1,6 +1,4 @@
 //
-// $FreeBSD: head/tools/tools/netmap/click-test.cfg 227614 2011-11-17 12:17:39Z luigi $
-//
 // A sample test configuration for click
 //
 //
diff --git a/utils/ctrl-api-test.c b/utils/ctrl-api-test.c
new file mode 100644
index 000000000..64c980e01
--- /dev/null
+++ b/utils/ctrl-api-test.c
@@ -0,0 +1,2348 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018 Vincenzo Maffione
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This program contains a suite of unit tests for the netmap control device.
+ *
+ * On FreeBSD, you can run these tests with Kyua once installed in the system:
+ *     # kyua test -k /usr/tests/sys/netmap/Kyuafile
+ *
+ * On Linux, you can run them directly:
+ *     # ./ctrl-api-test
+ */
+
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "libnetmap.h"
+
+#ifdef __FreeBSD__
+#include "freebsd_test_suite/macros.h"
+
+static int
+eventfd(int x __unused, int y __unused)
+{
+	errno = ENODEV;
+	return -1;
+}
+#else /* __linux__ */
+#include 
+#endif
+
+#define NM_IFNAMSZ 64
+
+static int
+exec_command(int argc, const char *const argv[])
+{
+	pid_t child_pid;
+	pid_t wret;
+	int child_status;
+	int i;
+
+	printf("Executing command: ");
+	for (i = 0; i < argc - 1; i++) {
+		if (!argv[i]) {
+			/* Invalid argument. */
+			return -1;
+		}
+		if (i > 0) {
+			putchar(' ');
+		}
+		printf("%s", argv[i]);
+	}
+	putchar('\n');
+
+	child_pid = fork();
+	if (child_pid == 0) {
+		char **av;
+		int fds[3];
+
+		/* Child process. Redirect stdin, stdout
+		 * and stderr. */
+		for (i = 0; i < 3; i++) {
+			close(i);
+			fds[i] = open("/dev/null", O_RDONLY);
+			if (fds[i] < 0) {
+				for (i--; i >= 0; i--) {
+					close(fds[i]);
+				}
+				return -1;
+			}
+		}
+
+		/* Make a copy of the arguments, passing them to execvp. */
+		av = calloc(argc, sizeof(av[0]));
+		if (!av) {
+			exit(EXIT_FAILURE);
+		}
+		for (i = 0; i < argc - 1; i++) {
+			av[i] = strdup(argv[i]);
+			if (!av[i]) {
+				exit(EXIT_FAILURE);
+			}
+		}
+		execvp(av[0], av);
+		perror("execvp()");
+		exit(EXIT_FAILURE);
+	}
+
+	wret = waitpid(child_pid, &child_status, 0);
+	if (wret < 0) {
+		fprintf(stderr, "waitpid() failed: %s\n", strerror(errno));
+		return wret;
+	}
+	if (WIFEXITED(child_status)) {
+		return WEXITSTATUS(child_status);
+	}
+
+	return -1;
+}
+
+
+#define THRET_SUCCESS	((void *)128)
+#define THRET_FAILURE	((void *)0)
+
+struct TestContext {
+	char ifname[NM_IFNAMSZ];
+	char ifname_ext[NM_IFNAMSZ];
+	char bdgname[NM_IFNAMSZ];
+	uint32_t nr_tx_slots;   /* slots in tx rings */
+	uint32_t nr_rx_slots;   /* slots in rx rings */
+	uint16_t nr_tx_rings;   /* number of tx rings */
+	uint16_t nr_rx_rings;   /* number of rx rings */
+	uint16_t nr_host_tx_rings;   /* number of host tx rings */
+	uint16_t nr_host_rx_rings;   /* number of host rx rings */
+	uint16_t nr_mem_id;     /* id of the memory allocator */
+	uint16_t nr_ringid;     /* ring(s) we care about */
+	uint32_t nr_mode;       /* specify NR_REG_* modes */
+	uint32_t nr_extra_bufs; /* number of requested extra buffers */
+	uint64_t nr_flags;      /* additional flags (see below) */
+	uint32_t nr_hdr_len; /* for PORT_HDR_SET and PORT_HDR_GET */
+	uint32_t nr_first_cpu_id;     /* vale polling */
+	uint32_t nr_num_polling_cpus; /* vale polling */
+	uint32_t sync_kloop_mode; /* sync-kloop */
+	int fd; /* netmap file descriptor */
+
+	void *csb;                    /* CSB entries (atok and ktoa) */
+	struct nmreq_option *nr_opt;  /* list of options */
+	sem_t *sem;	/* for thread synchronization */
+
+	struct nmctx *nmctx;
+	const char *ifparse;
+	struct nmport_d *nmport;      /* nmport descriptor from libnetmap */
+};
+
+static struct TestContext ctx_;
+
+typedef int (*testfunc_t)(struct TestContext *ctx);
+
+static void
+nmreq_hdr_init(struct nmreq_header *hdr, const char *ifname)
+{
+	memset(hdr, 0, sizeof(*hdr));
+	hdr->nr_version = NETMAP_API;
+	assert(strlen(ifname) < NM_IFNAMSZ);
+	strncpy(hdr->nr_name, ifname, sizeof(hdr->nr_name));
+}
+
+/* Single NETMAP_REQ_PORT_INFO_GET. */
+static int
+port_info_get(struct TestContext *ctx)
+{
+	struct nmreq_port_info_get req;
+	struct nmreq_header hdr;
+	int success;
+	int ret;
+
+	printf("Testing NETMAP_REQ_PORT_INFO_GET on '%s'\n", ctx->ifname_ext);
+
+	nmreq_hdr_init(&hdr, ctx->ifname_ext);
+	hdr.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	req.nr_mem_id = ctx->nr_mem_id;
+	ret           = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, PORT_INFO_GET)");
+		return ret;
+	}
+	printf("nr_memsize %llu\n", (unsigned long long)req.nr_memsize);
+	printf("nr_tx_slots %u\n", req.nr_tx_slots);
+	printf("nr_rx_slots %u\n", req.nr_rx_slots);
+	printf("nr_tx_rings %u\n", req.nr_tx_rings);
+	printf("nr_rx_rings %u\n", req.nr_rx_rings);
+	printf("nr_mem_id %u\n", req.nr_mem_id);
+
+	success = req.nr_memsize && req.nr_tx_slots && req.nr_rx_slots &&
+	          req.nr_tx_rings && req.nr_rx_rings;
+	if (!success) {
+		return -1;
+	}
+
+	/* Write back results to the context structure. */
+	ctx->nr_tx_slots = req.nr_tx_slots;
+	ctx->nr_rx_slots = req.nr_rx_slots;
+	ctx->nr_tx_rings = req.nr_tx_rings;
+	ctx->nr_rx_rings = req.nr_rx_rings;
+	ctx->nr_mem_id   = req.nr_mem_id;
+
+	return 0;
+}
+
+/* Single NETMAP_REQ_REGISTER, no use. */
+static int
+port_register(struct TestContext *ctx)
+{
+	struct nmreq_register req;
+	struct nmreq_header hdr;
+	int success;
+	int ret;
+
+	printf("Testing NETMAP_REQ_REGISTER(mode=%d,ringid=%d,"
+	       "flags=0x%llx) on '%s'\n",
+	       ctx->nr_mode, ctx->nr_ringid, (unsigned long long)ctx->nr_flags,
+	       ctx->ifname_ext);
+
+	nmreq_hdr_init(&hdr, ctx->ifname_ext);
+	hdr.nr_reqtype = NETMAP_REQ_REGISTER;
+	hdr.nr_body    = (uintptr_t)&req;
+	hdr.nr_options = (uintptr_t)ctx->nr_opt;
+	memset(&req, 0, sizeof(req));
+	req.nr_mem_id     = ctx->nr_mem_id;
+	req.nr_mode       = ctx->nr_mode;
+	req.nr_ringid     = ctx->nr_ringid;
+	req.nr_flags      = ctx->nr_flags;
+	req.nr_tx_slots   = ctx->nr_tx_slots;
+	req.nr_rx_slots   = ctx->nr_rx_slots;
+	req.nr_tx_rings   = ctx->nr_tx_rings;
+	req.nr_host_tx_rings = ctx->nr_host_tx_rings;
+	req.nr_host_rx_rings = ctx->nr_host_rx_rings;
+	req.nr_rx_rings   = ctx->nr_rx_rings;
+	req.nr_extra_bufs = ctx->nr_extra_bufs;
+	ret               = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, REGISTER)");
+		return ret;
+	}
+	printf("nr_offset 0x%llx\n", (unsigned long long)req.nr_offset);
+	printf("nr_memsize %llu\n", (unsigned long long)req.nr_memsize);
+	printf("nr_tx_slots %u\n", req.nr_tx_slots);
+	printf("nr_rx_slots %u\n", req.nr_rx_slots);
+	printf("nr_tx_rings %u\n", req.nr_tx_rings);
+	printf("nr_rx_rings %u\n", req.nr_rx_rings);
+	printf("nr_host_tx_rings %u\n", req.nr_host_tx_rings);
+	printf("nr_host_rx_rings %u\n", req.nr_host_rx_rings);
+	printf("nr_mem_id %u\n", req.nr_mem_id);
+	printf("nr_extra_bufs %u\n", req.nr_extra_bufs);
+
+	success = req.nr_memsize && (ctx->nr_mode == req.nr_mode) &&
+		       (ctx->nr_ringid == req.nr_ringid) &&
+		       (ctx->nr_flags == req.nr_flags) &&
+		       ((!ctx->nr_tx_slots && req.nr_tx_slots) ||
+			(ctx->nr_tx_slots == req.nr_tx_slots)) &&
+		       ((!ctx->nr_rx_slots && req.nr_rx_slots) ||
+			(ctx->nr_rx_slots == req.nr_rx_slots)) &&
+		       ((!ctx->nr_tx_rings && req.nr_tx_rings) ||
+			(ctx->nr_tx_rings == req.nr_tx_rings)) &&
+		       ((!ctx->nr_rx_rings && req.nr_rx_rings) ||
+			(ctx->nr_rx_rings == req.nr_rx_rings)) &&
+		       ((!ctx->nr_host_tx_rings && req.nr_host_tx_rings) ||
+			(ctx->nr_host_tx_rings == req.nr_host_tx_rings)) &&
+		       ((!ctx->nr_host_rx_rings && req.nr_host_rx_rings) ||
+			(ctx->nr_host_rx_rings == req.nr_host_rx_rings)) &&
+		       ((!ctx->nr_mem_id && req.nr_mem_id) ||
+			(ctx->nr_mem_id == req.nr_mem_id)) &&
+		       (ctx->nr_extra_bufs == req.nr_extra_bufs);
+	if (!success) {
+		return -1;
+	}
+
+	/* Write back results to the context structure.*/
+	ctx->nr_tx_slots   = req.nr_tx_slots;
+	ctx->nr_rx_slots   = req.nr_rx_slots;
+	ctx->nr_tx_rings   = req.nr_tx_rings;
+	ctx->nr_rx_rings   = req.nr_rx_rings;
+	ctx->nr_host_tx_rings = req.nr_host_tx_rings;
+	ctx->nr_host_rx_rings = req.nr_host_rx_rings;
+	ctx->nr_mem_id     = req.nr_mem_id;
+	ctx->nr_extra_bufs = req.nr_extra_bufs;
+
+	return 0;
+}
+
+static int
+niocregif(struct TestContext *ctx, int netmap_api)
+{
+	struct nmreq req;
+	int success;
+	int ret;
+
+	printf("Testing legacy NIOCREGIF on '%s'\n", ctx->ifname_ext);
+
+	memset(&req, 0, sizeof(req));
+	memcpy(req.nr_name, ctx->ifname_ext, sizeof(req.nr_name));
+	req.nr_name[sizeof(req.nr_name) - 1] = '\0';
+	req.nr_version = netmap_api;
+	req.nr_ringid     = ctx->nr_ringid;
+	req.nr_flags      = ctx->nr_mode | ctx->nr_flags;
+	req.nr_tx_slots   = ctx->nr_tx_slots;
+	req.nr_rx_slots   = ctx->nr_rx_slots;
+	req.nr_tx_rings   = ctx->nr_tx_rings;
+	req.nr_rx_rings   = ctx->nr_rx_rings;
+	req.nr_arg2     = ctx->nr_mem_id;
+	req.nr_arg3 = ctx->nr_extra_bufs;
+
+	ret = ioctl(ctx->fd, NIOCREGIF, &req);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCREGIF)");
+		return ret;
+	}
+
+	printf("nr_offset 0x%x\n", req.nr_offset);
+	printf("nr_memsize  %u\n", req.nr_memsize);
+	printf("nr_tx_slots %u\n", req.nr_tx_slots);
+	printf("nr_rx_slots %u\n", req.nr_rx_slots);
+	printf("nr_tx_rings %u\n", req.nr_tx_rings);
+	printf("nr_rx_rings %u\n", req.nr_rx_rings);
+	printf("nr_version  %d\n", req.nr_version);
+	printf("nr_ringid   %x\n", req.nr_ringid);
+	printf("nr_flags    %x\n", req.nr_flags);
+	printf("nr_arg2     %u\n", req.nr_arg2);
+	printf("nr_arg3     %u\n", req.nr_arg3);
+
+	success = req.nr_memsize &&
+	       (ctx->nr_ringid == req.nr_ringid) &&
+	       ((ctx->nr_mode | ctx->nr_flags) == req.nr_flags) &&
+	       ((!ctx->nr_tx_slots && req.nr_tx_slots) ||
+		(ctx->nr_tx_slots == req.nr_tx_slots)) &&
+	       ((!ctx->nr_rx_slots && req.nr_rx_slots) ||
+		(ctx->nr_rx_slots == req.nr_rx_slots)) &&
+	       ((!ctx->nr_tx_rings && req.nr_tx_rings) ||
+		(ctx->nr_tx_rings == req.nr_tx_rings)) &&
+	       ((!ctx->nr_rx_rings && req.nr_rx_rings) ||
+		(ctx->nr_rx_rings == req.nr_rx_rings)) &&
+	       ((!ctx->nr_mem_id && req.nr_arg2) ||
+		(ctx->nr_mem_id == req.nr_arg2)) &&
+	       (ctx->nr_extra_bufs == req.nr_arg3);
+	if (!success) {
+		return -1;
+	}
+
+	/* Write back results to the context structure.*/
+	ctx->nr_tx_slots   = req.nr_tx_slots;
+	ctx->nr_rx_slots   = req.nr_rx_slots;
+	ctx->nr_tx_rings   = req.nr_tx_rings;
+	ctx->nr_rx_rings   = req.nr_rx_rings;
+	ctx->nr_mem_id     = req.nr_arg2;
+	ctx->nr_extra_bufs = req.nr_arg3;
+
+	return ret;
+}
+
+/* The 11 ABI is the one right before the introduction of the new NIOCCTRL
+ * ABI. The 11 ABI is useful to perform tests with legacy applications
+ * (which use the 11 ABI) and new kernel (which uses 12, or higher).
+ * However, version 14 introduced a change in the layout of struct netmap_if,
+ * so that binary backward compatibility to 11 is not supported anymore.
+ */
+#define NETMAP_API_NIOCREGIF	14
+
+static int
+legacy_regif_default(struct TestContext *ctx)
+{
+	return niocregif(ctx, NETMAP_API_NIOCREGIF);
+}
+
+static int
+legacy_regif_all_nic(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	return niocregif(ctx, NETMAP_API);
+}
+
+static int
+legacy_regif_12(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	return niocregif(ctx, NETMAP_API_NIOCREGIF+1);
+}
+
+static int
+legacy_regif_sw(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_SW;
+	return niocregif(ctx,  NETMAP_API_NIOCREGIF);
+}
+
+static int
+legacy_regif_future(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_NIC_SW;
+	/* Test forward compatibility for the legacy ABI. This means
+	 * using an older kernel (with ABI 12 or higher) and a newer
+	 * application (with ABI greater than NETMAP_API). */
+	return niocregif(ctx, NETMAP_API+2);
+}
+
+static int
+legacy_regif_extra_bufs(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	ctx->nr_extra_bufs = 20;	/* arbitrary number of extra bufs */
+	return niocregif(ctx, NETMAP_API_NIOCREGIF);
+}
+
+static int
+legacy_regif_extra_bufs_pipe(struct TestContext *ctx)
+{
+	strncat(ctx->ifname_ext, "{pipeexbuf", sizeof(ctx->ifname_ext));
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	ctx->nr_extra_bufs = 58;	/* arbitrary number of extra bufs */
+
+	return niocregif(ctx, NETMAP_API_NIOCREGIF);
+}
+
+static int
+legacy_regif_extra_bufs_pipe_vale(struct TestContext *ctx)
+{
+	strncpy(ctx->ifname_ext, "valeX1:Y4", sizeof(ctx->ifname_ext));
+	return legacy_regif_extra_bufs_pipe(ctx);
+}
+
+/* Only valid after a successful port_register(). */
+static int
+num_registered_rings(struct TestContext *ctx)
+{
+	if (ctx->nr_flags & NR_TX_RINGS_ONLY) {
+		return ctx->nr_tx_rings;
+	}
+	if (ctx->nr_flags & NR_RX_RINGS_ONLY) {
+		return ctx->nr_rx_rings;
+	}
+
+	return ctx->nr_tx_rings + ctx->nr_rx_rings;
+}
+
+static int
+port_register_hwall_host(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_NIC_SW;
+	return port_register(ctx);
+}
+
+static int
+port_register_hostall(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_SW;
+	return port_register(ctx);
+}
+
+static int
+port_register_hwall(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	return port_register(ctx);
+}
+
+static int
+port_register_single_hw_pair(struct TestContext *ctx)
+{
+	ctx->nr_mode   = NR_REG_ONE_NIC;
+	ctx->nr_ringid = 0;
+	return port_register(ctx);
+}
+
+static int
+port_register_single_host_pair(struct TestContext *ctx)
+{
+	ctx->nr_mode   = NR_REG_ONE_SW;
+	ctx->nr_host_tx_rings = 2;
+	ctx->nr_host_rx_rings = 2;
+	ctx->nr_ringid = 1;
+	return port_register(ctx);
+}
+
+static int
+port_register_hostall_many(struct TestContext *ctx)
+{
+	ctx->nr_mode   = NR_REG_SW;
+	ctx->nr_host_tx_rings = 5;
+	ctx->nr_host_rx_rings = 4;
+	return port_register(ctx);
+}
+
+static int
+port_register_hwall_tx(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	ctx->nr_flags |= NR_TX_RINGS_ONLY;
+	return port_register(ctx);
+}
+
+static int
+port_register_hwall_rx(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	ctx->nr_flags |= NR_RX_RINGS_ONLY;
+	return port_register(ctx);
+}
+
+
+static int
+vale_mkname(char *vpname, struct TestContext *ctx)
+{
+	if (snprintf(vpname, NM_IFNAMSZ, "%s:%s", ctx->bdgname, ctx->ifname_ext) >= NM_IFNAMSZ) {
+		fprintf(stderr, "%s:%s too long (max %d chars)\n", ctx->bdgname, ctx->ifname_ext,
+				NM_IFNAMSZ - 1);
+		return -1;
+	}
+	return 0;
+}
+
+
+/* NETMAP_REQ_VALE_ATTACH */
+static int
+vale_attach(struct TestContext *ctx)
+{
+	struct nmreq_vale_attach req;
+	struct nmreq_header hdr;
+	char vpname[NM_IFNAMSZ];
+	int ret;
+
+	if (vale_mkname(vpname, ctx) < 0)
+		return -1;
+
+	printf("Testing NETMAP_REQ_VALE_ATTACH on '%s'\n", vpname);
+	nmreq_hdr_init(&hdr, vpname);
+	hdr.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	req.reg.nr_mem_id = ctx->nr_mem_id;
+	if (ctx->nr_mode == 0) {
+		ctx->nr_mode = NR_REG_ALL_NIC; /* default */
+	}
+	req.reg.nr_mode = ctx->nr_mode;
+	ret             = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_ATTACH)");
+		return ret;
+	}
+	printf("nr_mem_id %u\n", req.reg.nr_mem_id);
+
+	return ((!ctx->nr_mem_id && req.reg.nr_mem_id > 1) ||
+	        (ctx->nr_mem_id == req.reg.nr_mem_id)) &&
+	                       (ctx->nr_flags == req.reg.nr_flags)
+	               ? 0
+	               : -1;
+}
+
+/* NETMAP_REQ_VALE_DETACH */
+static int
+vale_detach(struct TestContext *ctx)
+{
+	struct nmreq_header hdr;
+	struct nmreq_vale_detach req;
+	char vpname[NM_IFNAMSZ];
+	int ret;
+
+	if (vale_mkname(vpname, ctx) < 0)
+		return -1;
+
+	printf("Testing NETMAP_REQ_VALE_DETACH on '%s'\n", vpname);
+	nmreq_hdr_init(&hdr, vpname);
+	hdr.nr_reqtype = NETMAP_REQ_VALE_DETACH;
+	hdr.nr_body    = (uintptr_t)&req;
+	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_DETACH)");
+		return ret;
+	}
+
+	return 0;
+}
+
+/* First NETMAP_REQ_VALE_ATTACH, then NETMAP_REQ_VALE_DETACH. */
+static int
+vale_attach_detach(struct TestContext *ctx)
+{
+	int ret;
+
+	if ((ret = vale_attach(ctx)) != 0) {
+		return ret;
+	}
+
+	return vale_detach(ctx);
+}
+
+static int
+vale_attach_detach_host_rings(struct TestContext *ctx)
+{
+	ctx->nr_mode = NR_REG_NIC_SW;
+	return vale_attach_detach(ctx);
+}
+
+/* First NETMAP_REQ_PORT_HDR_SET and the NETMAP_REQ_PORT_HDR_GET
+ * to check that we get the same value. */
+static int
+port_hdr_set_and_get(struct TestContext *ctx)
+{
+	struct nmreq_port_hdr req;
+	struct nmreq_header hdr;
+	int ret;
+
+	printf("Testing NETMAP_REQ_PORT_HDR_SET on '%s'\n", ctx->ifname_ext);
+
+	nmreq_hdr_init(&hdr, ctx->ifname_ext);
+	hdr.nr_reqtype = NETMAP_REQ_PORT_HDR_SET;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	req.nr_hdr_len = ctx->nr_hdr_len;
+	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, PORT_HDR_SET)");
+		return ret;
+	}
+
+	if (req.nr_hdr_len != ctx->nr_hdr_len) {
+		return -1;
+	}
+
+	printf("Testing NETMAP_REQ_PORT_HDR_GET on '%s'\n", ctx->ifname_ext);
+	hdr.nr_reqtype = NETMAP_REQ_PORT_HDR_GET;
+	req.nr_hdr_len = 0;
+	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, PORT_HDR_SET)");
+		return ret;
+	}
+	printf("nr_hdr_len %u\n", req.nr_hdr_len);
+
+	return (req.nr_hdr_len == ctx->nr_hdr_len) ? 0 : -1;
+}
+
+/*
+ * Possible lengths for the VirtIO network header, as specified by
+ * the standard:
+ *    http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html
+ */
+#define VIRTIO_NET_HDR_LEN				10
+#define VIRTIO_NET_HDR_LEN_WITH_MERGEABLE_RXBUFS	12
+
+static int
+vale_ephemeral_port_hdr_manipulation(struct TestContext *ctx)
+{
+	int ret;
+
+	strncpy(ctx->ifname_ext, "vale:eph0", sizeof(ctx->ifname_ext));
+	ctx->nr_mode = NR_REG_ALL_NIC;
+	if ((ret = port_register(ctx))) {
+		return ret;
+	}
+	/* Try to set and get all the acceptable values. */
+	ctx->nr_hdr_len = VIRTIO_NET_HDR_LEN_WITH_MERGEABLE_RXBUFS;
+	if ((ret = port_hdr_set_and_get(ctx))) {
+		return ret;
+	}
+	ctx->nr_hdr_len = 0;
+	if ((ret = port_hdr_set_and_get(ctx))) {
+		return ret;
+	}
+	ctx->nr_hdr_len = VIRTIO_NET_HDR_LEN;
+	if ((ret = port_hdr_set_and_get(ctx))) {
+		return ret;
+	}
+	return 0;
+}
+
+static int
+vale_persistent_port(struct TestContext *ctx)
+{
+	struct nmreq_vale_newif req;
+	struct nmreq_header hdr;
+	int result;
+	int ret;
+
+	strncpy(ctx->ifname_ext, "per4", sizeof(ctx->ifname_ext));
+
+	printf("Testing NETMAP_REQ_VALE_NEWIF on '%s'\n", ctx->ifname_ext);
+
+	nmreq_hdr_init(&hdr, ctx->ifname_ext);
+	hdr.nr_reqtype = NETMAP_REQ_VALE_NEWIF;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	req.nr_mem_id   = ctx->nr_mem_id;
+	req.nr_tx_slots = ctx->nr_tx_slots;
+	req.nr_rx_slots = ctx->nr_rx_slots;
+	req.nr_tx_rings = ctx->nr_tx_rings;
+	req.nr_rx_rings = ctx->nr_rx_rings;
+	ret             = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_NEWIF)");
+		return ret;
+	}
+
+	/* Attach the persistent VALE port to a switch and then detach. */
+	result = vale_attach_detach(ctx);
+
+	printf("Testing NETMAP_REQ_VALE_DELIF on '%s'\n", ctx->ifname_ext);
+	hdr.nr_reqtype = NETMAP_REQ_VALE_DELIF;
+	hdr.nr_body    = (uintptr_t)NULL;
+	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_NEWIF)");
+		if (result == 0) {
+			result = ret;
+		}
+	}
+
+	return result;
+}
+
+/* Single NETMAP_REQ_POOLS_INFO_GET. */
+static int
+pools_info_get(struct TestContext *ctx)
+{
+	struct nmreq_pools_info req;
+	struct nmreq_header hdr;
+	int ret;
+
+	printf("Testing NETMAP_REQ_POOLS_INFO_GET on '%s'\n", ctx->ifname_ext);
+
+	nmreq_hdr_init(&hdr, ctx->ifname_ext);
+	hdr.nr_reqtype = NETMAP_REQ_POOLS_INFO_GET;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	req.nr_mem_id = ctx->nr_mem_id;
+	ret           = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, POOLS_INFO_GET)");
+		return ret;
+	}
+	printf("nr_memsize %llu\n", (unsigned long long)req.nr_memsize);
+	printf("nr_mem_id %u\n", req.nr_mem_id);
+	printf("nr_if_pool_offset 0x%llx\n",
+		(unsigned long long)req.nr_if_pool_offset);
+	printf("nr_if_pool_objtotal %u\n", req.nr_if_pool_objtotal);
+	printf("nr_if_pool_objsize %u\n", req.nr_if_pool_objsize);
+	printf("nr_ring_pool_offset 0x%llx\n",
+		(unsigned long long)req.nr_if_pool_offset);
+	printf("nr_ring_pool_objtotal %u\n", req.nr_ring_pool_objtotal);
+	printf("nr_ring_pool_objsize %u\n", req.nr_ring_pool_objsize);
+	printf("nr_buf_pool_offset 0x%llx\n",
+		(unsigned long long)req.nr_buf_pool_offset);
+	printf("nr_buf_pool_objtotal %u\n", req.nr_buf_pool_objtotal);
+	printf("nr_buf_pool_objsize %u\n", req.nr_buf_pool_objsize);
+
+	return req.nr_memsize && req.nr_if_pool_objtotal &&
+	                       req.nr_if_pool_objsize &&
+	                       req.nr_ring_pool_objtotal &&
+	                       req.nr_ring_pool_objsize &&
+	                       req.nr_buf_pool_objtotal &&
+	                       req.nr_buf_pool_objsize
+	               ? 0
+	               : -1;
+}
+
+static int
+pools_info_get_and_register(struct TestContext *ctx)
+{
+	int ret;
+
+	/* Check that we can get pools info before we register
+	 * a netmap interface. */
+	ret = pools_info_get(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ctx->nr_mode = NR_REG_ONE_NIC;
+	ret          = port_register(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+	ctx->nr_mem_id = 1;
+
+	/* Check that we can get pools info also after we register. */
+	return pools_info_get(ctx);
+}
+
+static int
+pools_info_get_empty_ifname(struct TestContext *ctx)
+{
+	strncpy(ctx->ifname_ext, "", sizeof(ctx->ifname_ext));
+	return pools_info_get(ctx) != 0 ? 0 : -1;
+}
+
+static int
+pipe_master(struct TestContext *ctx)
+{
+	strncat(ctx->ifname_ext, "{pipeid1", sizeof(ctx->ifname_ext));
+	ctx->nr_mode = NR_REG_NIC_SW;
+
+	if (port_register(ctx) == 0) {
+		printf("pipes should not accept NR_REG_NIC_SW\n");
+		return -1;
+	}
+	ctx->nr_mode = NR_REG_ALL_NIC;
+
+	return port_register(ctx);
+}
+
+static int
+pipe_slave(struct TestContext *ctx)
+{
+	strncat(ctx->ifname_ext, "}pipeid2", sizeof(ctx->ifname_ext));
+	ctx->nr_mode = NR_REG_ALL_NIC;
+
+	return port_register(ctx);
+}
+
+/* Test PORT_INFO_GET and POOLS_INFO_GET on a pipe. This is useful to test the
+ * registration request used internally by netmap. */
+static int
+pipe_port_info_get(struct TestContext *ctx)
+{
+	strncat(ctx->ifname_ext, "}pipeid3", sizeof(ctx->ifname_ext));
+
+	return port_info_get(ctx);
+}
+
+static int
+pipe_pools_info_get(struct TestContext *ctx)
+{
+	strncat(ctx->ifname_ext, "{xid", sizeof(ctx->ifname_ext));
+
+	return pools_info_get(ctx);
+}
+
+/* NETMAP_REQ_VALE_POLLING_ENABLE */
+static int
+vale_polling_enable(struct TestContext *ctx)
+{
+	struct nmreq_vale_polling req;
+	struct nmreq_header hdr;
+	char vpname[NM_IFNAMSZ];
+	int ret;
+
+	if (vale_mkname(vpname, ctx) < 0)
+		return -1;
+
+	printf("Testing NETMAP_REQ_VALE_POLLING_ENABLE on '%s'\n", vpname);
+
+	nmreq_hdr_init(&hdr, vpname);
+	hdr.nr_reqtype = NETMAP_REQ_VALE_POLLING_ENABLE;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	req.nr_mode             = ctx->nr_mode;
+	req.nr_first_cpu_id     = ctx->nr_first_cpu_id;
+	req.nr_num_polling_cpus = ctx->nr_num_polling_cpus;
+	ret                     = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_POLLING_ENABLE)");
+		return ret;
+	}
+
+	return (req.nr_mode == ctx->nr_mode &&
+	        req.nr_first_cpu_id == ctx->nr_first_cpu_id &&
+	        req.nr_num_polling_cpus == ctx->nr_num_polling_cpus)
+	               ? 0
+	               : -1;
+}
+
+/* NETMAP_REQ_VALE_POLLING_DISABLE */
+static int
+vale_polling_disable(struct TestContext *ctx)
+{
+	struct nmreq_vale_polling req;
+	struct nmreq_header hdr;
+	char vpname[NM_IFNAMSZ];
+	int ret;
+
+	if (vale_mkname(vpname, ctx) < 0)
+		return -1;
+
+	printf("Testing NETMAP_REQ_VALE_POLLING_DISABLE on '%s'\n", vpname);
+
+	nmreq_hdr_init(&hdr, vpname);
+	hdr.nr_reqtype = NETMAP_REQ_VALE_POLLING_DISABLE;
+	hdr.nr_body    = (uintptr_t)&req;
+	memset(&req, 0, sizeof(req));
+	ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, VALE_POLLING_DISABLE)");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int
+vale_polling_enable_disable(struct TestContext *ctx)
+{
+	int ret = 0;
+
+	if ((ret = vale_attach(ctx)) != 0) {
+		return ret;
+	}
+
+	ctx->nr_mode             = NETMAP_POLLING_MODE_SINGLE_CPU;
+	ctx->nr_num_polling_cpus = 1;
+	ctx->nr_first_cpu_id     = 0;
+	if ((ret = vale_polling_enable(ctx))) {
+		vale_detach(ctx);
+#ifdef __FreeBSD__
+		/* NETMAP_REQ_VALE_POLLING_DISABLE is disabled on FreeBSD,
+		 * because it is currently broken. We are happy to see that
+		 * it fails. */
+		return 0;
+#else
+		return ret;
+#endif
+	}
+
+	if ((ret = vale_polling_disable(ctx))) {
+		vale_detach(ctx);
+		return ret;
+	}
+
+	return vale_detach(ctx);
+}
+
+static void
+push_option(struct nmreq_option *opt, struct TestContext *ctx)
+{
+	opt->nro_next = (uintptr_t)ctx->nr_opt;
+	ctx->nr_opt   = opt;
+}
+
+static void
+clear_options(struct TestContext *ctx)
+{
+	ctx->nr_opt = NULL;
+}
+
+static int
+checkoption(struct nmreq_option *opt, struct nmreq_option *exp)
+{
+	if (opt->nro_next != exp->nro_next) {
+		printf("nro_next %p expected %p\n",
+		       (void *)(uintptr_t)opt->nro_next,
+		       (void *)(uintptr_t)exp->nro_next);
+		return -1;
+	}
+	if (opt->nro_reqtype != exp->nro_reqtype) {
+		printf("nro_reqtype %u expected %u\n", opt->nro_reqtype,
+		       exp->nro_reqtype);
+		return -1;
+	}
+	if (opt->nro_status != exp->nro_status) {
+		printf("nro_status %u expected %u\n", opt->nro_status,
+		       exp->nro_status);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+unsupported_option(struct TestContext *ctx)
+{
+	struct nmreq_option opt, save;
+
+	printf("Testing unsupported option on %s\n", ctx->ifname_ext);
+
+	memset(&opt, 0, sizeof(opt));
+	opt.nro_reqtype = 1234;
+	push_option(&opt, ctx);
+	save = opt;
+
+	if (port_register_hwall(ctx) >= 0)
+		return -1;
+
+	clear_options(ctx);
+	save.nro_status = EOPNOTSUPP;
+	return checkoption(&opt, &save);
+}
+
+static int
+infinite_options(struct TestContext *ctx)
+{
+	struct nmreq_option opt;
+
+	printf("Testing infinite list of options on %s (invalid options)\n", ctx->ifname_ext);
+
+	memset(&opt, 0, sizeof(opt));
+	opt.nro_reqtype = NETMAP_REQ_OPT_MAX + 1;
+	push_option(&opt, ctx);
+	opt.nro_next = (uintptr_t)&opt;
+	if (port_register_hwall(ctx) >= 0)
+		return -1;
+	clear_options(ctx);
+	return (errno == EMSGSIZE ? 0 : -1);
+}
+
+static int
+infinite_options2(struct TestContext *ctx)
+{
+	struct nmreq_option opt;
+
+	printf("Testing infinite list of options on %s (valid options)\n", ctx->ifname_ext);
+
+	memset(&opt, 0, sizeof(opt));
+	opt.nro_reqtype = NETMAP_REQ_OPT_OFFSETS;
+	push_option(&opt, ctx);
+	opt.nro_next = (uintptr_t)&opt;
+	if (port_register_hwall(ctx) >= 0)
+		return -1;
+	clear_options(ctx);
+	return (errno == EINVAL ? 0 : -1);
+}
+
+static int
+invalid_valid_options3(struct TestContext *ctx)
+{
+	struct nmreq_option opt[100];
+	unsigned int i;
+
+	printf("Testing infinite list of options on %s (invalid and valid option)\n", ctx->ifname_ext);
+
+	memset(&opt, 0, sizeof(opt));
+	for (i = 0; i < (sizeof(opt)/sizeof(struct nmreq_option) - 1); i++) {
+		opt[i].nro_reqtype = NETMAP_REQ_OPT_MAX;
+		opt[i].nro_next = (uintptr_t)&opt[i+1];
+	}
+	opt[i].nro_size = 1000;
+	opt[i].nro_reqtype = NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS;
+	ctx->nr_opt = opt;
+	if (port_register_hwall(ctx) >= 0)
+		return -1;
+	clear_options(ctx);
+	return (errno == EMSGSIZE ? 0 : -1);
+}
+
+#ifdef CONFIG_NETMAP_EXTMEM
+int
+change_param(const char *pname, unsigned long newv, unsigned long *poldv)
+{
+#ifdef __linux__
+	char param[256] = "/sys/module/netmap/parameters/";
+	unsigned long oldv;
+	FILE *f;
+
+	strncat(param, pname, sizeof(param) - 1);
+
+	f = fopen(param, "r+");
+	if (f == NULL) {
+		perror(param);
+		return -1;
+	}
+	if (fscanf(f, "%ld", &oldv) != 1) {
+		perror(param);
+		fclose(f);
+		return -1;
+	}
+	if (poldv)
+		*poldv = oldv;
+	rewind(f);
+	if (fprintf(f, "%ld\n", newv) < 0) {
+		perror(param);
+		fclose(f);
+		return -1;
+	}
+	fclose(f);
+	printf("change_param: %s: %ld -> %ld\n", pname, oldv, newv);
+#endif /* __linux__ */
+	return 0;
+}
+
+static int
+push_extmem_option(struct TestContext *ctx, const struct nmreq_pools_info *pi,
+		struct nmreq_opt_extmem *e)
+{
+	void *addr;
+
+	addr = mmap(NULL, pi->nr_memsize, PROT_READ | PROT_WRITE,
+	            MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+	if (addr == MAP_FAILED) {
+		perror("mmap");
+		return -1;
+	}
+
+	memset(e, 0, sizeof(*e));
+	e->nro_opt.nro_reqtype = NETMAP_REQ_OPT_EXTMEM;
+	e->nro_info = *pi;
+	e->nro_usrptr          = (uintptr_t)addr;
+
+	push_option(&e->nro_opt, ctx);
+
+	return 0;
+}
+
+static int
+pop_extmem_option(struct TestContext *ctx, struct nmreq_opt_extmem *exp)
+{
+	struct nmreq_opt_extmem *e;
+	int ret;
+
+	e           = (struct nmreq_opt_extmem *)(uintptr_t)ctx->nr_opt;
+	ctx->nr_opt = (struct nmreq_option *)(uintptr_t)ctx->nr_opt->nro_next;
+
+	if ((ret = checkoption(&e->nro_opt, &exp->nro_opt))) {
+		return ret;
+	}
+
+	if (e->nro_usrptr != exp->nro_usrptr) {
+		printf("usrptr %" PRIu64 " expected %" PRIu64 "\n",
+		       e->nro_usrptr, exp->nro_usrptr);
+		return -1;
+	}
+	if (e->nro_info.nr_memsize != exp->nro_info.nr_memsize) {
+		printf("memsize %" PRIu64 " expected %" PRIu64 "\n",
+		       e->nro_info.nr_memsize, exp->nro_info.nr_memsize);
+		return -1;
+	}
+
+	if ((ret = munmap((void *)(uintptr_t)e->nro_usrptr,
+	                  e->nro_info.nr_memsize)))
+		return ret;
+
+	return 0;
+}
+
+static int
+_extmem_option(struct TestContext *ctx,
+		const struct nmreq_pools_info *pi)
+{
+	struct nmreq_opt_extmem e, save;
+	int ret;
+
+	if ((ret = push_extmem_option(ctx, pi, &e)) < 0)
+		return ret;
+
+	save = e;
+
+	strncpy(ctx->ifname_ext, "vale0:0", sizeof(ctx->ifname_ext));
+	ctx->nr_tx_slots = 16;
+	ctx->nr_rx_slots = 16;
+
+	if ((ret = port_register_hwall(ctx)))
+		return ret;
+
+	ret = pop_extmem_option(ctx, &save);
+
+	return ret;
+}
+
+static size_t
+pools_info_min_memsize(const struct nmreq_pools_info *pi)
+{
+	size_t tot = 0;
+
+	tot += pi->nr_if_pool_objtotal * pi->nr_if_pool_objsize;
+	tot += pi->nr_ring_pool_objtotal * pi->nr_ring_pool_objsize;
+	tot += pi->nr_buf_pool_objtotal * pi->nr_buf_pool_objsize;
+
+	return tot;
+}
+
+/*
+ * Fill the specification of a netmap memory allocator to be
+ * used with the 'struct nmreq_opt_extmem' option. Arbitrary
+ * values are used for the parameters, but with enough netmap
+ * rings, netmap ifs, and buffers to support a VALE port.
+ */
+static void
+pools_info_fill(struct nmreq_pools_info *pi)
+{
+	pi->nr_if_pool_objtotal = 2;
+	pi->nr_if_pool_objsize = 1024;
+	pi->nr_ring_pool_objtotal = 64;
+	pi->nr_ring_pool_objsize = 512;
+	pi->nr_buf_pool_objtotal = 4096;
+	pi->nr_buf_pool_objsize = 2048;
+	pi->nr_memsize = pools_info_min_memsize(pi);
+}
+
+static int
+extmem_option(struct TestContext *ctx)
+{
+	struct nmreq_pools_info	pools_info;
+
+	pools_info_fill(&pools_info);
+
+	printf("Testing extmem option on vale0:0\n");
+	return _extmem_option(ctx, &pools_info);
+}
+
+static int
+bad_extmem_option(struct TestContext *ctx)
+{
+	struct nmreq_pools_info	pools_info;
+
+	printf("Testing bad extmem option on vale0:0\n");
+
+	pools_info_fill(&pools_info);
+	/* Request a large ring size, to make sure that the kernel
+	 * rejects our request. */
+	pools_info.nr_ring_pool_objsize = (1 << 20);
+
+	return _extmem_option(ctx, &pools_info) < 0 ? 0 : -1;
+}
+
+static int
+duplicate_extmem_options(struct TestContext *ctx)
+{
+	struct nmreq_opt_extmem e1, save1, e2, save2;
+	struct nmreq_pools_info	pools_info;
+	int ret;
+
+	printf("Testing duplicate extmem option on vale0:0\n");
+
+	pools_info_fill(&pools_info);
+
+	if ((ret = push_extmem_option(ctx, &pools_info, &e1)) < 0)
+		return ret;
+
+	if ((ret = push_extmem_option(ctx, &pools_info, &e2)) < 0) {
+		clear_options(ctx);
+		return ret;
+	}
+
+	save1 = e1;
+	save2 = e2;
+
+	strncpy(ctx->ifname_ext, "vale0:0", sizeof(ctx->ifname_ext));
+	ctx->nr_tx_slots = 16;
+	ctx->nr_rx_slots = 16;
+
+	ret = port_register_hwall(ctx);
+	if (ret >= 0) {
+		printf("duplicate option not detected\n");
+		return -1;
+	}
+
+	save2.nro_opt.nro_status = EINVAL;
+	if ((ret = pop_extmem_option(ctx, &save2)))
+		return ret;
+
+	save1.nro_opt.nro_status = EINVAL;
+	if ((ret = pop_extmem_option(ctx, &save1)))
+		return ret;
+
+	return 0;
+}
+#endif /* CONFIG_NETMAP_EXTMEM */
+
+static int
+push_csb_option(struct TestContext *ctx, struct nmreq_opt_csb *opt)
+{
+	size_t csb_size;
+	int num_entries;
+	int ret;
+
+	ctx->nr_flags |= NR_EXCLUSIVE;
+
+	/* Get port info in order to use num_registered_rings(). */
+	ret = port_info_get(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+	num_entries = num_registered_rings(ctx);
+
+	csb_size = (sizeof(struct nm_csb_atok) + sizeof(struct nm_csb_ktoa)) *
+	           num_entries;
+	assert(csb_size > 0);
+	if (ctx->csb) {
+		free(ctx->csb);
+	}
+	ret = posix_memalign(&ctx->csb, sizeof(struct nm_csb_atok), csb_size);
+	if (ret != 0) {
+		printf("Failed to allocate CSB memory\n");
+		exit(EXIT_FAILURE);
+	}
+
+	memset(opt, 0, sizeof(*opt));
+	opt->nro_opt.nro_reqtype = NETMAP_REQ_OPT_CSB;
+	opt->csb_atok            = (uintptr_t)ctx->csb;
+	opt->csb_ktoa            = (uintptr_t)(((uint8_t *)ctx->csb) +
+                                    sizeof(struct nm_csb_atok) * num_entries);
+
+	printf("Pushing option NETMAP_REQ_OPT_CSB\n");
+	push_option(&opt->nro_opt, ctx);
+
+	return 0;
+}
+
+static int
+csb_mode(struct TestContext *ctx)
+{
+	struct nmreq_opt_csb opt;
+	int ret;
+
+	ret = push_csb_option(ctx, &opt);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = port_register_hwall(ctx);
+	clear_options(ctx);
+
+	return ret;
+}
+
+static int
+csb_mode_invalid_memory(struct TestContext *ctx)
+{
+	struct nmreq_opt_csb opt;
+	int ret;
+
+	memset(&opt, 0, sizeof(opt));
+	opt.nro_opt.nro_reqtype = NETMAP_REQ_OPT_CSB;
+	opt.csb_atok            = (uintptr_t)0x10;
+	opt.csb_ktoa            = (uintptr_t)0x800;
+	push_option(&opt.nro_opt, ctx);
+
+	ctx->nr_flags = NR_EXCLUSIVE;
+	ret           = port_register_hwall(ctx);
+	clear_options(ctx);
+
+	return (ret < 0) ? 0 : -1;
+}
+
+static int
+sync_kloop_stop(struct TestContext *ctx)
+{
+	struct nmreq_header hdr;
+	int ret;
+
+	printf("Testing NETMAP_REQ_SYNC_KLOOP_STOP on '%s'\n", ctx->ifname_ext);
+
+	nmreq_hdr_init(&hdr, ctx->ifname_ext);
+	hdr.nr_reqtype = NETMAP_REQ_SYNC_KLOOP_STOP;
+	ret            = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, SYNC_KLOOP_STOP)");
+	}
+
+	return ret;
+}
+
+static void *
+sync_kloop_worker(void *opaque)
+{
+	struct TestContext *ctx = opaque;
+	struct nmreq_sync_kloop_start req;
+	struct nmreq_header hdr;
+	int ret;
+
+	printf("Testing NETMAP_REQ_SYNC_KLOOP_START on '%s'\n", ctx->ifname_ext);
+
+	nmreq_hdr_init(&hdr, ctx->ifname_ext);
+	hdr.nr_reqtype = NETMAP_REQ_SYNC_KLOOP_START;
+	hdr.nr_body    = (uintptr_t)&req;
+	hdr.nr_options = (uintptr_t)ctx->nr_opt;
+	memset(&req, 0, sizeof(req));
+	req.sleep_us = 500;
+	ret          = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, SYNC_KLOOP_START)");
+	}
+
+	if (ctx->sem) {
+		sem_post(ctx->sem);
+	}
+
+	pthread_exit(ret ? (void *)THRET_FAILURE : (void *)THRET_SUCCESS);
+}
+
+static int
+sync_kloop_start_stop(struct TestContext *ctx)
+{
+	pthread_t th;
+	void *thret = THRET_FAILURE;
+	int ret;
+
+	ret = pthread_create(&th, NULL, sync_kloop_worker, ctx);
+	if (ret != 0) {
+		printf("pthread_create(kloop): %s\n", strerror(ret));
+		return -1;
+	}
+
+	ret = sync_kloop_stop(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = pthread_join(th, &thret);
+	if (ret != 0) {
+		printf("pthread_join(kloop): %s\n", strerror(ret));
+	}
+
+	return thret == THRET_SUCCESS ? 0 : -1;
+}
+
+static int
+sync_kloop(struct TestContext *ctx)
+{
+	int ret;
+
+	ret = csb_mode(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+
+	return sync_kloop_start_stop(ctx);
+}
+
+static int
+sync_kloop_eventfds(struct TestContext *ctx)
+{
+	struct nmreq_opt_sync_kloop_eventfds *evopt = NULL;
+	struct nmreq_opt_sync_kloop_mode modeopt;
+	struct nmreq_option evsave;
+	int num_entries;
+	size_t opt_size;
+	int ret, i;
+
+	memset(&modeopt, 0, sizeof(modeopt));
+	modeopt.nro_opt.nro_reqtype = NETMAP_REQ_OPT_SYNC_KLOOP_MODE;
+	modeopt.mode = ctx->sync_kloop_mode;
+	push_option(&modeopt.nro_opt, ctx);
+
+	num_entries = num_registered_rings(ctx);
+	opt_size    = sizeof(*evopt) + num_entries * sizeof(evopt->eventfds[0]);
+	evopt = calloc(1, opt_size);
+	evopt->nro_opt.nro_next    = 0;
+	evopt->nro_opt.nro_reqtype = NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS;
+	evopt->nro_opt.nro_status  = 0;
+	evopt->nro_opt.nro_size    = opt_size;
+	for (i = 0; i < num_entries; i++) {
+		int efd = eventfd(0, 0);
+
+		evopt->eventfds[i].ioeventfd = efd;
+		efd                        = eventfd(0, 0);
+		evopt->eventfds[i].irqfd = efd;
+	}
+
+	push_option(&evopt->nro_opt, ctx);
+	evsave = evopt->nro_opt;
+
+	ret = sync_kloop_start_stop(ctx);
+	if (ret != 0) {
+		free(evopt);
+		clear_options(ctx);
+		return ret;
+	}
+#ifdef __linux__
+	evsave.nro_status = 0;
+#else  /* !__linux__ */
+	evsave.nro_status = EOPNOTSUPP;
+#endif /* !__linux__ */
+
+	ret = checkoption(&evopt->nro_opt, &evsave);
+	free(evopt);
+	clear_options(ctx);
+
+	return ret;
+}
+
+static int
+sync_kloop_eventfds_all_mode(struct TestContext *ctx,
+			     uint32_t sync_kloop_mode)
+{
+	int ret;
+
+	ret = csb_mode(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ctx->sync_kloop_mode = sync_kloop_mode;
+
+	return sync_kloop_eventfds(ctx);
+}
+
+static int
+sync_kloop_eventfds_all(struct TestContext *ctx)
+{
+	return sync_kloop_eventfds_all_mode(ctx, 0);
+}
+
+static int
+sync_kloop_eventfds_all_tx(struct TestContext *ctx)
+{
+	struct nmreq_opt_csb opt;
+	int ret;
+
+	ret = push_csb_option(ctx, &opt);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = port_register_hwall_tx(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+	clear_options(ctx);
+
+	return sync_kloop_eventfds(ctx);
+}
+
+static int
+sync_kloop_eventfds_all_direct(struct TestContext *ctx)
+{
+	return sync_kloop_eventfds_all_mode(ctx,
+	    NM_OPT_SYNC_KLOOP_DIRECT_TX | NM_OPT_SYNC_KLOOP_DIRECT_RX);
+}
+
+static int
+sync_kloop_eventfds_all_direct_tx(struct TestContext *ctx)
+{
+	return sync_kloop_eventfds_all_mode(ctx,
+	    NM_OPT_SYNC_KLOOP_DIRECT_TX);
+}
+
+static int
+sync_kloop_eventfds_all_direct_rx(struct TestContext *ctx)
+{
+	return sync_kloop_eventfds_all_mode(ctx,
+	    NM_OPT_SYNC_KLOOP_DIRECT_RX);
+}
+
+static int
+sync_kloop_nocsb(struct TestContext *ctx)
+{
+	int ret;
+
+	ret = port_register_hwall(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+
+	/* Sync kloop must fail because we did not use
+	 * NETMAP_REQ_CSB_ENABLE. */
+	return sync_kloop_start_stop(ctx) != 0 ? 0 : -1;
+}
+
+static int
+csb_enable(struct TestContext *ctx)
+{
+	struct nmreq_option saveopt;
+	struct nmreq_opt_csb opt;
+	struct nmreq_header hdr;
+	int ret;
+
+	ret = push_csb_option(ctx, &opt);
+	if (ret != 0) {
+		return ret;
+	}
+	saveopt = opt.nro_opt;
+	saveopt.nro_status = 0;
+
+	nmreq_hdr_init(&hdr, ctx->ifname_ext);
+	hdr.nr_reqtype = NETMAP_REQ_CSB_ENABLE;
+	hdr.nr_options = (uintptr_t)ctx->nr_opt;
+	hdr.nr_body = (uintptr_t)NULL;
+
+	printf("Testing NETMAP_REQ_CSB_ENABLE on '%s'\n", ctx->ifname_ext);
+
+	ret           = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret != 0) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, CSB_ENABLE)");
+		return ret;
+	}
+
+	ret = checkoption(&opt.nro_opt, &saveopt);
+	clear_options(ctx);
+
+	return ret;
+}
+
+static int
+sync_kloop_csb_enable(struct TestContext *ctx)
+{
+	int ret;
+
+	ctx->nr_flags |= NR_EXCLUSIVE;
+	ret = port_register_hwall(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = csb_enable(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+
+	return sync_kloop_start_stop(ctx);
+}
+
+static int
+sync_kloop_conflict(struct TestContext *ctx)
+{
+	struct nmreq_opt_csb opt;
+	pthread_t th1, th2;
+	void *thret1 = THRET_FAILURE, *thret2 = THRET_FAILURE;
+	struct timespec to;
+	sem_t sem;
+	int err = 0;
+	int ret;
+
+	ret = push_csb_option(ctx, &opt);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = port_register_hwall(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+	clear_options(ctx);
+
+	ret = sem_init(&sem, 0, 0);
+	if (ret != 0) {
+		printf("sem_init() failed: %s\n", strerror(ret));
+		return ret;
+	}
+	ctx->sem = &sem;
+
+	ret = pthread_create(&th1, NULL, sync_kloop_worker, ctx);
+	err |= ret;
+	if (ret != 0) {
+		printf("pthread_create(kloop1): %s\n", strerror(ret));
+	}
+
+	ret = pthread_create(&th2, NULL, sync_kloop_worker, ctx);
+	err |= ret;
+	if (ret != 0) {
+		printf("pthread_create(kloop2): %s\n", strerror(ret));
+	}
+
+	/* Wait for one of the two threads to fail to start the kloop, to
+	 * avoid a race condition where th1 starts the loop and stops,
+	 * and after that th2 starts the loop successfully. */
+	clock_gettime(CLOCK_REALTIME, &to);
+	to.tv_sec += 2;
+	ret = sem_timedwait(&sem, &to);
+	err |= ret;
+	if (ret != 0) {
+		printf("sem_timedwait() failed: %s\n", strerror(errno));
+	}
+
+	err |= sync_kloop_stop(ctx);
+
+	ret = pthread_join(th1, &thret1);
+	err |= ret;
+	if (ret != 0) {
+		printf("pthread_join(kloop1): %s\n", strerror(ret));
+	}
+
+	ret = pthread_join(th2, &thret2);
+	err |= ret;
+	if (ret != 0) {
+		printf("pthread_join(kloop2): %s %d\n", strerror(ret), ret);
+	}
+
+	sem_destroy(&sem);
+	ctx->sem = NULL;
+	if (err) {
+		return err;
+	}
+
+	/* Check that one of the two failed, while the other one succeeded. */
+	return ((thret1 == THRET_SUCCESS && thret2 == THRET_FAILURE) ||
+			(thret1 == THRET_FAILURE && thret2 == THRET_SUCCESS))
+	               ? 0
+	               : -1;
+}
+
+static int
+sync_kloop_eventfds_mismatch(struct TestContext *ctx)
+{
+	struct nmreq_opt_csb opt;
+	int ret;
+
+	ret = push_csb_option(ctx, &opt);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = port_register_hwall_rx(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+	clear_options(ctx);
+
+	/* Deceive num_registered_rings() to trigger a failure of
+	 * sync_kloop_eventfds(). The latter will think that all the
+	 * rings were registered, and allocate the wrong number of
+	 * eventfds. */
+	ctx->nr_flags &= ~NR_RX_RINGS_ONLY;
+
+	return (sync_kloop_eventfds(ctx) != 0) ? 0 : -1;
+}
+
+static int
+null_port(struct TestContext *ctx)
+{
+	int ret;
+
+	ctx->nr_mem_id = 1;
+	ctx->nr_mode = NR_REG_NULL;
+	ctx->nr_tx_rings = 10;
+	ctx->nr_rx_rings = 5;
+	ctx->nr_tx_slots = 256;
+	ctx->nr_rx_slots = 100;
+	ret = port_register(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+	return 0;
+}
+
+static int
+null_port_all_zero(struct TestContext *ctx)
+{
+	int ret;
+
+	ctx->nr_mem_id = 1;
+	ctx->nr_mode = NR_REG_NULL;
+	ctx->nr_tx_rings = 0;
+	ctx->nr_rx_rings = 0;
+	ctx->nr_tx_slots = 0;
+	ctx->nr_rx_slots = 0;
+	ret = port_register(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+	return 0;
+}
+
+static int
+null_port_sync(struct TestContext *ctx)
+{
+	int ret;
+
+	ctx->nr_mem_id = 1;
+	ctx->nr_mode = NR_REG_NULL;
+	ctx->nr_tx_rings = 10;
+	ctx->nr_rx_rings = 5;
+	ctx->nr_tx_slots = 256;
+	ctx->nr_rx_slots = 100;
+	ret = port_register(ctx);
+	if (ret != 0) {
+		return ret;
+	}
+	ret = ioctl(ctx->fd, NIOCTXSYNC, 0);
+	if (ret != 0) {
+		return ret;
+	}
+	return 0;
+}
+
+struct nmreq_parse_test {
+	const char *ifname;
+	const char *exp_port;
+	const char *exp_suff;
+	int exp_error;
+	uint32_t exp_mode;
+	uint16_t exp_ringid;
+	uint64_t exp_flags;
+};
+
+static struct nmreq_parse_test nmreq_parse_tests[] = {
+	/* port spec is the input. The expected results are as follows:
+	 * - port: what should go into hdr.nr_name
+	 * - suff: the trailing part of the input after parsing (NULL means equal to port spec)
+	 * - err: the expected return value, interpreted as follows
+	 *       err > 0 => nmreq_header_parse should fail with the given error
+	 *       err < 0 => nrmeq_header_parse should succeed, but nmreq_register_decode should
+	 *       		   fail with error |err|
+	 *       err = 0 => should succeed
+	 * - mode, ringid flags: what should go into the corresponding nr_* fields in the
+	 *   	nmreq_register struct in case of success
+	 */
+
+	/*port spec*/			/*port*/	/*suff*/    /*err*/	/*mode*/    /*ringid*/ /*flags*/
+	{ "netmap:eth0",		"eth0",		"",		0, 	NR_REG_ALL_NIC,	0,	0 },
+	{ "netmap:eth0-1",		"eth0",		"",		0, 	NR_REG_ONE_NIC, 1,	0 },
+	{ "netmap:eth0-",		"eth0",		"-",		-EINVAL,0,		0,	0 },
+	{ "netmap:eth0/x",		"eth0",		"",		0, 	NR_REG_ALL_NIC, 0,	NR_EXCLUSIVE },
+	{ "netmap:eth0/z",		"eth0",		"",		0, 	NR_REG_ALL_NIC, 0,	NR_ZCOPY_MON },
+	{ "netmap:eth0/r",		"eth0",		"",		0, 	NR_REG_ALL_NIC, 0,	NR_MONITOR_RX },
+	{ "netmap:eth0/t",		"eth0",		"",		0, 	NR_REG_ALL_NIC, 0,	NR_MONITOR_TX },
+	{ "netmap:eth0-2/Tx",		"eth0",		"",		0, 	NR_REG_ONE_NIC, 2,	NR_TX_RINGS_ONLY|NR_EXCLUSIVE },
+	{ "netmap:eth0*",		"eth0",		"",		0, 	NR_REG_NIC_SW,  0,	0 },
+	{ "netmap:eth0^",		"eth0",		"",		0, 	NR_REG_SW,	0,	0 },
+	{ "netmap:eth0@2",		"eth0",	        "",		0,	NR_REG_ALL_NIC, 0,	0 },
+	{ "netmap:eth0@2/R",		"eth0",	        "",		0,	NR_REG_ALL_NIC, 0,	NR_RX_RINGS_ONLY },
+	{ "netmap:eth0@netmap:lo/R",	"eth0",	        "@netmap:lo/R",	0,	NR_REG_ALL_NIC,	0,	0 },
+	{ "netmap:eth0/R@xxx",		"eth0",	        "@xxx",		0,	NR_REG_ALL_NIC,	0,	NR_RX_RINGS_ONLY },
+	{ "netmap:eth0@2/R@2",		"eth0",	        "",		0,	NR_REG_ALL_NIC, 0,	NR_RX_RINGS_ONLY },
+	{ "netmap:eth0@2/R@3",		"eth0",	        "@2/R@3",	-EINVAL,0,		0,	0 },
+	{ "netmap:eth0@",		"eth0",	        "@",		-EINVAL,0,		0,	0 },
+	{ "netmap:",			"",		NULL,		EINVAL, 0,		0,	0 },
+	{ "netmap:^",			"",		NULL,		EINVAL,	0,		0,	0 },
+	{ "netmap:{",			"",		NULL,		EINVAL,	0,		0,	0 },
+	{ "netmap:vale0:0",		NULL,		NULL,		EINVAL,	0,		0,	0 },
+	{ "eth0",			NULL,		NULL,		EINVAL, 0,		0,	0 },
+	{ "vale0:0",			"vale0:0",	"",		0,	NR_REG_ALL_NIC, 0,	0 },
+	{ "vale:0",			"vale:0",	"",		0,	NR_REG_ALL_NIC, 0,	0 },
+	{ "valeXXX:YYY",		"valeXXX:YYY",	"",		0,	NR_REG_ALL_NIC, 0,	0 },
+	{ "valeXXX:YYY-4",		"valeXXX:YYY",	"",		0,	NR_REG_ONE_NIC, 4,	0 },
+	{ "netmapXXX:eth0",		NULL,		NULL,		EINVAL,	0,		0,	0 },
+	{ "netmap:14",			"14",		"",		0, 	NR_REG_ALL_NIC,	0,	0 },
+	{ "netmap:eth0&",		NULL,		NULL,		EINVAL, 0,		0,	0 },
+	{ "netmap:pipe{0",		"pipe{0",	"",		0,	NR_REG_ALL_NIC, 0,	0 },
+	{ "netmap:pipe{in",		"pipe{in",	"",		0,	NR_REG_ALL_NIC, 0,	0 },
+	{ "netmap:pipe{in-7",		"pipe{in",	"",		0,	NR_REG_ONE_NIC, 7,	0 },
+	{ "vale0:0{0",			"vale0:0{0",	"",		0,	NR_REG_ALL_NIC, 0,	0 },
+	{ "netmap:pipe{1}2",		NULL,		NULL,		EINVAL, 0,		0,	0 },
+	{ "vale0:0@opt", 		"vale0:0",	"@opt",		0,	NR_REG_ALL_NIC, 0,	0 },
+	{ "vale0:0/Tx@opt", 		"vale0:0",	"@opt",		0,	NR_REG_ALL_NIC, 0,	NR_TX_RINGS_ONLY|NR_EXCLUSIVE },
+	{ "vale0:0-3@opt", 		"vale0:0",	"@opt",		0,	NR_REG_ONE_NIC, 3,	0 },
+	{ "vale0:0@", 			"vale0:0",	"@",		-EINVAL,0,	        0,	0 },
+	{ "",				NULL,		NULL,		EINVAL, 0,		0,	0 },
+	{ NULL,				NULL,		NULL,		0, 	0,		0,	0 },
+};
+
+static void
+randomize(void *dst, size_t n)
+{
+	size_t i;
+	char *dst_ = dst;
+
+	for (i = 0; i < n; i++)
+		dst_[i] = (char)random();
+}
+
+static int
+nmreq_hdr_parsing(struct TestContext *ctx,
+		struct nmreq_parse_test *t,
+		struct nmreq_header *hdr)
+{
+	const char *save;
+	struct nmreq_header orig_hdr;
+
+	save = ctx->ifparse = t->ifname;
+	orig_hdr = *hdr;
+
+	printf("nmreq_header: \"%s\"\n", ctx->ifparse);
+	if (nmreq_header_decode(&ctx->ifparse, hdr, ctx->nmctx) < 0) {
+		if (t->exp_error > 0) {
+			if (errno != t->exp_error) {
+				printf("!!! got errno=%d, want %d\n",
+						errno, t->exp_error);
+				return -1;
+			}
+			if (ctx->ifparse != save) {
+				printf("!!! parse error, but first arg changed\n");
+				return -1;
+			}
+			if (memcmp(&orig_hdr, hdr, sizeof(*hdr))) {
+				printf("!!! parse error, but header changed\n");
+				return -1;
+			}
+			return 0;
+		}
+		printf ("!!! nmreq_header_decode was expected to succeed, but it failed with error %d\n", errno);
+		return -1;
+	}
+	if (t->exp_error > 0) {
+		printf("!!! nmreq_header_decode returns 0, but error %d was expected\n", t->exp_error);
+		return -1;
+	}
+	if (strcmp(t->exp_port, hdr->nr_name) != 0) {
+		printf("!!! got '%s', want '%s'\n", hdr->nr_name, t->exp_port);
+		return -1;
+	}
+	if (hdr->nr_reqtype != orig_hdr.nr_reqtype ||
+	    hdr->nr_options != orig_hdr.nr_options ||
+	    hdr->nr_body    != orig_hdr.nr_body) {
+		printf("!!! some fields of the nmreq_header where changed unexpectedly\n");
+		return -1;
+	}
+	return 0;
+}
+
+static int
+nmreq_reg_parsing(struct TestContext *ctx,
+		struct nmreq_parse_test *t,
+		struct nmreq_register *reg)
+{
+	const char *save;
+	struct nmreq_register orig_reg;
+
+
+	save = ctx->ifparse;
+	orig_reg = *reg;
+
+	printf("nmreq_register: \"%s\"\n", ctx->ifparse);
+	if (nmreq_register_decode(&ctx->ifparse, reg, ctx->nmctx) < 0) {
+		if (t->exp_error < 0) {
+			if (errno != -t->exp_error) {
+				printf("!!! got errno=%d, want %d\n",
+						errno, -t->exp_error);
+				return -1;
+			}
+			if (ctx->ifparse != save) {
+				printf("!!! parse error, but first arg changed\n");
+				return -1;
+			}
+			if (memcmp(&orig_reg, reg, sizeof(*reg))) {
+				printf("!!! parse error, but nmreq_register changed\n");
+				return -1;
+			}
+			return 0;
+		}
+		printf ("!!! parse failed but it should have succeeded\n");
+		return -1;
+	}
+	if (t->exp_error < 0) {
+		printf("!!! nmreq_register_decode returns 0, but error %d was expected\n", -t->exp_error);
+		return -1;
+	}
+	if (reg->nr_mode != t->exp_mode) {
+		printf("!!! got nr_mode '%d', want '%d'\n", reg->nr_mode, t->exp_mode);
+		return -1;
+	}
+	if (reg->nr_ringid != t->exp_ringid) {
+		printf("!!! got nr_ringid '%d', want '%d'\n", reg->nr_ringid, t->exp_ringid);
+		return -1;
+	}
+	if (reg->nr_flags != t->exp_flags) {
+		printf("!!! got nm_flags '%llx', want '%llx\n", (unsigned long long)reg->nr_flags,
+				(unsigned long long)t->exp_flags);
+		return -1;
+	}
+	if (reg->nr_offset     != orig_reg.nr_offset     ||
+	    reg->nr_memsize    != orig_reg.nr_memsize    ||
+	    reg->nr_tx_slots   != orig_reg.nr_tx_slots   ||
+	    reg->nr_rx_slots   != orig_reg.nr_rx_slots   ||
+	    reg->nr_tx_rings   != orig_reg.nr_tx_rings   ||
+	    reg->nr_rx_rings   != orig_reg.nr_rx_rings   ||
+	    reg->nr_extra_bufs != orig_reg.nr_extra_bufs)
+	{
+		printf("!!! some fields of the nmreq_register where changed unexpectedly\n");
+		return -1;
+	}
+	return 0;
+}
+
+static void
+nmctx_parsing_error(struct nmctx *ctx, const char *msg)
+{
+	(void)ctx;
+	printf("    got message: %s\n", msg);
+}
+
+static int
+nmreq_parsing(struct TestContext *ctx)
+{
+	struct nmreq_parse_test *t;
+	struct nmreq_header hdr;
+	struct nmreq_register reg;
+	struct nmctx test_nmctx, *nmctx;
+	int ret = 0;
+
+	nmctx = nmctx_get();
+	if (nmctx == NULL) {
+		printf("Failed to acquire nmctx: %s", strerror(errno));
+		return -1;
+	}
+	test_nmctx = *nmctx;
+	test_nmctx.error = nmctx_parsing_error;
+	ctx->nmctx = &test_nmctx;
+	for (t = nmreq_parse_tests; t->ifname != NULL; t++) {
+		const char *exp_suff = t->exp_suff != NULL ?
+			t->exp_suff : t->ifname;
+
+		randomize(&hdr, sizeof(hdr));
+		randomize(®, sizeof(reg));
+		reg.nr_mem_id = 0;
+		if (nmreq_hdr_parsing(ctx, t, &hdr) < 0) {
+			ret = -1;
+		} else if (t->exp_error <= 0 && nmreq_reg_parsing(ctx, t, ®) < 0) {
+			ret = -1;
+		}
+		if (strcmp(ctx->ifparse, exp_suff) != 0) {
+			printf("!!! string suffix after parse is '%s', but it should be '%s'\n",
+					ctx->ifparse, exp_suff);
+			ret = -1;
+		}
+	}
+	ctx->nmctx = NULL;
+	return ret;
+}
+
+static int
+binarycomp(struct TestContext *ctx)
+{
+#define ckroff(f, o) do {\
+	if (offsetof(struct netmap_ring, f) != (o)) {\
+		printf("offset of netmap_ring.%s is %zd, but it should be %d",\
+				#f, offsetof(struct netmap_ring, f), (o));\
+		return -1;\
+	}\
+} while (0)
+
+	(void)ctx;
+
+	ckroff(buf_ofs, 0);
+	ckroff(num_slots, 8);
+	ckroff(nr_buf_size, 12);
+	ckroff(ringid, 16);
+	ckroff(dir, 18);
+	ckroff(head, 20);
+	ckroff(cur, 24);
+	ckroff(tail, 28);
+	ckroff(flags, 32);
+	ckroff(ts, 40);
+	ckroff(offset_mask, 56);
+	ckroff(buf_align, 64);
+	ckroff(sem, 128);
+	ckroff(slot, 256);
+
+	return 0;
+}
+
+static void
+usage(const char *prog)
+{
+	printf("%s -i IFNAME\n"
+	       "[-j TEST_NUM1[-[TEST_NUM2]] | -[TEST_NUM_2]]\n"
+	       "[-l (list test cases)]\n",
+	       prog);
+}
+
+struct mytest {
+	testfunc_t test;
+	const char *name;
+};
+
+#define decltest(f)                                                            \
+	{                                                                      \
+		.test = f, .name = #f                                          \
+	}
+
+static struct mytest tests[] = {
+	decltest(port_info_get),
+	decltest(port_register_hwall_host),
+	decltest(port_register_hwall),
+	decltest(port_register_hostall),
+	decltest(port_register_single_hw_pair),
+	decltest(port_register_single_host_pair),
+	decltest(port_register_hostall_many),
+	decltest(vale_attach_detach),
+	decltest(vale_attach_detach_host_rings),
+	decltest(vale_ephemeral_port_hdr_manipulation),
+	decltest(vale_persistent_port),
+	decltest(pools_info_get_and_register),
+	decltest(pools_info_get_empty_ifname),
+	decltest(pipe_master),
+	decltest(pipe_slave),
+	decltest(pipe_port_info_get),
+	decltest(pipe_pools_info_get),
+	decltest(vale_polling_enable_disable),
+	decltest(unsupported_option),
+	decltest(infinite_options),
+	decltest(infinite_options2),
+	decltest(invalid_valid_options3),
+#ifdef CONFIG_NETMAP_EXTMEM
+	decltest(extmem_option),
+	decltest(bad_extmem_option),
+	decltest(duplicate_extmem_options),
+#endif /* CONFIG_NETMAP_EXTMEM */
+	decltest(csb_mode),
+	decltest(csb_mode_invalid_memory),
+	decltest(sync_kloop),
+	decltest(sync_kloop_eventfds_all),
+	decltest(sync_kloop_eventfds_all_tx),
+	decltest(sync_kloop_eventfds_all_direct),
+	decltest(sync_kloop_eventfds_all_direct_tx),
+	decltest(sync_kloop_eventfds_all_direct_rx),
+	decltest(sync_kloop_nocsb),
+	decltest(sync_kloop_csb_enable),
+	decltest(sync_kloop_conflict),
+	decltest(sync_kloop_eventfds_mismatch),
+	decltest(null_port),
+	decltest(null_port_all_zero),
+	decltest(null_port_sync),
+	decltest(legacy_regif_default),
+	decltest(legacy_regif_all_nic),
+	decltest(legacy_regif_12),
+	decltest(legacy_regif_sw),
+	decltest(legacy_regif_future),
+	decltest(legacy_regif_extra_bufs),
+	decltest(legacy_regif_extra_bufs_pipe),
+	decltest(legacy_regif_extra_bufs_pipe_vale),
+	decltest(nmreq_parsing),
+	decltest(binarycomp),
+};
+
+static void
+context_cleanup(struct TestContext *ctx)
+{
+	if (ctx->csb) {
+		free(ctx->csb);
+		ctx->csb = NULL;
+	}
+
+	close(ctx->fd);
+	ctx->fd = -1;
+}
+
+static int
+parse_interval(const char *arg, int *j, int *k)
+{
+	const char *scan = arg;
+	char *rest;
+
+	*j = 0;
+	*k = -1;
+	if (*scan == '-') {
+		scan++;
+		goto get_k;
+	}
+	if (!isdigit(*scan))
+		goto err;
+	*k = strtol(scan, &rest, 10);
+	*j = *k - 1;
+	scan = rest;
+	if (*scan == '-') {
+		*k = -1;
+		scan++;
+	}
+get_k:
+	if (*scan == '\0')
+		return 0;
+	if (!isdigit(*scan))
+		goto err;
+	*k = strtol(scan, &rest, 10);
+	scan = rest;
+	if (!(*scan == '\0'))
+		goto err;
+
+	return 0;
+
+err:
+	fprintf(stderr, "syntax error in '%s', must be num[-[num]] or -[num]\n", arg);
+	return -1;
+}
+
+#define ARGV_APPEND(_av, _ac, _x)\
+	do {\
+		assert((int)(_ac) < (int)(sizeof(_av)/sizeof((_av)[0])));\
+		(_av)[(_ac)++] = _x;\
+	} while (0)
+
+static void
+tap_cleanup(int signo)
+{
+	const char *av[8];
+	int ac = 0;
+
+	(void)signo;
+#ifdef __FreeBSD__
+	ARGV_APPEND(av, ac, "ifconfig");
+	ARGV_APPEND(av, ac, ctx_.ifname);
+	ARGV_APPEND(av, ac, "destroy");
+#else
+	ARGV_APPEND(av, ac, "ip");
+	ARGV_APPEND(av, ac, "link");
+	ARGV_APPEND(av, ac, "del");
+	ARGV_APPEND(av, ac, ctx_.ifname);
+#endif
+	ARGV_APPEND(av, ac, NULL);
+	if (exec_command(ac, av)) {
+		printf("Failed to destroy tap interface\n");
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	int create_tap = 1;
+	int num_tests;
+	int ret  = 0;
+	int j    = 0;
+	int k    = -1;
+	int list = 0;
+	int opt;
+	int i;
+
+#ifdef __FreeBSD__
+	PLAIN_REQUIRE_KERNEL_MODULE("if_tap", 0);
+	PLAIN_REQUIRE_KERNEL_MODULE("netmap", 0);
+#endif
+
+	memset(&ctx_, 0, sizeof(ctx_));
+
+	{
+		struct timespec t;
+		int idx;
+
+		clock_gettime(CLOCK_REALTIME, &t);
+		srand((unsigned int)t.tv_nsec);
+		idx = rand() % 8000 + 100;
+		snprintf(ctx_.ifname, sizeof(ctx_.ifname), "tap%d", idx);
+		idx = rand() % 800 + 100;
+		snprintf(ctx_.bdgname, sizeof(ctx_.bdgname), "vale%d", idx);
+	}
+
+	while ((opt = getopt(argc, argv, "hi:j:l")) != -1) {
+		switch (opt) {
+		case 'h':
+			usage(argv[0]);
+			return 0;
+
+		case 'i':
+			strncpy(ctx_.ifname, optarg, sizeof(ctx_.ifname) - 1);
+			create_tap = 0;
+			break;
+
+		case 'j':
+			if (parse_interval(optarg, &j, &k) < 0) {
+				usage(argv[0]);
+				return -1;
+			}
+			break;
+
+		case 'l':
+			list = 1;
+			create_tap = 0;
+			break;
+
+		default:
+			printf("    Unrecognized option %c\n", opt);
+			usage(argv[0]);
+			return -1;
+		}
+	}
+
+	num_tests = sizeof(tests) / sizeof(tests[0]);
+
+	if (j < 0 || j >= num_tests || k > num_tests) {
+		fprintf(stderr, "Test interval %d-%d out of range (%d-%d)\n",
+				j + 1, k, 1, num_tests + 1);
+		return -1;
+	}
+
+	if (k < 0)
+		k = num_tests;
+
+	if (list) {
+		printf("Available tests:\n");
+		for (i = 0; i < num_tests; i++) {
+			printf("#%03d: %s\n", i + 1, tests[i].name);
+		}
+		return 0;
+	}
+
+	if (create_tap) {
+		struct sigaction sa;
+		const char *av[8];
+		int ac = 0;
+#ifdef __FreeBSD__
+		ARGV_APPEND(av, ac, "ifconfig");
+		ARGV_APPEND(av, ac, ctx_.ifname);
+		ARGV_APPEND(av, ac, "create");
+		ARGV_APPEND(av, ac, "up");
+#else
+		ARGV_APPEND(av, ac, "ip");
+		ARGV_APPEND(av, ac, "tuntap");
+		ARGV_APPEND(av, ac, "add");
+		ARGV_APPEND(av, ac, "mode");
+		ARGV_APPEND(av, ac, "tap");
+		ARGV_APPEND(av, ac, "name");
+		ARGV_APPEND(av, ac, ctx_.ifname);
+#endif
+		ARGV_APPEND(av, ac, NULL);
+		if (exec_command(ac, av)) {
+			printf("Failed to create tap interface\n");
+			return -1;
+		}
+
+		sa.sa_handler = tap_cleanup;
+		sigemptyset(&sa.sa_mask);
+		sa.sa_flags = SA_RESTART;
+		ret         = sigaction(SIGINT, &sa, NULL);
+		if (ret) {
+			perror("sigaction(SIGINT)");
+			goto out;
+		}
+		ret = sigaction(SIGTERM, &sa, NULL);
+		if (ret) {
+			perror("sigaction(SIGTERM)");
+			goto out;
+		}
+	}
+
+	for (i = j; i < k; i++) {
+		struct TestContext ctxcopy;
+		int fd;
+		printf("==> Start of Test #%d [%s]\n", i + 1, tests[i].name);
+		fd = open("/dev/netmap", O_RDWR);
+		if (fd < 0) {
+			perror("open(/dev/netmap)");
+			ret = fd;
+			goto out;
+		}
+		memcpy(&ctxcopy, &ctx_, sizeof(ctxcopy));
+		ctxcopy.fd = fd;
+		memcpy(ctxcopy.ifname_ext, ctxcopy.ifname,
+			sizeof(ctxcopy.ifname));
+		ret        = tests[i].test(&ctxcopy);
+		if (ret != 0) {
+			printf("Test #%d [%s] failed\n", i + 1, tests[i].name);
+			goto out;
+		}
+		printf("==> Test #%d [%s] successful\n", i + 1, tests[i].name);
+		context_cleanup(&ctxcopy);
+	}
+out:
+	tap_cleanup(0);
+
+	return ret;
+}
diff --git a/utils/ctrs.h b/utils/ctrs.h
deleted file mode 100644
index 40c6b3dfb..000000000
--- a/utils/ctrs.h
+++ /dev/null
@@ -1,106 +0,0 @@
-#ifndef CTRS_H_
-#define CTRS_H_
-
-#include 
-
-/* counters to accumulate statistics */
-struct my_ctrs {
-	uint64_t pkts, bytes, events, drop;
-	uint64_t min_space;
-	struct timeval t;
-};
-
-/* very crude code to print a number in normalized form.
- * Caller has to make sure that the buffer is large enough.
- */
-static const char *
-norm2(char *buf, double val, char *fmt)
-{
-	char *units[] = { "", "K", "M", "G", "T" };
-	u_int i;
-
-	for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *) - 1; i++)
-		val /= 1000;
-	sprintf(buf, fmt, val, units[i]);
-	return buf;
-}
-
-static __inline const char *
-norm(char *buf, double val)
-{
-	return norm2(buf, val, "%.3f %s");
-}
-
-static __inline int
-timespec_ge(const struct timespec *a, const struct timespec *b)
-{
-
-	if (a->tv_sec > b->tv_sec)
-		return (1);
-	if (a->tv_sec < b->tv_sec)
-		return (0);
-	if (a->tv_nsec >= b->tv_nsec)
-		return (1);
-	return (0);
-}
-
-static __inline struct timespec
-timeval2spec(const struct timeval *a)
-{
-	struct timespec ts = {
-		.tv_sec = a->tv_sec,
-		.tv_nsec = a->tv_usec * 1000
-	};
-	return ts;
-}
-
-static __inline struct timeval
-timespec2val(const struct timespec *a)
-{
-	struct timeval tv = {
-		.tv_sec = a->tv_sec,
-		.tv_usec = a->tv_nsec / 1000
-	};
-	return tv;
-}
-
-
-static __inline struct timespec
-timespec_add(struct timespec a, struct timespec b)
-{
-	struct timespec ret = { a.tv_sec + b.tv_sec, a.tv_nsec + b.tv_nsec };
-	if (ret.tv_nsec >= 1000000000) {
-		ret.tv_sec++;
-		ret.tv_nsec -= 1000000000;
-	}
-	return ret;
-}
-
-static __inline struct timespec
-timespec_sub(struct timespec a, struct timespec b)
-{
-	struct timespec ret = { a.tv_sec - b.tv_sec, a.tv_nsec - b.tv_nsec };
-	if (ret.tv_nsec < 0) {
-		ret.tv_sec--;
-		ret.tv_nsec += 1000000000;
-	}
-	return ret;
-}
-
-static uint64_t
-wait_for_next_report(struct timeval *prev, struct timeval *cur,
-		int report_interval)
-{
-	struct timeval delta;
-
-	delta.tv_sec = report_interval/1000;
-	delta.tv_usec = (report_interval%1000)*1000;
-	if (select(0, NULL, NULL, NULL, &delta) < 0 && errno != EINTR) {
-		perror("select");
-		abort();
-	}
-	gettimeofday(cur, NULL);
-	timersub(cur, prev, &delta);
-	return delta.tv_sec* 1000000 + delta.tv_usec;
-}
-#endif /* CTRS_H_ */
diff --git a/utils/extmem-example.c b/utils/extmem-example.c
new file mode 100644
index 000000000..405f659c2
--- /dev/null
+++ b/utils/extmem-example.c
@@ -0,0 +1,128 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+/*
+ * example usage of the extmem feature. The feature is disabled by default.
+ * To enable it, add --enable-extmem to the ./configure command when you
+ * build netmap.
+ */
+int main(int argc, char *argv[])
+{
+	struct nmreq_header hdr;
+	struct nmreq_register req;
+	struct nmreq_opt_extmem ext;
+	struct netmap_if *nif;
+	void *addr;
+	int mem_fd, netmap_fd;
+	const char *ifname, *filename;
+	off_t filesize;
+
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s  \n", argv[0]);
+		exit(1);
+	}
+
+	ifname = argv[1];
+	filename = argv[2];
+
+	/* with extmem, the netmap port(s) data structures
+	 * (if, rings and buffers) will be allocated from a
+	 * user-provied memory area. This can be, for example,
+	 * a pseudo-file in the hugetlbfs.
+	 */
+
+	/* open and mmap the file */
+	mem_fd = open(filename, O_RDWR);
+	if (mem_fd < 0) {
+		perror(filename);
+		exit(1);
+	}
+
+	filesize = lseek(mem_fd, 0, SEEK_END);
+	if (filesize < 0) {
+		perror("lseek");
+		exit(1);
+	}
+
+	addr = mmap(NULL, filesize, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0);
+	if (addr == MAP_FAILED) {
+		perror("mmap");
+		exit(1);
+	}
+
+	/* the new netmap API has a NIOCCTRL ioctl() for all kinds
+	 * of netmap control requests (opening a port, creating
+	 * persistent vale ports, etc.). All requests are made up
+	 * of a common header (struct nmreq_header) which points
+	 * to a request-specific body (struct nmreq_register for
+	 * opening ports). The header may also point to a list of
+	 * options. Extmem is one such option.
+	 */
+
+	/* create an option with type EXTMEM, passing the address
+	 * of the mmap()ed memory and its size
+	 */
+	memset(&ext, 0, sizeof(ext));
+	ext.nro_opt.nro_reqtype = NETMAP_REQ_OPT_EXTMEM;
+	ext.nro_usrptr          = (uintptr_t)addr;
+	ext.nro_info.nr_memsize = filesize;
+
+	/* initialize the register request */
+	memset(&req, 0, sizeof(req));
+	req.nr_mode = NR_REG_ALL_NIC; /* or whatever */
+
+	/* initialize the header */
+	memset(&hdr, 0, sizeof(hdr));
+	hdr.nr_version = NETMAP_API;
+	/* NOTE: this is the ifname without the 'netmap:' prefix,
+	 * but possibly including the '{' or '}' symbol for opening
+	 * a netmap pipe. The pipe identifier can be any alphanumeric
+	 * string, not just numbers. For VALE ports, use the entire
+	 * valeXXX:yyy name.
+	 */
+	strncpy(hdr.nr_name, ifname, sizeof(hdr.nr_name) - 1);
+	hdr.nr_reqtype = NETMAP_REQ_REGISTER;
+	/* link the request body */
+	hdr.nr_body    = (uintptr_t)&req;
+	/* and the head of the options list */
+	hdr.nr_options = (uintptr_t)&ext;
+
+	/* now pass everything to the kernel */
+        netmap_fd = open("/dev/netmap", O_RDWR);
+	if (netmap_fd < 0) {
+		perror("/dev/netmap");
+		exit(1);
+	}
+
+	if (ioctl(netmap_fd, NIOCCTRL, &hdr) < 0) {
+		/* EOPNOTSUPP if extmem was not compiled in */
+		perror(ifname);
+		exit(1);
+	}
+
+	/* now we can use the mmap()ed area (NOTE: mmap() of "/dev/netmap" will
+	 * fail, so we must use the 'addr' obtained above.)
+	 *
+	 * Other processes can share the memory (e.g., to open other ports in
+	 * the same region) but they must mmap() the original file and go
+	 * through the same procedure as above.
+	 */
+
+	nif = NETMAP_IF(addr, req.nr_offset);
+
+	/* and so on ... */
+	(void)nif;
+	return 0;
+}
diff --git a/utils/fd_server-legacy.c b/utils/fd_server-legacy.c
new file mode 100644
index 000000000..2f2862a11
--- /dev/null
+++ b/utils/fd_server-legacy.c
@@ -0,0 +1,349 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#define NETMAP_WITH_LIBS
+#include 
+
+#include "fd_server-legacy.h"
+
+struct nmd_entry {
+	char if_name[NETMAP_REQ_IFNAMSIZ];
+	struct nm_desc *nmd;
+	uint8_t is_in_use;
+	uint8_t is_open;
+};
+
+#define printf(format, ...) syslog(LOG_NOTICE, format, ##__VA_ARGS__)
+
+#define MAX_OPEN_IF 128
+struct nmd_entry entries[MAX_OPEN_IF];
+int num_entries = 0;
+
+static void
+print_request(struct fd_request *req)
+{
+
+	printf("action: %s, if_name: '%s'\n",
+	       req->action == FD_GET
+	               ? "FD_GET"
+	               : req->action == FD_RELEASE
+	                         ? "FD_RELEASE"
+	                         : req->action == FD_CLOSE ? "FD_CLOSE"
+	                                                   : "FD_STOP",
+	       req->if_name);
+}
+
+struct nmd_entry *
+search_des(const char *if_name)
+{
+	int i;
+
+	// printf("searching %s\n", if_name);
+	for (i = 0; i < num_entries; ++i) {
+		struct nmd_entry *entry = &entries[i];
+
+		// printf("i=%d, is_open=%d, is_in_use=%d, if_name=%s\n",
+		// 	i, entry->is_open, entry->is_in_use, entry->if_name);
+
+		if (entry->is_open == 0) {
+			continue;
+		}
+
+		if (strncmp(entry->if_name, if_name, IFNAMSIZ) == 0) {
+			// printf("finished searching with a match\n");
+			return entry;
+		}
+	}
+
+	// printf("finished searching without a match\n");
+	return NULL;
+}
+
+struct nmd_entry *
+get_free_des(void)
+{
+	if (num_entries == MAX_OPEN_IF) {
+		return NULL;
+	}
+
+	return &entries[num_entries++];
+}
+
+int
+get_fd(const char *if_name, struct fd_response *res)
+{
+	struct nmd_entry *entry;
+
+	entry = search_des(if_name);
+	if (entry != NULL) {
+		if (entry->is_in_use == 1) {
+			printf("if_name %s is in use\n", if_name);
+			res->result = EBUSY;
+			return -1;
+		}
+		memcpy(&res->req, &entry->nmd->req, sizeof(entry->nmd->req));
+		return entry->nmd->fd;
+	}
+
+	entry = get_free_des();
+	if (entry == NULL) {
+		printf("Out of memory\n");
+		res->result = ENOMEM;
+		return -1;
+	}
+
+	entry->nmd = nm_open(if_name, NULL, 0, NULL);
+	if (entry->nmd == NULL) {
+		printf("Failed to nm_open(%s) with error %d\n", if_name, errno);
+		res->result = errno;
+		return -1;
+	}
+	strncpy(entry->if_name, if_name, sizeof(entry->if_name));
+	entry->if_name[sizeof(entry->if_name) - 1] = '\0';
+
+	memcpy(&res->req, &entry->nmd->req, sizeof(entry->nmd->req));
+	entry->is_in_use = 1;
+	entry->is_open   = 1;
+	return entry->nmd->fd;
+}
+
+void
+release_fd(const char *if_name, struct fd_response *res)
+{
+	struct nmd_entry *entry;
+
+	entry = search_des(if_name);
+	if (entry == NULL) {
+		printf("if_name %s isn't open\n", if_name);
+		res->result = ENOENT;
+		return;
+	}
+
+	entry->is_in_use = 0;
+}
+
+void
+close_fd(const char *if_name, struct fd_response *res)
+{
+	struct nmd_entry *entry;
+	int ret;
+
+	if (if_name == NULL || strnlen(if_name, NETMAP_REQ_IFNAMSIZ) == 0) {
+		res->result = EINVAL;
+		return;
+	}
+
+	entry = search_des(if_name);
+	if (entry == NULL) {
+		res->result = ENOENT;
+		printf("if_name %s hasn't been opened\n", if_name);
+		return;
+	}
+
+	ret         = nm_close(entry->nmd);
+	res->result = ret;
+	if (ret != 0) {
+		printf("error while close interface %s\n", if_name);
+		return;
+	}
+	entry->is_in_use = 0;
+	entry->is_open   = 0;
+}
+
+int
+send_fd(int socket, int fd, void *buf, size_t buf_size)
+{
+	union {
+		char buf[CMSG_SPACE(sizeof(int))];
+		struct cmsghdr align;
+	} ancillary;
+	struct cmsghdr *cmsg;
+	struct iovec iov[1];
+	struct msghdr msg;
+	int ret;
+
+	iov[0].iov_base = buf;
+	iov[0].iov_len  = buf_size;
+	memset(&msg, 0, sizeof(struct msghdr));
+	msg.msg_iov    = iov;
+	msg.msg_iovlen = 1;
+
+	if (fd >= 0) {
+		/* We need the ancillary data only when we're sending a file
+		 * descriptor, and a file descriptor cannot be negative.
+		 */
+		printf("sending a file descriptor\n");
+		msg.msg_control         = ancillary.buf;
+		msg.msg_controllen      = sizeof(ancillary.buf);
+		cmsg                    = CMSG_FIRSTHDR(&msg);
+		cmsg->cmsg_level        = SOL_SOCKET;
+		cmsg->cmsg_type         = SCM_RIGHTS;
+		cmsg->cmsg_len          = CMSG_LEN(sizeof(int));
+		memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+	}
+
+	ret = sendmsg(socket, &msg, 0);
+	return ret;
+}
+
+int
+handle_request(int accept_socket, int listen_socket)
+{
+	struct fd_response res;
+	struct fd_request req;
+	int fd = -1;
+	int amount;
+	int ret;
+
+	memset(&req, 0, sizeof(req));
+	amount = recv(accept_socket, &req, sizeof(struct fd_request), 0);
+	if (amount == -1) {
+		printf("error while receiving the request\n");
+		return -1;
+	}
+
+	print_request(&req);
+	memset(&res, 0, sizeof(res));
+	switch (req.action) {
+	case FD_GET:
+		fd = get_fd(req.if_name, &res);
+		break;
+	case FD_RELEASE:
+		release_fd(req.if_name, &res);
+		return 0;
+	case FD_CLOSE:
+		close_fd(req.if_name, &res);
+		return 0;
+	case FD_STOP:
+		printf("shutting down\n");
+		close(listen_socket);
+		close(accept_socket);
+		exit(EXIT_SUCCESS);
+		break;
+	default:
+		res.result = EOPNOTSUPP;
+	}
+
+	ret = send_fd(accept_socket, fd, &res, sizeof(struct fd_response));
+	if (ret == -1) {
+		printf("error while sending the response\n");
+	}
+	return ret;
+}
+
+void
+main_loop(void)
+{
+	struct sockaddr_un name;
+	int socket_fd;
+	int ret;
+
+	printf("starting up.\n");
+	if (unlink(SOCKET_NAME) == -1 && errno != ENOENT) {
+		printf("error %d during unlink()", errno);
+		exit(EXIT_FAILURE);
+	}
+	socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (socket_fd == -1) {
+		printf("error during socket()\n");
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&name, 0, sizeof(struct sockaddr_un));
+	name.sun_family = AF_UNIX;
+	strncpy(name.sun_path, SOCKET_NAME, sizeof(name.sun_path) - 1);
+	ret = bind(socket_fd, (const struct sockaddr *)&name,
+	           sizeof(struct sockaddr_un));
+	if (ret == -1) {
+		printf("error during bind()\n");
+		exit(EXIT_FAILURE);
+	}
+
+	ret = listen(socket_fd, 2);
+	if (ret == -1) {
+		printf("error during listen()");
+		exit(EXIT_FAILURE);
+	}
+
+	printf("listening\n");
+	for (;;) {
+		int conn_fd;
+		int ret;
+
+		conn_fd = accept(socket_fd, NULL, NULL);
+		if (conn_fd == -1) {
+			printf("error during accept(), shutting down\n");
+			exit(EXIT_FAILURE);
+		}
+
+		ret = handle_request(conn_fd, socket_fd);
+		if (ret == -1) {
+			printf("error while handling a request\n");
+		}
+		(void)ret;
+		close(conn_fd);
+	}
+}
+
+void
+daemonize(void)
+{
+	pid_t pid;
+	int i;
+
+	pid = fork();
+	if (pid < 0) {
+		exit(EXIT_FAILURE);
+	}
+	if (pid > 0) {
+		exit(EXIT_SUCCESS);
+	}
+
+	if (setsid() == -1) {
+		exit(EXIT_FAILURE);
+	}
+
+	signal(SIGCHLD, SIG_IGN);
+	signal(SIGHUP, SIG_IGN);
+
+	pid = fork();
+	if (pid < 0) {
+		exit(EXIT_FAILURE);
+	}
+	if (pid > 0) {
+		exit(EXIT_SUCCESS);
+	}
+
+	umask(0);
+
+	if (chdir("/") == -1) {
+		exit(EXIT_FAILURE);
+	}
+
+	for (i = sysconf(_SC_OPEN_MAX); i >= 0; i--) {
+		close(i);
+	}
+
+	openlog("nm_fd_server", LOG_PID, LOG_DAEMON);
+}
+
+int
+main()
+{
+	daemonize();
+	main_loop();
+	return 0;
+}
diff --git a/utils/fd_server-legacy.h b/utils/fd_server-legacy.h
new file mode 100644
index 000000000..3345b54f9
--- /dev/null
+++ b/utils/fd_server-legacy.h
@@ -0,0 +1,28 @@
+#ifndef FD_LIB_H
+#define FD_LIB_H
+
+#include 
+#include 
+#include 
+
+#define SOCKET_NAME "/tmp/netmap-fdserver-legacy"
+
+struct fd_request {
+#define FD_GET 1
+#define FD_RELEASE 2
+#define FD_CLOSE 3
+#define FD_STOP 4
+	uint8_t action;
+	char if_name[NETMAP_REQ_IFNAMSIZ];
+};
+
+struct fd_response {
+	int32_t result;
+	struct nmreq req;
+};
+
+int send_fd(int socket, int fd, void *buf, size_t buf_size);
+
+int recv_fd(int socket, int *fd, void *buf, size_t buf_size);
+
+#endif /* FD_LIB_H */
diff --git a/utils/fd_server.c b/utils/fd_server.c
new file mode 100644
index 000000000..fa0db1c9b
--- /dev/null
+++ b/utils/fd_server.c
@@ -0,0 +1,387 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#include "fd_server.h"
+
+struct nmd_entry {
+	char if_name[NETMAP_REQ_IFNAMSIZ];
+	struct nmport_d *nmd;
+	uint8_t is_in_use;
+	uint8_t is_open;
+};
+
+int foreground = 0;
+
+#define msg(format, ...) do {					\
+	if (foreground) {					\
+		printf(format, ##__VA_ARGS__);			\
+	} else {						\
+		syslog(LOG_NOTICE, format, ##__VA_ARGS__);	\
+	}							\
+} while(0)
+
+#define MAX_OPEN_IF 128
+struct nmd_entry entries[MAX_OPEN_IF];
+int num_entries = 0;
+
+static void
+print_request(struct fd_request *req)
+{
+
+	msg("action: %s, if_name: '%s'\n",
+	       req->action == FD_GET
+	               ? "FD_GET"
+	               : req->action == FD_RELEASE
+	                         ? "FD_RELEASE"
+	                         : req->action == FD_CLOSE ? "FD_CLOSE"
+	                                                   : "FD_STOP",
+	       req->if_name);
+}
+
+struct nmd_entry *
+search_des(const char *if_name)
+{
+	int i;
+
+	// msg("searching %s\n", if_name);
+	for (i = 0; i < num_entries; ++i) {
+		struct nmd_entry *entry = &entries[i];
+
+		// msg("i=%d, is_open=%d, is_in_use=%d, if_name=%s\n",
+		// 	i, entry->is_open, entry->is_in_use, entry->if_name);
+
+		if (entry->is_open == 0) {
+			continue;
+		}
+
+		if (strncmp(entry->if_name, if_name, IFNAMSIZ) == 0) {
+			// msg("finished searching with a match\n");
+			return entry;
+		}
+	}
+
+	// msg("finished searching without a match\n");
+	return NULL;
+}
+
+struct nmd_entry *
+get_free_des(void)
+{
+	if (num_entries == MAX_OPEN_IF) {
+		return NULL;
+	}
+
+	return &entries[num_entries++];
+}
+
+int
+marshal(struct fd_response *res, struct nmd_entry *entry)
+{
+	if (entry->nmd->hdr.nr_options) {
+		msg("options are not supported\n");
+		res->result = EOPNOTSUPP;
+		return -1;
+	}
+
+	// copy the header
+	res->hdr = entry->nmd->hdr;
+	res->hdr.nr_options = 0;
+	res->hdr.nr_body = 0;
+	// copy the body
+	res->reg = entry->nmd->reg;
+	return 0;
+}
+
+int
+get_fd(const char *if_name, struct fd_response *res)
+{
+	struct nmd_entry *entry;
+
+	entry = search_des(if_name);
+	if (entry != NULL) {
+		if (entry->is_in_use == 1) {
+			msg("if_name %s is in use\n", if_name);
+			res->result = EBUSY;
+			return -1;
+		}
+		if (marshal(res, entry) < 0)
+			return -1;
+		res->result = 0;
+		return entry->nmd->fd;
+	}
+
+	entry = get_free_des();
+	if (entry == NULL) {
+		msg("Out of memory\n");
+		res->result = ENOMEM;
+		return -1;
+	}
+
+	entry->nmd = nmport_open(if_name);
+	if (entry->nmd == NULL) {
+		msg("Failed to nm_open(%s) with error %d\n", if_name, errno);
+		res->result = errno;
+		return -1;
+	}
+	strncpy(entry->if_name, if_name, sizeof(entry->if_name));
+	entry->if_name[sizeof(entry->if_name) - 1] = '\0';
+
+	if (marshal(res, entry) < 0)
+		return -1;
+	res->result = 0;
+	entry->is_in_use = 1;
+	entry->is_open   = 1;
+	return entry->nmd->fd;
+}
+
+void
+release_fd(const char *if_name, struct fd_response *res)
+{
+	struct nmd_entry *entry;
+
+	entry = search_des(if_name);
+	if (entry == NULL) {
+		msg("if_name %s isn't open\n", if_name);
+		res->result = ENOENT;
+		return;
+	}
+
+	entry->is_in_use = 0;
+}
+
+void
+close_fd(const char *if_name, struct fd_response *res)
+{
+	struct nmd_entry *entry;
+
+	if (if_name == NULL || strnlen(if_name, NETMAP_REQ_IFNAMSIZ) == 0) {
+		res->result = EINVAL;
+		return;
+	}
+
+	entry = search_des(if_name);
+	if (entry == NULL) {
+		res->result = ENOENT;
+		msg("if_name %s hasn't been opened\n", if_name);
+		return;
+	}
+
+	nmport_close(entry->nmd);
+	res->result = 0;
+	entry->is_in_use = 0;
+	entry->is_open   = 0;
+}
+
+int
+send_fd(int socket, int fd, void *buf, size_t buf_size)
+{
+	union {
+		char buf[CMSG_SPACE(sizeof(int))];
+		struct cmsghdr align;
+	} ancillary;
+	struct cmsghdr *cmsg;
+	struct iovec iov[1];
+	struct msghdr msg;
+	int ret;
+
+	iov[0].iov_base = buf;
+	iov[0].iov_len  = buf_size;
+	memset(&msg, 0, sizeof(struct msghdr));
+	msg.msg_iov    = iov;
+	msg.msg_iovlen = 1;
+
+	if (fd >= 0) {
+		/* We need the ancillary data only when we're sending a file
+		 * descriptor, and a file descriptor cannot be negative.
+		 */
+		msg("sending a file descriptor\n");
+		msg.msg_control         = ancillary.buf;
+		msg.msg_controllen      = sizeof(ancillary.buf);
+		cmsg                    = CMSG_FIRSTHDR(&msg);
+		cmsg->cmsg_level        = SOL_SOCKET;
+		cmsg->cmsg_type         = SCM_RIGHTS;
+		cmsg->cmsg_len          = CMSG_LEN(sizeof(int));
+		memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+	}
+
+	ret = sendmsg(socket, &msg, 0);
+	return ret;
+}
+
+int
+handle_request(int accept_socket, int listen_socket)
+{
+	struct fd_response res;
+	struct fd_request req;
+	int fd = -1;
+	int amount;
+	int ret;
+
+	memset(&req, 0, sizeof(req));
+	amount = recv(accept_socket, &req, sizeof(struct fd_request), 0);
+	if (amount == -1) {
+		msg("error while receiving the request\n");
+		return -1;
+	}
+
+	print_request(&req);
+	switch (req.action) {
+	case FD_GET:
+		fd = get_fd(req.if_name, &res);
+		break;
+	case FD_RELEASE:
+		release_fd(req.if_name, &res);
+		return 0;
+	case FD_CLOSE:
+		close_fd(req.if_name, &res);
+		return 0;
+	case FD_STOP:
+		msg("shutting down\n");
+		close(listen_socket);
+		close(accept_socket);
+		exit(EXIT_SUCCESS);
+		break;
+	default:
+		res.result = EOPNOTSUPP;
+	}
+
+	ret = send_fd(accept_socket, fd, &res, sizeof(res));
+	if (ret == -1) {
+		msg("error while sending the response\n");
+	}
+	return ret;
+}
+
+void
+main_loop(void)
+{
+	struct sockaddr_un name;
+	int socket_fd;
+	int ret;
+
+	msg("starting up.\n");
+	if (unlink(SOCKET_NAME) == -1 && errno != ENOENT) {
+		msg("error %d during unlink()", errno);
+		exit(EXIT_FAILURE);
+	}
+	socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (socket_fd == -1) {
+		msg("error during socket()\n");
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&name, 0, sizeof(struct sockaddr_un));
+	name.sun_family = AF_UNIX;
+	strncpy(name.sun_path, SOCKET_NAME, sizeof(name.sun_path) - 1);
+	ret = bind(socket_fd, (const struct sockaddr *)&name,
+	           sizeof(struct sockaddr_un));
+	if (ret == -1) {
+		msg("error during bind()\n");
+		exit(EXIT_FAILURE);
+	}
+
+	ret = listen(socket_fd, 2);
+	if (ret == -1) {
+		msg("error during listen()");
+		exit(EXIT_FAILURE);
+	}
+
+	msg("listening\n");
+	for (;;) {
+		int conn_fd;
+		int ret;
+
+		conn_fd = accept(socket_fd, NULL, NULL);
+		if (conn_fd == -1) {
+			msg("error during accept(), shutting down\n");
+			exit(EXIT_FAILURE);
+		}
+
+		ret = handle_request(conn_fd, socket_fd);
+		if (ret == -1) {
+			msg("error while handling a request\n");
+		}
+		(void)ret;
+		close(conn_fd);
+	}
+}
+
+void
+daemonize(void)
+{
+	pid_t pid;
+	int i;
+
+	pid = fork();
+	if (pid < 0) {
+		exit(EXIT_FAILURE);
+	}
+	if (pid > 0) {
+		exit(EXIT_SUCCESS);
+	}
+
+	if (setsid() == -1) {
+		exit(EXIT_FAILURE);
+	}
+
+	signal(SIGCHLD, SIG_IGN);
+	signal(SIGHUP, SIG_IGN);
+
+	pid = fork();
+	if (pid < 0) {
+		exit(EXIT_FAILURE);
+	}
+	if (pid > 0) {
+		exit(EXIT_SUCCESS);
+	}
+
+	umask(0);
+
+	if (chdir("/") == -1) {
+		exit(EXIT_FAILURE);
+	}
+
+	for (i = sysconf(_SC_OPEN_MAX); i >= 0; i--) {
+		close(i);
+	}
+
+	openlog("nm_fd_server", LOG_PID, LOG_DAEMON);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int opt;
+
+	while ( (opt = getopt(argc, argv, "f")) != -1) {
+		switch (opt) {
+		case 'f':
+			foreground = 1;
+			break;
+		default:
+			fprintf(stderr, "Unknown option: %c\n", opt);
+			exit(EXIT_FAILURE);
+			break;
+		}
+	}
+	if (!foreground)
+		daemonize();
+	main_loop();
+	return 0;
+}
diff --git a/utils/fd_server.h b/utils/fd_server.h
new file mode 100644
index 000000000..825318dea
--- /dev/null
+++ b/utils/fd_server.h
@@ -0,0 +1,29 @@
+#ifndef FD_LIB_H
+#define FD_LIB_H
+
+#include 
+#include 
+#include 
+
+#define SOCKET_NAME "/tmp/netmap-fdserver"
+
+struct fd_request {
+#define FD_GET 1
+#define FD_RELEASE 2
+#define FD_CLOSE 3
+#define FD_STOP 4
+	uint8_t action;
+	char if_name[NETMAP_REQ_IFNAMSIZ];
+};
+
+struct fd_response {
+	int32_t result;
+	struct nmreq_header hdr;
+	struct nmreq_register reg;
+};
+
+int send_fd(int socket, int fd, void *buf, size_t buf_size);
+
+int recv_fd(int socket, int *fd, void *buf, size_t buf_size);
+
+#endif /* FD_LIB_H */
diff --git a/utils/functional-legacy.c b/utils/functional-legacy.c
new file mode 100644
index 000000000..7b84823de
--- /dev/null
+++ b/utils/functional-legacy.c
@@ -0,0 +1,1461 @@
+/*
+ * A tool for functional testing netmap transmission and reception.
+ *
+ * Copyright (C) 2018 Vincenzo Maffione. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#define NETMAP_WITH_LIBS
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "fd_server-legacy.h"
+
+#define ETH_ADDR_LEN 6
+
+struct Event {
+	unsigned evtype;
+#define EVENT_TYPE_RX 0x1
+#define EVENT_TYPE_TX 0x2
+#define EVENT_TYPE_PAUSE 0x3
+	unsigned num; /* > 1 if repeated event */
+
+	/* Tx and Rx event. */
+	unsigned pkt_len;
+	char filler;
+
+	/* Pause event. */
+	unsigned long long usecs;
+};
+
+struct extra_buffer {
+	uint32_t buf_idx;
+	TAILQ_ENTRY(extra_buffer) list_entry;
+};
+
+;
+
+struct Global {
+	struct nm_desc *nmd;
+	const char *ifname;
+	unsigned wait_link_secs;    /* wait for link */
+	unsigned timeout_secs;      /* transmit/receive timeout */
+	int ignore_if_not_matching; /* ignore certain received packets */
+	int success_if_no_receive;  /* exit status 0 if we receive no packets */
+	int sequential_fill;        /* increment fill char for multi-packets
+	                            operations */
+	int request_from_fd_server; /* false --> directly open the interface */
+
+#define LV_ERROR_MSG 1
+#define LV_DEBUG_SEND_RECV 2
+#define LV_DEBUG_EXTRA_BUF 3
+#define LV_DEBUG_BUILD_PACKET 4
+#define LV_DEBUG_PARSE_ARGS 5
+	int verbosity_level;
+
+	/* List of currently not in use normal buffers. */
+	TAILQ_HEAD(extra_buf_head, extra_buffer) extra_buffers_head;
+	unsigned extra_buffers_num; /* number of granted extra buffers */
+
+#define MAX_PKT_SIZE 65536
+	char pktm[MAX_PKT_SIZE]; /* packet model */
+	unsigned pktm_len;       /* packet model length */
+	char pktr[MAX_PKT_SIZE]; /* packet received */
+	unsigned pktr_len;       /* length of received packet */
+	unsigned max_frag_size;  /* max bytes per netmap TX slot */
+
+	char src_mac[ETH_ADDR_LEN];
+	char dst_mac[ETH_ADDR_LEN];
+	uint32_t src_ip;
+	uint32_t dst_ip;
+	uint16_t src_port;
+	uint16_t dst_port;
+	char filler;
+
+#define MAX_EVENTS 64
+	unsigned num_events;
+	struct Event events[MAX_EVENTS];
+	unsigned num_loops;
+};
+
+void release_if_fd(struct Global *, const char *);
+void release_extra_buffers(struct Global *);
+
+void
+verbose_print(int current_verbosity, int required_verbosity, char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	if (current_verbosity >= required_verbosity) {
+		vprintf(format, args);
+	}
+
+	va_end(args);
+}
+
+void
+verbose_perror(int current_verbosity, int required_verbosity, char *str)
+{
+	if (current_verbosity >= required_verbosity) {
+		perror(str);
+	}
+}
+
+void
+cleanup(struct Global *g)
+{
+	if (g->extra_buffers_num > 0) {
+		release_extra_buffers(g);
+	}
+
+	if (g->request_from_fd_server) {
+		release_if_fd(g, g->ifname);
+	} else {
+		nm_close(g->nmd);
+	}
+}
+
+static void
+fill_packet_field(struct Global *g, unsigned offset, const char *content,
+                  unsigned content_len)
+{
+	if (offset + content_len > sizeof(g->pktm)) {
+		verbose_print(g->verbosity_level, LV_ERROR_MSG,
+		              "Packet layout overflow: %u + %u > %lu\n", offset,
+		              content_len, sizeof(g->pktm));
+		cleanup(g);
+		exit(EXIT_FAILURE);
+	}
+
+	memcpy(g->pktm + offset, content, content_len);
+}
+
+static void
+fill_packet_8bit(struct Global *g, unsigned offset, uint8_t val)
+{
+	fill_packet_field(g, offset, (const char *)&val, sizeof(val));
+}
+
+static void
+fill_packet_16bit(struct Global *g, unsigned offset, uint16_t val)
+{
+	val = htons(val);
+	fill_packet_field(g, offset, (const char *)&val, sizeof(val));
+}
+
+static void
+fill_packet_32bit(struct Global *g, unsigned offset, uint32_t val)
+{
+	val = htonl(val);
+	fill_packet_field(g, offset, (const char *)&val, sizeof(val));
+}
+
+/* Compute the checksum of the given ip header. */
+static uint32_t
+checksum(const void *data, uint16_t len, uint32_t sum /* host endianness */)
+{
+	const uint8_t *addr = data;
+	uint32_t i;
+
+	/* Checksum all the pairs of bytes first... */
+	for (i = 0; i < (len & ~1U); i += 2) {
+		sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i)));
+		if (sum > 0xFFFF) {
+			sum -= 0xFFFF;
+		}
+	}
+	/*
+	 * If there's a single byte left over, checksum it, too.
+	 * Network byte order is big-endian, so the remaining byte is
+	 * the high byte.
+	 */
+	if (i < len) {
+		sum += addr[i] << 8;
+		if (sum > 0xFFFF) {
+			sum -= 0xFFFF;
+		}
+	}
+	return sum;
+}
+
+static uint16_t
+wrapsum(uint32_t sum /* host endianness */)
+{
+	sum = ~sum & 0xFFFF;
+	return sum; /* host endianness */
+}
+
+static void
+build_packet(struct Global *g)
+{
+	unsigned ofs = 0;
+	unsigned ethofs;
+	unsigned ipofs;
+	unsigned udpofs;
+	unsigned pldofs;
+
+	memset(g->pktm, 0, sizeof(g->pktm));
+	verbose_print(g->verbosity_level, LV_DEBUG_BUILD_PACKET,
+	              "%s: starting at ofs %u\n", __func__, ofs);
+
+	ethofs = ofs;
+	(void)ethofs;
+	/* Ethernet destination and source MAC address plus ethertype. */
+	fill_packet_field(g, ofs, g->dst_mac, ETH_ADDR_LEN);
+	ofs += ETH_ADDR_LEN;
+	fill_packet_field(g, ofs, g->src_mac, ETH_ADDR_LEN);
+	ofs += ETH_ADDR_LEN;
+	fill_packet_16bit(g, ofs, ETHERTYPE_IP);
+	ofs += 2;
+	verbose_print(g->verbosity_level, LV_DEBUG_BUILD_PACKET,
+	              "%s: eth done, ofs %u\n", __func__, ofs);
+
+	ipofs = ofs;
+	/* First byte of IP header. */
+	fill_packet_8bit(g, ofs, (IPVERSION << 4) | ((sizeof(struct ip)) >> 2));
+	ofs += 1;
+	/* Skip QoS byte. */
+	ofs += 1;
+	/* Total length. */
+	fill_packet_16bit(g, ofs, g->pktm_len - ipofs);
+	ofs += 2;
+	/* Skip identification field. */
+	ofs += 2;
+	/* Offset (and flags) field. */
+	fill_packet_16bit(g, ofs, IP_DF);
+	ofs += 2;
+	/* TTL. */
+	fill_packet_8bit(g, ofs, IPDEFTTL);
+	ofs += 1;
+	/* Protocol. */
+	fill_packet_8bit(g, ofs, IPPROTO_UDP);
+	ofs += 1;
+	/* Skip checksum for now. */
+	ofs += 2;
+	/* Source IP address. */
+	fill_packet_32bit(g, ofs, g->src_ip);
+	ofs += 4;
+	/* Dst IP address. */
+	fill_packet_32bit(g, ofs, g->dst_ip);
+	ofs += 4;
+	/* Now put the checksum. */
+	fill_packet_16bit(
+	        g, ipofs + 10,
+	        wrapsum(checksum(g->pktm + ipofs, sizeof(struct ip), 0)));
+	verbose_print(g->verbosity_level, LV_DEBUG_BUILD_PACKET,
+	              "%s: ip done, ofs %u\n", __func__, ofs);
+
+	udpofs = ofs;
+	/* UDP source port. */
+	fill_packet_16bit(g, ofs, g->src_port);
+	ofs += 2;
+	/* UDP source port. */
+	fill_packet_16bit(g, ofs, g->dst_port);
+	ofs += 2;
+	/* UDP length (UDP header + data). */
+	fill_packet_16bit(g, ofs, g->pktm_len - udpofs);
+	ofs += 2;
+	/* Skip the UDP checksum for now. */
+	ofs += 2;
+	verbose_print(g->verbosity_level, LV_DEBUG_BUILD_PACKET,
+	              "%s: udp done, ofs %u\n", __func__, ofs);
+
+	/* Fill UDP payload. */
+	pldofs = ofs;
+	for (; ofs < g->pktm_len; ofs++) {
+		fill_packet_8bit(g, ofs, g->filler);
+	}
+	verbose_print(g->verbosity_level, LV_DEBUG_BUILD_PACKET,
+	              "%s: payload done, ofs %u\n", __func__, ofs);
+
+	/* Put the UDP checksum now.
+	 * Magic: taken from sbin/dhclient/packet.c */
+	fill_packet_16bit(
+	        g, udpofs + 6,
+	        wrapsum(checksum(
+	                /* udp header */ g->pktm + udpofs,
+	                sizeof(struct udphdr),
+	                checksum(/* udp payload */ g->pktm + pldofs,
+	                         g->pktm_len - pldofs,
+	                         checksum(/* pseudo header */ g->pktm + ipofs +
+	                                          12,
+	                                  2 * sizeof(g->src_ip),
+	                                  IPPROTO_UDP + (uint32_t)(g->pktm_len -
+	                                                           udpofs))))));
+}
+
+static int
+tx_flush(struct Global *g)
+{
+	struct nm_desc *nmd = g->nmd;
+	unsigned elapsed_ms = 0;
+	unsigned wait_ms    = 100;
+	int i;
+
+	for (;;) {
+		int pending = 0;
+		for (i = nmd->first_tx_ring; i <= nmd->last_tx_ring; i++) {
+			struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, i);
+
+			pending += nm_tx_pending(ring);
+		}
+
+		if (!pending) {
+			return 0;
+		}
+
+		if (elapsed_ms > g->timeout_secs * 1000) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "%s: Timeout\n", __func__);
+			return -1;
+		}
+
+		usleep(wait_ms * 1000);
+		elapsed_ms += wait_ms;
+
+		ioctl(nmd->fd, NIOCTXSYNC, NULL);
+	}
+}
+
+uint64_t
+ring_avail_packets(struct netmap_ring *ring, unsigned pkt_len)
+{
+	uint64_t slot_per_packet;
+
+	slot_per_packet = ceil((double)pkt_len / (double)ring->nr_buf_size);
+	return nm_ring_space(ring) / slot_per_packet;
+}
+
+uint64_t
+adapter_avail_sends(struct nm_desc *nmd, unsigned pkt_len)
+{
+	uint64_t sends_available = 0;
+	unsigned int i;
+
+	for (i = nmd->first_tx_ring; i <= nmd->last_tx_ring; i++) {
+		struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, i);
+
+		sends_available += ring_avail_packets(ring, pkt_len);
+	}
+
+	return sends_available;
+}
+
+void
+put_one_packet(struct Global *g, struct netmap_ring *ring)
+{
+	unsigned head  = ring->head;
+	unsigned frags = 0;
+	unsigned ofs   = 0;
+
+	for (;;) {
+		struct netmap_slot *slot = &ring->slot[head];
+		char *buf                = NETMAP_BUF(ring, slot->buf_idx);
+		unsigned copysize        = g->pktm_len - ofs;
+
+		if (copysize > ring->nr_buf_size) {
+			copysize = ring->nr_buf_size;
+		}
+		if (copysize > g->max_frag_size) {
+			copysize = g->max_frag_size;
+		}
+
+		memcpy(buf, g->pktm + ofs, copysize);
+		ofs += copysize;
+		slot->len   = copysize;
+		slot->flags = NS_MOREFRAG;
+		head        = nm_ring_next(ring, head);
+		frags++;
+		if (ofs >= g->pktm_len) {
+			/* Last fragment. */
+			assert(ofs == g->pktm_len);
+			slot->flags = NS_REPORT;
+			break;
+		}
+	}
+
+	ring->head = ring->cur = head;
+	verbose_print(g->verbosity_level, LV_DEBUG_SEND_RECV,
+	              "packet (%u bytes, %u frags) placed to TX\n", g->pktm_len,
+	              frags);
+}
+
+/* Used for multi-packets sequential send/receive actions */
+char
+next_fill(char cur_fill)
+{
+	if (cur_fill == 'z')
+		return 'a';
+	if (cur_fill == 'Z')
+		return 'A';
+	return ++cur_fill;
+}
+
+/* Transmit packets_num packets using any combination of TX rings. */
+static int
+tx(struct Global *g, unsigned packets_num)
+{
+	struct nm_desc *nmd = g->nmd;
+	unsigned elapsed_ms = 0;
+	unsigned wait_ms    = 100;
+	unsigned int i;
+
+	/* We cycle here until either we timeout or we find enough space. */
+	for (;;) {
+		if (adapter_avail_sends(nmd, g->pktm_len) >= packets_num) {
+			break;
+		}
+
+		if (elapsed_ms > g->timeout_secs * 1000) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "%s: Timeout\n", __func__);
+			return -1;
+		}
+
+		/* Retry after a short while. */
+		usleep(wait_ms * 1000);
+		elapsed_ms += wait_ms;
+		ioctl(nmd->fd, NIOCTXSYNC, NULL);
+	}
+
+	/* Once we have enough space, we start filling slots. We might use
+	 * multiple rings.
+	 */
+	for (i = nmd->first_tx_ring; i <= nmd->last_tx_ring; i++) {
+		struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, i);
+		uint64_t ring_sends_num;
+
+		for (ring_sends_num = ring_avail_packets(ring, g->pktm_len);
+		     ring_sends_num > 0 && packets_num > 0;
+		     --ring_sends_num, --packets_num) {
+			put_one_packet(g, ring);
+
+			if (g->sequential_fill == 1) {
+				g->filler = next_fill(g->filler);
+				build_packet(g);
+			}
+		}
+
+		if (packets_num == 0) {
+			break;
+		}
+	}
+
+	assert(packets_num == 0);
+	/* Once we're done we sync, sending all packets at once. */
+	ioctl(nmd->fd, NIOCTXSYNC, NULL);
+	return 0;
+}
+
+/* If -I option is specified, we want to ignore frames that don't match
+ * our expected ethernet header.
+ * This function currently assumes that Ethernet header starts from
+ * the beginning of the packet buffers. */
+static int
+ignore_received_frame(struct Global *g)
+{
+	if (!g->ignore_if_not_matching) {
+		return 0; /* don't ignore */
+	}
+
+	if (g->pktr_len < 14 || memcmp(g->pktm, g->pktr, 14) != 0) {
+		return 1; /* ignore */
+	}
+
+	return 0; /* don't ignore */
+}
+
+uint64_t
+adapter_avail_receives(struct nm_desc *nmd, unsigned pkt_len)
+{
+	uint64_t receives_available = 0;
+	unsigned int i;
+
+	for (i = nmd->first_rx_ring; i <= nmd->last_rx_ring; i++) {
+		struct netmap_ring *ring = NETMAP_RXRING(nmd->nifp, i);
+
+		receives_available += ring_avail_packets(ring, pkt_len);
+	}
+
+	return receives_available;
+}
+
+static int
+rx_check(struct Global *g)
+{
+	unsigned i;
+
+	if (g->pktr_len != g->pktm_len) {
+		verbose_print(g->verbosity_level, LV_ERROR_MSG,
+		              "Received packet length (%u) different from "
+		              "expected (%u bytes)\n",
+		              g->pktr_len, g->pktm_len);
+		return -1;
+	}
+
+	for (i = 0; i < g->pktr_len; i++) {
+		if (g->pktr[i] != g->pktm[i]) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "Received packet differs from model at "
+			              "offset %u (0x%02x!=0x%02x)\n",
+			              i, g->pktr[i], (uint8_t)g->pktm[i]);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+int
+read_one_packet(struct Global *g, struct netmap_ring *ring)
+{
+	unsigned head = ring->head;
+	int frags     = 0;
+
+	g->pktr_len = 0;
+	for (;;) {
+		struct netmap_slot *slot = &ring->slot[head];
+		char *buf                = NETMAP_BUF(ring, slot->buf_idx);
+
+		if (g->pktr_len + slot->len > sizeof(g->pktr)) {
+			/* Sanity check. */
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "Error: received packet too "
+			              "large "
+			              "(>= %u bytes) ",
+			              g->pktr_len + slot->len);
+			cleanup(g);
+			exit(EXIT_FAILURE);
+		}
+
+		memcpy(g->pktr + g->pktr_len, buf, slot->len);
+		g->pktr_len += slot->len;
+		head = nm_ring_next(ring, head);
+		frags++;
+		if (!(slot->flags & NS_MOREFRAG)) {
+			break;
+		}
+
+		if (head == ring->tail) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "warning: truncated packet "
+			              "(len=%u)\n",
+			              g->pktr_len);
+			frags = -1;
+			break;
+		}
+	}
+
+	ring->head = ring->cur = head;
+	verbose_print(g->verbosity_level, LV_DEBUG_SEND_RECV,
+	              "packet (%u bytes, %d frags) received "
+	              "from RX\n",
+	              g->pktr_len, frags);
+	return frags;
+}
+
+/* Receive packets_num packets from any combination of RX rings. */
+static int
+rx(struct Global *g, unsigned packets_num)
+{
+	struct nm_desc *nmd = g->nmd;
+	unsigned elapsed_ms = 0;
+	unsigned wait_ms    = 100;
+	unsigned int i;
+
+	/* We cycle here until either we timeout or we find enough space. */
+	for (;;) {
+	again:
+		if (adapter_avail_receives(nmd, g->pktm_len) >= packets_num) {
+			break;
+		}
+
+		if (elapsed_ms > g->timeout_secs * 1000) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "%s: Timeout\n", __func__);
+			/* -n flag */
+			return g->success_if_no_receive == 1 ? 0 : -1;
+		}
+
+		/* Retry after a short while. */
+		usleep(wait_ms * 1000);
+		elapsed_ms += wait_ms;
+		ioctl(nmd->fd, NIOCRXSYNC, NULL);
+	}
+
+	/* Once we have enough space, we start reading packets. We might use
+	 * multiple rings.
+	 */
+	for (i = nmd->first_rx_ring; i <= nmd->last_rx_ring; i++) {
+		struct netmap_ring *ring = NETMAP_RXRING(nmd->nifp, i);
+		uint64_t ring_receives_num;
+
+		for (ring_receives_num = ring_avail_packets(ring, g->pktm_len);
+		     ring_receives_num > 0 && packets_num > 0;
+		     --ring_receives_num, --packets_num) {
+			int frags = 0;
+
+			frags = read_one_packet(g, ring);
+			if (frags == -1) {
+				break; /* Truncated packet, skip this ring. */
+			}
+
+			if (ignore_received_frame(g)) {
+				verbose_print(g->verbosity_level,
+				              LV_DEBUG_SEND_RECV,
+				              "(ignoring packet with %u bytes "
+				              "and "
+				              "%d frags received from RX ring "
+				              "#%d)\n",
+				              g->pktr_len, frags, i);
+				elapsed_ms = 0;
+				/* We can go back there, because we're
+				 * decrementing packets_num each time, therefore
+				 * the we will wait only for the remaining
+				 * packets.
+				 */
+				goto again;
+			}
+
+			/* As soon as we find a packet which doesn't match our
+			 * packet model we exit with status EXIT_FAILURE.
+			 */
+			if (rx_check(g)) {
+				cleanup(g);
+				exit(EXIT_FAILURE);
+			}
+
+			if (g->sequential_fill == 1) {
+				g->filler = next_fill(g->filler);
+				build_packet(g);
+			}
+		}
+
+		if (packets_num == 0) {
+			break;
+		}
+	}
+
+	assert(packets_num == 0);
+	/* Once we're done we sync, freeing all slots at once. */
+	ioctl(nmd->fd, NIOCRXSYNC, NULL);
+	return 0;
+}
+
+static int
+parse_txrx_event(const char *opt, unsigned event_type, struct Event *event,
+                 int verbosity_level)
+{
+	char *strbuf = strdup(opt);
+	char *save   = strbuf;
+	int more;
+	char *c;
+	int ret = -1;
+
+	if (!strbuf || strlen(strbuf) == 0) {
+		goto out;
+	}
+
+	event->evtype = event_type;
+	event->filler = 'a';
+	event->num    = 1;
+
+	for (c = strbuf; *c != '\0' && *c != ':'; c++) {
+	}
+	more           = (*c == ':');
+	*c             = '\0';
+	event->pkt_len = atoi(strbuf);
+	if (event->pkt_len == 0) {
+		goto out;
+	}
+	if (more) {
+		strbuf = c + 1;
+		for (c = strbuf; *c != '\0' && *c != ':'; c++) {
+		}
+		more          = (*c == ':');
+		*c            = '\0';
+		event->filler = strbuf[0];
+	}
+	if (more) {
+		strbuf = c + 1;
+		for (c = strbuf; *c != '\0'; c++) {
+		}
+		event->num = atoi(strbuf);
+		if (event->num == 0) {
+			goto out;
+		}
+	}
+
+	ret = 0;
+	verbose_print(verbosity_level, LV_DEBUG_PARSE_ARGS, "parsed %u:%c:%u\n",
+	              event->pkt_len, event->filler, event->num);
+out:
+	if (save) {
+		free(save);
+	}
+	return ret;
+}
+
+static int
+parse_pause_event(const char *opt, struct Event *event, int verbosity_level)
+{
+	char *strbuf = strdup(opt);
+	char *save   = strbuf;
+	unsigned mul = 1000000;
+	int ret      = -1;
+
+	while (*strbuf != '\0' && isdigit(*strbuf)) {
+		strbuf++;
+	}
+	if (!strcmp(strbuf, "us")) {
+		mul = 1;
+	} else if (!strcmp(strbuf, "ms")) {
+		mul = 1000;
+	} else if (strcmp(strbuf, "s") && strcmp(strbuf, "")) {
+		goto out;
+	}
+
+	event->evtype = EVENT_TYPE_PAUSE;
+	event->usecs  = atoi(save);
+	if (event->usecs == 0) {
+		goto out;
+	}
+
+	event->usecs *= mul;
+	event->num = 1;
+	ret        = 0;
+	verbose_print(verbosity_level, LV_DEBUG_PARSE_ARGS,
+	              "parsed %llu usecs\n", event->usecs);
+out:
+	free(save);
+	return ret;
+}
+
+static struct Global _g;
+
+static void
+usage(FILE *stream)
+{
+	fprintf(stream,
+	        "usage: ./functional {-c | -o | -i | -I}\n"
+	        "Required:\n"
+	        "    -c (shuts down the fd server),\n"
+	        "    -o (starts the fd server),\n"
+	        "    -i NETMAP_PORT (requests the interface from the fd "
+	        "server),\n"
+	        "    -I NETMAP_PORT (directly opens the interface)\n"
+	        "Optional:\n"
+	        "    [-s SOURCE MAC ADDRESS (=0:0:0:0:0:0)]\n"
+	        "    [-d DESTINATION MAC ADDRESS (=FF:FF:FF:FF:FF:FF)]\n"
+	        "    [-F MAX_FRAGMENT_SIZE (=inf)]\n"
+	        "    [-T TIMEOUT_SECS (=1)]\n"
+	        "    [-w WAIT_FOR_LINK_SECS (=0)]\n"
+	        "    [-t LEN[:FILLCHAR[:NUM]] (transmit NUM packets with size "
+	        "LEN bytes)]\n"
+	        "    [-r LEN[:FILLCHAR[:NUM]] (expect to receive NUM packets "
+	        "with size LEN bytes)]\n"
+	        "    [-p NUM[us|ms|s]] (pause for NUM us/ms/s)]\n"
+	        "    [-g (ignore ethernet frames with unmatching Ethernet "
+	        "header)]\n"
+	        "    [-n (exit status = 0 <==> no frames were received)]\n"
+	        "    [-q (during multi-packets send/receive increments fill "
+	        "character after each operation)]\n"
+	        "    [-e NUM (use NUM extra buffers to send packets, "
+	        "can only be used when with -I)]\n"
+	        "    [-v (increment verbosity level)]\n"
+	        "    [-C [NUM (=1)] (how many times to run the events)]\n"
+	        "\nExample:\n"
+	        "    $ ./functional -i netmap:lo -t 100 -r 100 -t 40:b:2 -r "
+	        "40:b:2\n");
+}
+
+/* TODO: Move functions to communicate to the fd_server to another file */
+/* Copied from nm_open() */
+void
+fill_nm_desc(struct nm_desc *des, struct nmreq *req, int fd)
+{
+	uint32_t nr_reg;
+
+	memset(des, 0, sizeof(*des));
+	des->self = des;
+	des->fd   = fd;
+	memcpy(&des->req, req, sizeof(des->req));
+	nr_reg = req->nr_flags & NR_REG_MASK;
+
+	if (nr_reg == NR_REG_SW) { /* host stack */
+		des->first_tx_ring = des->last_tx_ring = des->req.nr_tx_rings;
+		des->first_rx_ring = des->last_rx_ring = des->req.nr_rx_rings;
+	} else if (nr_reg == NR_REG_ALL_NIC) { /* only nic */
+		des->first_tx_ring = 0;
+		des->first_rx_ring = 0;
+		des->last_tx_ring  = des->req.nr_tx_rings - 1;
+		des->last_rx_ring  = des->req.nr_rx_rings - 1;
+	} else if (nr_reg == NR_REG_NIC_SW) {
+		des->first_tx_ring = 0;
+		des->first_rx_ring = 0;
+		des->last_tx_ring  = des->req.nr_tx_rings;
+		des->last_rx_ring  = des->req.nr_rx_rings;
+	} else if (nr_reg == NR_REG_ONE_NIC) {
+		/* XXX check validity */
+		des->first_tx_ring = des->last_tx_ring = des->first_rx_ring =
+		        des->last_rx_ring =
+		                des->req.nr_ringid & NETMAP_RING_MASK;
+	} else { /* pipes */
+		des->first_tx_ring = des->last_tx_ring = 0;
+		des->first_rx_ring = des->last_rx_ring = 0;
+	}
+}
+
+int
+connect_to_fd_server(struct Global *g)
+{
+	struct sockaddr_un name;
+	unsigned elapsed_ms = 0;
+	unsigned wait_ms    = 100;
+	int socket_fd;
+
+	socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (socket_fd == -1) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "socket()");
+		return -1;
+	}
+
+	memset(&name, 0, sizeof(name));
+	name.sun_family = AF_UNIX;
+	strncpy(name.sun_path, SOCKET_NAME, sizeof(name.sun_path) - 1);
+	name.sun_path[sizeof(name.sun_path) - 1] = '\0';
+	while (connect(socket_fd, (const struct sockaddr *)&name,
+	               sizeof(struct sockaddr_un)) == -1) {
+		if (elapsed_ms > g->timeout_secs * 1000) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "%s: Timeout\n", __func__);
+			return -1;
+		}
+
+		usleep(wait_ms * 1000);
+		elapsed_ms += wait_ms;
+	}
+
+	return socket_fd;
+}
+
+void
+start_fd_server(struct Global *g)
+{
+	int socket_fd;
+	pid_t pid;
+
+	pid = fork();
+	if (pid < 0) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "fork()");
+		exit(EXIT_FAILURE);
+	}
+	if (pid > 0) {
+		wait(NULL);
+		return;
+	}
+
+	if (execlp("fd_server-legacy", "fd_server-legacy", (char *)NULL)) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "exec()");
+		exit(EXIT_FAILURE);
+	}
+
+	socket_fd = connect_to_fd_server(g);
+	if (socket_fd == -1) {
+		verbose_print(g->verbosity_level, LV_ERROR_MSG,
+		              "Can't connect to fd_server\n");
+		exit(EXIT_FAILURE);
+	}
+	close(socket_fd);
+}
+
+int
+recv_fd(int socket, int *fd, void *buf, size_t buf_size)
+{
+	union {
+		char buf[CMSG_SPACE(sizeof(int))];
+		struct cmsghdr align;
+	} ancillary;
+	struct fd_response *res;
+	struct cmsghdr *cmsg;
+	struct iovec iov[1];
+	struct msghdr msg;
+	int amount;
+
+	errno           = 0;
+	iov[0].iov_base = buf;
+	iov[0].iov_len  = buf_size;
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov    = iov;
+	msg.msg_iovlen = 1;
+	memset(ancillary.buf, 0, sizeof(ancillary.buf));
+	msg.msg_control    = ancillary.buf;
+	msg.msg_controllen = sizeof(ancillary.buf);
+	cmsg               = CMSG_FIRSTHDR(&msg);
+	cmsg->cmsg_level   = SOL_SOCKET;
+	cmsg->cmsg_type    = SCM_RIGHTS;
+	cmsg->cmsg_len     = CMSG_LEN(sizeof(int));
+	amount             = recvmsg(socket, &msg, 0);
+	if (amount == -1) {
+		return -1;
+	}
+
+	res = iov[0].iov_base;
+	if (res->result != 0) {
+		errno = res->result;
+		return -1;
+	}
+
+	/* If res->result == 0, we know for sure that a file descriptor has been
+	 * sent through the ancillary data.
+	 */
+	cmsg = CMSG_FIRSTHDR(&msg);
+	memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+
+	return amount;
+}
+
+struct nm_desc *
+get_if_fd(struct Global *g, const char *if_name)
+{
+	struct fd_response res;
+	struct fd_request req;
+	struct nm_desc *nmd;
+	int socket_fd;
+	int new_fd;
+	int ret;
+
+	socket_fd = connect_to_fd_server(g);
+	if (socket_fd == -1) {
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.action = FD_GET;
+	strncpy(req.if_name, if_name, sizeof(req.if_name)-1);
+	ret = send(socket_fd, &req, sizeof(req), 0);
+	if (ret < 0) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "send()");
+		return NULL;
+	}
+
+	memset(&res, 0, sizeof(res));
+	ret = recv_fd(socket_fd, &new_fd, &res, sizeof(res));
+	if (ret == -1) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "recv_fd()");
+		return NULL;
+	}
+	close(socket_fd);
+
+	nmd = malloc(sizeof(*nmd));
+	if (nmd == NULL) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "malloc()");
+		return NULL;
+	}
+
+	fill_nm_desc(nmd, &res.req, new_fd);
+	if (nm_mmap(nmd, NULL) != 0) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "nm_mmap()");
+		return NULL;
+	}
+
+	return nmd;
+}
+
+void
+release_if_fd(struct Global *g, const char *if_name)
+{
+	struct fd_request req;
+	int socket_fd;
+	int ret;
+
+	socket_fd = connect_to_fd_server(g);
+	if (socket_fd == -1) {
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.action = FD_RELEASE;
+	strncpy(req.if_name, if_name, sizeof(req.if_name)-1);
+
+	ret = send(socket_fd, &req, sizeof(req), 0);
+	if (ret <= 0) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "send()");
+	}
+
+	close(socket_fd);
+}
+
+void
+stop_fd_server(struct Global *g)
+{
+	struct fd_request req;
+	int socket_fd;
+	int ret;
+
+	socket_fd = connect_to_fd_server(g);
+	if (socket_fd == -1) {
+		verbose_print(g->verbosity_level, LV_DEBUG_SEND_RECV,
+		              "fd_server already down\n");
+		return;
+	}
+	verbose_print(g->verbosity_level, LV_DEBUG_SEND_RECV,
+	              "Shutting down fd_server\n");
+
+	memset(&req, 0, sizeof(req));
+	req.action = FD_STOP;
+	ret        = send(socket_fd, &req, sizeof(req), 0);
+	if (ret == -1) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "send()");
+	}
+	/* By calling recv() we synchronize with the fd_server closing the
+	 * socket.
+	 * This way we're sure that during the next call to ./functional
+	 * the fd_server has already closed its end and we avoid a possible race
+	 * condition. Otherwise the call to functional might connect to the
+	 * previous fd_server backlog.
+	 */
+	recv(socket_fd, &req, sizeof(req), 0);
+	close(socket_fd);
+}
+
+int
+parse_mac_address(const char *opt, char *mac)
+{
+	if (6 == sscanf(opt, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mac[0], &mac[1],
+	                &mac[2], &mac[3], &mac[4], &mac[5])) {
+		return 0;
+	}
+	return -1;
+}
+
+/* Uses the first adapter slot (any will do) to save the extra buffers indexes.
+ */
+int
+parse_extra_buffers_indexes(struct Global *g)
+{
+	struct netmap_if *nifp   = g->nmd->nifp;
+	struct netmap_ring *ring = NETMAP_TXRING(nifp, g->nmd->first_tx_ring);
+	struct netmap_slot *slot = &ring->slot[ring->head];
+	uint32_t extra_buf_index = nifp->ni_bufs_head;
+	uint32_t real_index      = slot->buf_idx;
+	struct extra_buffer *u_buf;
+	unsigned i;
+
+	verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "Parsing %u extra buffers:\n", g->extra_buffers_num);
+	for (i = 0; i < g->extra_buffers_num; i++) {
+		if (extra_buf_index == 0) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "   error, index = 0\n");
+			return -1;
+		}
+		verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "   index = %u\n", extra_buf_index);
+
+		u_buf = malloc(sizeof(*u_buf));
+		if (u_buf == NULL) {
+			verbose_perror(g->verbosity_level, LV_ERROR_MSG,
+			               "malloc()");
+			return -1;
+		}
+		u_buf->buf_idx = extra_buf_index;
+		TAILQ_INSERT_HEAD(&g->extra_buffers_head, u_buf, list_entry);
+		slot->buf_idx   = extra_buf_index;
+		extra_buf_index = *(uint32_t *)NETMAP_BUF(ring, slot->buf_idx);
+	}
+	slot->buf_idx = real_index;
+
+	return 0;
+}
+
+/* Loops through the adapter slots, swapping the default buffers with the
+ * extra buffers. Keeps going until we run out of extra buffers, or adapter
+ * slots.
+ */
+int
+swap_in_extra_buffers(struct Global *g)
+{
+	unsigned extra_buffers_num = g->extra_buffers_num;
+	unsigned int i;
+
+	for (i = g->nmd->first_tx_ring; i <= g->nmd->last_tx_ring; i++) {
+		struct netmap_ring *ring = NETMAP_TXRING(g->nmd->nifp, i);
+		unsigned head;
+
+		for (head = ring->head; head != ring->tail;
+		     head = nm_ring_next(ring, head)) {
+			struct extra_buffer *u_buf =
+			        TAILQ_FIRST(&g->extra_buffers_head);
+			struct netmap_slot *slot = &ring->slot[head];
+			uint32_t real_index      = slot->buf_idx;
+
+			if (u_buf == NULL) {
+				/* We finished swapping in extra buffers */
+				return 0;
+			}
+
+			slot->buf_idx = u_buf->buf_idx;
+			slot->flags |= NS_BUF_CHANGED;
+			u_buf->buf_idx = real_index;
+			TAILQ_REMOVE(&g->extra_buffers_head, u_buf, list_entry);
+			TAILQ_INSERT_TAIL(&g->extra_buffers_head, u_buf,
+			                 list_entry);
+
+			if (--extra_buffers_num == 0) {
+				return 0;
+			}
+		}
+	}
+
+	/* This is reached if the adapter has less slots than the number of
+	 * requested extra buffers. Nevertheless this is not a problem as the
+	 * not in use extra buffers will will be released during cleanup().
+	 */
+	return 0;
+}
+
+/* We only re-build the extra buffers list, as requested from netmap. We don't
+ * undo the swapping that we did at the start of the program to swap in the
+ * extra buffer. This probably leaves the netmap adapter in an inconsistent
+ * state, that's why we only support this option for interfaces requested
+ * directly.
+ */
+void
+release_extra_buffers(struct Global *g)
+{
+	struct netmap_if *nifp   = g->nmd->nifp;
+	struct netmap_ring *ring = NETMAP_TXRING(nifp, g->nmd->first_tx_ring);
+	struct netmap_slot *slot = &ring->slot[ring->head];
+	uint32_t real_index      = slot->buf_idx;
+	struct extra_buffer *u_buf;
+	uint32_t *next_extra_buffer;
+
+	verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "Releasing %u extra buffers:\n", g->extra_buffers_num);
+	if (TAILQ_EMPTY(&g->extra_buffers_head)) {
+		return;
+	}
+
+	u_buf = TAILQ_FIRST(&g->extra_buffers_head);
+	nifp->ni_bufs_head = u_buf->buf_idx;
+	verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "   head index %u\n", nifp->ni_bufs_head);
+	slot->buf_idx = u_buf->buf_idx;
+	TAILQ_REMOVE(&g->extra_buffers_head, u_buf, list_entry);
+	free(u_buf);
+
+	while (!TAILQ_EMPTY(&g->extra_buffers_head)) {
+		next_extra_buffer = (uint32_t *)NETMAP_BUF(ring, slot->buf_idx);
+		u_buf = TAILQ_FIRST(&g->extra_buffers_head);
+		verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "   index = %u\n", u_buf->buf_idx);
+		*next_extra_buffer = u_buf->buf_idx;
+		slot->buf_idx = u_buf->buf_idx;
+		TAILQ_REMOVE(&g->extra_buffers_head, u_buf, list_entry);
+		free(u_buf);
+	}
+	next_extra_buffer = (uint32_t *)NETMAP_BUF(ring, slot->buf_idx);
+	*next_extra_buffer = 0;
+	slot->buf_idx = real_index;
+}
+
+int
+main(int argc, char **argv)
+{
+	struct Global *g = &_g;
+	unsigned int i, c;
+	int opt;
+	int ret;
+
+	g->nmd            = NULL;
+	g->ifname         = NULL;
+	g->wait_link_secs = 0;
+	g->timeout_secs   = 1;
+	g->pktm_len       = 60;
+	g->max_frag_size  = ~0U; /* unlimited */
+	for (i = 0; i < ETH_ADDR_LEN; i++) {
+		g->src_mac[i] = 0x00;
+	}
+	for (i = 0; i < ETH_ADDR_LEN; i++) {
+		g->dst_mac[i] = 0xFF;
+	}
+	g->src_ip                 = 0x0A000005; /* 10.0.0.5 */
+	g->dst_ip                 = 0x0A000007; /* 10.0.0.7 */
+	g->filler                 = 'a';
+	g->num_events             = 0;
+	g->ignore_if_not_matching = /*false=*/0;
+	g->success_if_no_receive  = /*false=*/0;
+	g->request_from_fd_server = /*true=*/1;
+	g->sequential_fill        = /*false=*/0;
+	g->extra_buffers_num      = 0;
+	g->verbosity_level        = 0;
+	g->num_loops              = 1;
+	g->extra_buffers_num      = 0;
+	TAILQ_INIT(&g->extra_buffers_head);
+
+	while ((opt = getopt(argc, argv, "hconqe:s:d:i:I:w:F:T:t:r:gvp:C:")) !=
+	       -1) {
+		switch (opt) {
+		case 'h':
+			usage(stdout);
+			return 0;
+
+		/* TODO: move this option to fd_server */
+		case 'c':
+			stop_fd_server(g);
+			return 0;
+
+		/* TODO: move this option to fd_server */
+		case 'o':
+			start_fd_server(g);
+			return 0;
+
+		case 'n':
+			g->success_if_no_receive = /*true=*/1;
+			break;
+
+		case 'q':
+			g->sequential_fill = /*true=*/1;
+			break;
+
+		case 'e':
+			g->extra_buffers_num = atoi(optarg);
+			if (g->extra_buffers_num <= 0) {
+				verbose_print(
+				        g->verbosity_level, LV_ERROR_MSG,
+				        "Invalid number of extra buffers\n");
+				exit(EXIT_FAILURE);
+			};
+			verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "Requesting %u extra buffers\n", g->extra_buffers_num);
+			break;
+
+		case 's':
+			ret = parse_mac_address(optarg, g->src_mac);
+			if (ret == -1) {
+				verbose_print(g->verbosity_level, LV_ERROR_MSG,
+				              "Invalid source MAC address\n");
+				exit(EXIT_FAILURE);
+			}
+			break;
+
+		case 'd':
+			ret = parse_mac_address(optarg, g->dst_mac);
+			if (ret == -1) {
+				verbose_print(
+				        g->verbosity_level, LV_ERROR_MSG,
+				        "Invalid destination MAC address\n");
+				exit(EXIT_FAILURE);
+			}
+			break;
+
+		case 'i':
+			g->ifname = optarg;
+			break;
+
+		case 'I':
+			g->ifname                 = optarg;
+			g->request_from_fd_server = /*false=*/0;
+			break;
+
+		case 'F':
+			g->max_frag_size = atoi(optarg);
+			break;
+
+		case 'w':
+			g->wait_link_secs = atoi(optarg);
+			break;
+
+		case 'T':
+			g->timeout_secs = atoi(optarg);
+			break;
+
+		case 't':
+		case 'r':
+		case 'p': {
+			int ret = 0;
+
+			if (g->num_events >= MAX_EVENTS) {
+				verbose_print(g->verbosity_level, LV_ERROR_MSG,
+				              "Too many events\n");
+				exit(EXIT_FAILURE);
+			}
+
+			if (opt == 'p') {
+				ret = parse_pause_event(
+				        optarg, g->events + g->num_events,
+				        g->verbosity_level);
+			} else {
+				ret = parse_txrx_event(
+				        optarg,
+				        (opt == 't') ? EVENT_TYPE_TX
+				                     : EVENT_TYPE_RX,
+				        g->events + g->num_events,
+				        g->verbosity_level);
+			}
+			if (ret) {
+				verbose_print(g->verbosity_level, LV_ERROR_MSG,
+				              "Invalid event syntax '%s'\n",
+				              optarg);
+				usage(stderr);
+				exit(EXIT_FAILURE);
+			}
+			g->num_events++;
+			break;
+		}
+
+		case 'g':
+			g->ignore_if_not_matching = 1;
+			break;
+
+		case 'v':
+			g->verbosity_level++;
+			break;
+
+		case 'C':
+			g->num_loops = atoi(optarg);
+			if (g->num_loops == 0) {
+				verbose_print(g->verbosity_level, LV_ERROR_MSG,
+				              "Invalid -C option '%s'\n",
+				              optarg);
+				exit(EXIT_FAILURE);
+			}
+			break;
+
+		default:
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "Unrecognized option %c\n", optopt);
+			usage(stderr);
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (g->ifname == NULL) {
+		verbose_print(g->verbosity_level, LV_ERROR_MSG,
+		              "Missing ifname\n");
+		usage(stderr);
+		exit(EXIT_FAILURE);
+	}
+
+	if (g->request_from_fd_server == 1 && g->extra_buffers_num > 0) {
+		verbose_print(
+		        g->verbosity_level, LV_ERROR_MSG,
+		        "Extra buffers can only be used when requesting an "
+		        "interface directly\n");
+		exit(EXIT_FAILURE);
+	}
+
+	if (g->request_from_fd_server == 0) {
+		/* We directly open the file descriptor. */
+		if (g->extra_buffers_num > 0) {
+			struct nmreq req;
+
+			memset(&req, 0, sizeof(req));
+			req.nr_arg3 = g->extra_buffers_num;
+			g->nmd      = nm_open(g->ifname, &req, 0, NULL);
+		} else {
+			g->nmd = nm_open(g->ifname, NULL, 0, NULL);
+		}
+	} else {
+		g->nmd = get_if_fd(g, g->ifname);
+	}
+	if (g->nmd == NULL) {
+		verbose_print(g->verbosity_level, LV_ERROR_MSG,
+		              "Failed to nm_open(%s)\n", g->ifname);
+		exit(EXIT_FAILURE);
+	}
+
+	if (g->extra_buffers_num > 0) {
+		/* Stores the real number of extra buffers. */
+		g->extra_buffers_num = g->nmd->req.nr_arg3;
+		verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "Received %u extra buffers\n", g->extra_buffers_num);
+		ret = parse_extra_buffers_indexes(g);
+		if (ret == -1) {
+			cleanup(g);
+			exit(EXIT_FAILURE);
+		}
+
+		swap_in_extra_buffers(g);
+	}
+
+	if (g->wait_link_secs > 0) {
+		sleep(g->wait_link_secs);
+	}
+
+	for (c = 0; c < g->num_loops; c++) {
+		for (i = 0; i < g->num_events; i++) {
+			const struct Event *e = g->events + i;
+
+			if (e->evtype == EVENT_TYPE_TX ||
+			    e->evtype == EVENT_TYPE_RX) {
+				g->filler   = e->filler;
+				g->pktm_len = e->pkt_len;
+				build_packet(g);
+			}
+
+			switch (e->evtype) {
+			case EVENT_TYPE_TX:
+				if (tx(g, e->num)) {
+					cleanup(g);
+					exit(EXIT_FAILURE);
+				}
+				break;
+
+			case EVENT_TYPE_RX:
+				if (rx(g, e->num)) {
+					cleanup(g);
+					exit(EXIT_FAILURE);
+				}
+				break;
+
+			case EVENT_TYPE_PAUSE:
+				usleep(e->usecs);
+				break;
+			}
+		}
+	}
+
+	/* if we have sent something, wait for all tx to complete */
+	tx_flush(g);
+	cleanup(g);
+	return 0;
+}
diff --git a/utils/functional.c b/utils/functional.c
new file mode 100644
index 000000000..ed20fd920
--- /dev/null
+++ b/utils/functional.c
@@ -0,0 +1,1432 @@
+/*
+ * A tool for functional testing netmap transmission and reception.
+ *
+ * Copyright (C) 2018 Vincenzo Maffione. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#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 
+#include 
+#include 
+
+#include "fd_server.h"
+
+#define ETH_ADDR_LEN 6
+
+struct Event {
+	unsigned evtype;
+#define EVENT_TYPE_RX 0x1
+#define EVENT_TYPE_TX 0x2
+#define EVENT_TYPE_PAUSE 0x3
+	unsigned num; /* > 1 if repeated event */
+
+	/* Tx and Rx event. */
+	unsigned pkt_len;
+	char filler;
+
+	/* Pause event. */
+	unsigned long long usecs;
+};
+
+struct extra_buffer {
+	uint32_t buf_idx;
+	TAILQ_ENTRY(extra_buffer) list_entry;
+};
+
+struct Global {
+	struct nmport_d *nmd;
+	const char *ifname;
+	unsigned wait_link_secs;    /* wait for link */
+	unsigned timeout_secs;      /* transmit/receive timeout */
+	int ignore_if_not_matching; /* ignore certain received packets */
+	int success_if_no_receive;  /* exit status 0 if we receive no packets */
+	int sequential_fill;        /* increment fill char for multi-packets
+	                            operations */
+	int request_from_fd_server; /* false --> directly open the interface */
+
+#define LV_ERROR_MSG 1
+#define LV_DEBUG_SEND_RECV 2
+#define LV_DEBUG_EXTRA_BUF 3
+#define LV_DEBUG_BUILD_PACKET 4
+#define LV_DEBUG_PARSE_ARGS 5
+	int verbosity_level;
+
+	/* List of currently not in use normal buffers. */
+	TAILQ_HEAD(extra_buf_head, extra_buffer) extra_buffers_head;
+	unsigned extra_buffers_num; /* number of granted extra buffers */
+
+#define MAX_PKT_SIZE 65536
+	char pktm[MAX_PKT_SIZE]; /* packet model */
+	unsigned pktm_len;       /* packet model length */
+	char pktr[MAX_PKT_SIZE]; /* packet received */
+	unsigned pktr_len;       /* length of received packet */
+	unsigned max_frag_size;  /* max bytes per netmap TX slot */
+
+	char src_mac[ETH_ADDR_LEN];
+	char dst_mac[ETH_ADDR_LEN];
+	uint32_t src_ip;
+	uint32_t dst_ip;
+	uint16_t src_port;
+	uint16_t dst_port;
+	char filler;
+
+#define MAX_EVENTS 64
+	unsigned num_events;
+	struct Event events[MAX_EVENTS];
+	unsigned num_loops;
+};
+
+void release_if_fd(struct Global *, const char *);
+void release_extra_buffers(struct Global *);
+
+void
+verbose_print(int current_verbosity, int required_verbosity, const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	if (current_verbosity >= required_verbosity) {
+		vprintf(format, args);
+	}
+
+	va_end(args);
+}
+
+void
+verbose_perror(int current_verbosity, int required_verbosity, const char *str)
+{
+	if (current_verbosity >= required_verbosity) {
+		perror(str);
+	}
+}
+
+void
+cleanup(struct Global *g)
+{
+	if (g->extra_buffers_num > 0) {
+		release_extra_buffers(g);
+	}
+
+	if (g->request_from_fd_server) {
+		release_if_fd(g, g->ifname);
+	} else {
+		nmport_close(g->nmd);
+	}
+}
+
+static void
+fill_packet_field(struct Global *g, unsigned offset, const char *content,
+                  unsigned content_len)
+{
+	if (offset + content_len > sizeof(g->pktm)) {
+		verbose_print(g->verbosity_level, LV_ERROR_MSG,
+		              "Packet layout overflow: %u + %u > %lu\n", offset,
+		              content_len, sizeof(g->pktm));
+		cleanup(g);
+		exit(EXIT_FAILURE);
+	}
+
+	memcpy(g->pktm + offset, content, content_len);
+}
+
+static void
+fill_packet_8bit(struct Global *g, unsigned offset, uint8_t val)
+{
+	fill_packet_field(g, offset, (const char *)&val, sizeof(val));
+}
+
+static void
+fill_packet_16bit(struct Global *g, unsigned offset, uint16_t val)
+{
+	val = htons(val);
+	fill_packet_field(g, offset, (const char *)&val, sizeof(val));
+}
+
+static void
+fill_packet_32bit(struct Global *g, unsigned offset, uint32_t val)
+{
+	val = htonl(val);
+	fill_packet_field(g, offset, (const char *)&val, sizeof(val));
+}
+
+/* Compute the checksum of the given ip header. */
+static uint32_t
+checksum(const void *data, uint16_t len, uint32_t sum /* host endianness */)
+{
+	const uint8_t *addr = data;
+	uint32_t i;
+
+	/* Checksum all the pairs of bytes first... */
+	for (i = 0; i < (len & ~1U); i += 2) {
+		sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i)));
+		if (sum > 0xFFFF) {
+			sum -= 0xFFFF;
+		}
+	}
+	/*
+	 * If there's a single byte left over, checksum it, too.
+	 * Network byte order is big-endian, so the remaining byte is
+	 * the high byte.
+	 */
+	if (i < len) {
+		sum += addr[i] << 8;
+		if (sum > 0xFFFF) {
+			sum -= 0xFFFF;
+		}
+	}
+	return sum;
+}
+
+static uint16_t
+wrapsum(uint32_t sum /* host endianness */)
+{
+	sum = ~sum & 0xFFFF;
+	return sum; /* host endianness */
+}
+
+static void
+build_packet(struct Global *g)
+{
+	unsigned ofs = 0;
+	unsigned ethofs;
+	unsigned ipofs;
+	unsigned udpofs;
+	unsigned pldofs;
+
+	memset(g->pktm, 0, sizeof(g->pktm));
+	verbose_print(g->verbosity_level, LV_DEBUG_BUILD_PACKET,
+	              "%s: starting at ofs %u\n", __func__, ofs);
+
+	ethofs = ofs;
+	(void)ethofs;
+	/* Ethernet destination and source MAC address plus ethertype. */
+	fill_packet_field(g, ofs, g->dst_mac, ETH_ADDR_LEN);
+	ofs += ETH_ADDR_LEN;
+	fill_packet_field(g, ofs, g->src_mac, ETH_ADDR_LEN);
+	ofs += ETH_ADDR_LEN;
+	fill_packet_16bit(g, ofs, ETHERTYPE_IP);
+	ofs += 2;
+	verbose_print(g->verbosity_level, LV_DEBUG_BUILD_PACKET,
+	              "%s: eth done, ofs %u\n", __func__, ofs);
+
+	ipofs = ofs;
+	/* First byte of IP header. */
+	fill_packet_8bit(g, ofs, (IPVERSION << 4) | ((sizeof(struct ip)) >> 2));
+	ofs += 1;
+	/* Skip QoS byte. */
+	ofs += 1;
+	/* Total length. */
+	fill_packet_16bit(g, ofs, g->pktm_len - ipofs);
+	ofs += 2;
+	/* Skip identification field. */
+	ofs += 2;
+	/* Offset (and flags) field. */
+	fill_packet_16bit(g, ofs, IP_DF);
+	ofs += 2;
+	/* TTL. */
+	fill_packet_8bit(g, ofs, IPDEFTTL);
+	ofs += 1;
+	/* Protocol. */
+	fill_packet_8bit(g, ofs, IPPROTO_UDP);
+	ofs += 1;
+	/* Skip checksum for now. */
+	ofs += 2;
+	/* Source IP address. */
+	fill_packet_32bit(g, ofs, g->src_ip);
+	ofs += 4;
+	/* Dst IP address. */
+	fill_packet_32bit(g, ofs, g->dst_ip);
+	ofs += 4;
+	/* Now put the checksum. */
+	fill_packet_16bit(
+	        g, ipofs + 10,
+	        wrapsum(checksum(g->pktm + ipofs, sizeof(struct ip), 0)));
+	verbose_print(g->verbosity_level, LV_DEBUG_BUILD_PACKET,
+	              "%s: ip done, ofs %u\n", __func__, ofs);
+
+	udpofs = ofs;
+	/* UDP source port. */
+	fill_packet_16bit(g, ofs, g->src_port);
+	ofs += 2;
+	/* UDP source port. */
+	fill_packet_16bit(g, ofs, g->dst_port);
+	ofs += 2;
+	/* UDP length (UDP header + data). */
+	fill_packet_16bit(g, ofs, g->pktm_len - udpofs);
+	ofs += 2;
+	/* Skip the UDP checksum for now. */
+	ofs += 2;
+	verbose_print(g->verbosity_level, LV_DEBUG_BUILD_PACKET,
+	              "%s: udp done, ofs %u\n", __func__, ofs);
+
+	/* Fill UDP payload. */
+	pldofs = ofs;
+	for (; ofs < g->pktm_len; ofs++) {
+		fill_packet_8bit(g, ofs, g->filler);
+	}
+	verbose_print(g->verbosity_level, LV_DEBUG_BUILD_PACKET,
+	              "%s: payload done, ofs %u\n", __func__, ofs);
+
+	/* Put the UDP checksum now.
+	 * Magic: taken from sbin/dhclient/packet.c */
+	fill_packet_16bit(
+	        g, udpofs + 6,
+	        wrapsum(checksum(
+	                /* udp header */ g->pktm + udpofs,
+	                sizeof(struct udphdr),
+	                checksum(/* udp payload */ g->pktm + pldofs,
+	                         g->pktm_len - pldofs,
+	                         checksum(/* pseudo header */ g->pktm + ipofs +
+	                                          12,
+	                                  2 * sizeof(g->src_ip),
+	                                  IPPROTO_UDP + (uint32_t)(g->pktm_len -
+	                                                           udpofs))))));
+}
+
+static int
+tx_flush(struct Global *g)
+{
+	struct nmport_d *nmd = g->nmd;
+	unsigned elapsed_ms = 0;
+	unsigned wait_ms    = 100;
+	int i;
+
+	for (;;) {
+		int pending = 0;
+		for (i = nmd->first_tx_ring; i <= nmd->last_tx_ring; i++) {
+			struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, i);
+
+			pending += nm_tx_pending(ring);
+		}
+
+		if (!pending) {
+			return 0;
+		}
+
+		if (elapsed_ms > g->timeout_secs * 1000) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "%s: Timeout\n", __func__);
+			return -1;
+		}
+
+		usleep(wait_ms * 1000);
+		elapsed_ms += wait_ms;
+
+		ioctl(nmd->fd, NIOCTXSYNC, NULL);
+	}
+}
+
+uint64_t
+ring_avail_packets(struct netmap_ring *ring, unsigned pkt_len)
+{
+	uint64_t slot_per_packet;
+
+	slot_per_packet = ceil((double)pkt_len / (double)ring->nr_buf_size);
+	return nm_ring_space(ring) / slot_per_packet;
+}
+
+uint64_t
+adapter_avail_sends(struct nmport_d *nmd, unsigned pkt_len)
+{
+	uint64_t sends_available = 0;
+	unsigned int i;
+
+	for (i = nmd->first_tx_ring; i <= nmd->last_tx_ring; i++) {
+		struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, i);
+
+		sends_available += ring_avail_packets(ring, pkt_len);
+	}
+
+	return sends_available;
+}
+
+void
+put_one_packet(struct Global *g, struct netmap_ring *ring)
+{
+	unsigned head  = ring->head;
+	unsigned frags = 0;
+	unsigned ofs   = 0;
+
+	for (;;) {
+		struct netmap_slot *slot = &ring->slot[head];
+		char *buf                = NETMAP_BUF(ring, slot->buf_idx);
+		unsigned copysize        = g->pktm_len - ofs;
+
+		if (copysize > ring->nr_buf_size) {
+			copysize = ring->nr_buf_size;
+		}
+		if (copysize > g->max_frag_size) {
+			copysize = g->max_frag_size;
+		}
+
+		memcpy(buf, g->pktm + ofs, copysize);
+		ofs += copysize;
+		slot->len   = copysize;
+		slot->flags = NS_MOREFRAG;
+		head        = nm_ring_next(ring, head);
+		frags++;
+		if (ofs >= g->pktm_len) {
+			/* Last fragment. */
+			assert(ofs == g->pktm_len);
+			slot->flags = NS_REPORT;
+			break;
+		}
+	}
+
+	ring->head = ring->cur = head;
+	verbose_print(g->verbosity_level, LV_DEBUG_SEND_RECV,
+	              "packet (%u bytes, %u frags) placed to TX\n", g->pktm_len,
+	              frags);
+}
+
+/* Used for multi-packets sequential send/receive actions */
+char
+next_fill(char cur_fill)
+{
+	if (cur_fill == 'z')
+		return 'a';
+	if (cur_fill == 'Z')
+		return 'A';
+	return ++cur_fill;
+}
+
+/* Transmit packets_num packets using any combination of TX rings. */
+static int
+tx(struct Global *g, unsigned packets_num)
+{
+	struct nmport_d *nmd = g->nmd;
+	unsigned elapsed_ms = 0;
+	unsigned wait_ms    = 100;
+	unsigned int i;
+
+	/* We cycle here until either we timeout or we find enough space. */
+	for (;;) {
+		if (adapter_avail_sends(nmd, g->pktm_len) >= packets_num) {
+			break;
+		}
+
+		if (elapsed_ms > g->timeout_secs * 1000) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "%s: Timeout\n", __func__);
+			return -1;
+		}
+
+		/* Retry after a short while. */
+		usleep(wait_ms * 1000);
+		elapsed_ms += wait_ms;
+		ioctl(nmd->fd, NIOCTXSYNC, NULL);
+	}
+
+	/* Once we have enough space, we start filling slots. We might use
+	 * multiple rings.
+	 */
+	for (i = nmd->first_tx_ring; i <= nmd->last_tx_ring; i++) {
+		struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, i);
+		uint64_t ring_sends_num;
+
+		for (ring_sends_num = ring_avail_packets(ring, g->pktm_len);
+		     ring_sends_num > 0 && packets_num > 0;
+		     --ring_sends_num, --packets_num) {
+			put_one_packet(g, ring);
+
+			if (g->sequential_fill == 1) {
+				g->filler = next_fill(g->filler);
+				build_packet(g);
+			}
+		}
+
+		if (packets_num == 0) {
+			break;
+		}
+	}
+
+	assert(packets_num == 0);
+	/* Once we're done we sync, sending all packets at once. */
+	ioctl(nmd->fd, NIOCTXSYNC, NULL);
+	return 0;
+}
+
+/* If -I option is specified, we want to ignore frames that don't match
+ * our expected ethernet header.
+ * This function currently assumes that Ethernet header starts from
+ * the beginning of the packet buffers. */
+static int
+ignore_received_frame(struct Global *g)
+{
+	if (!g->ignore_if_not_matching) {
+		return 0; /* don't ignore */
+	}
+
+	if (g->pktr_len < 14 || memcmp(g->pktm, g->pktr, 14) != 0) {
+		return 1; /* ignore */
+	}
+
+	return 0; /* don't ignore */
+}
+
+uint64_t
+adapter_avail_receives(struct nmport_d *nmd, unsigned pkt_len)
+{
+	uint64_t receives_available = 0;
+	unsigned int i;
+
+	for (i = nmd->first_rx_ring; i <= nmd->last_rx_ring; i++) {
+		struct netmap_ring *ring = NETMAP_RXRING(nmd->nifp, i);
+
+		receives_available += ring_avail_packets(ring, pkt_len);
+	}
+
+	return receives_available;
+}
+
+static int
+rx_check(struct Global *g)
+{
+	unsigned i;
+
+	if (g->pktr_len != g->pktm_len) {
+		verbose_print(g->verbosity_level, LV_ERROR_MSG,
+		              "Received packet length (%u) different from "
+		              "expected (%u bytes)\n",
+		              g->pktr_len, g->pktm_len);
+		return -1;
+	}
+
+	for (i = 0; i < g->pktr_len; i++) {
+		if (g->pktr[i] != g->pktm[i]) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "Received packet differs from model at "
+			              "offset %u (0x%02x!=0x%02x)\n",
+			              i, g->pktr[i], (uint8_t)g->pktm[i]);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+int
+read_one_packet(struct Global *g, struct netmap_ring *ring)
+{
+	unsigned head = ring->head;
+	int frags     = 0;
+
+	g->pktr_len = 0;
+	for (;;) {
+		struct netmap_slot *slot = &ring->slot[head];
+		char *buf                = NETMAP_BUF(ring, slot->buf_idx);
+
+		if (g->pktr_len + slot->len > sizeof(g->pktr)) {
+			/* Sanity check. */
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "Error: received packet too "
+			              "large "
+			              "(>= %u bytes) ",
+			              g->pktr_len + slot->len);
+			cleanup(g);
+			exit(EXIT_FAILURE);
+		}
+
+		memcpy(g->pktr + g->pktr_len, buf, slot->len);
+		g->pktr_len += slot->len;
+		head = nm_ring_next(ring, head);
+		frags++;
+		if (!(slot->flags & NS_MOREFRAG)) {
+			break;
+		}
+
+		if (head == ring->tail) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "warning: truncated packet "
+			              "(len=%u)\n",
+			              g->pktr_len);
+			frags = -1;
+			break;
+		}
+	}
+
+	ring->head = ring->cur = head;
+	verbose_print(g->verbosity_level, LV_DEBUG_SEND_RECV,
+	              "packet (%u bytes, %d frags) received "
+	              "from RX\n",
+	              g->pktr_len, frags);
+	return frags;
+}
+
+/* Receive packets_num packets from any combination of RX rings. */
+static int
+rx(struct Global *g, unsigned packets_num)
+{
+	struct nmport_d *nmd = g->nmd;
+	unsigned elapsed_ms = 0;
+	unsigned wait_ms    = 100;
+	unsigned int i;
+
+	/* We cycle here until either we timeout or we find enough space. */
+	for (;;) {
+	again:
+		if (adapter_avail_receives(nmd, g->pktm_len) >= packets_num) {
+			break;
+		}
+
+		if (elapsed_ms > g->timeout_secs * 1000) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "%s: Timeout\n", __func__);
+			/* -n flag */
+			return g->success_if_no_receive == 1 ? 0 : -1;
+		}
+
+		/* Retry after a short while. */
+		usleep(wait_ms * 1000);
+		elapsed_ms += wait_ms;
+		ioctl(nmd->fd, NIOCRXSYNC, NULL);
+	}
+
+	/* Once we have enough space, we start reading packets. We might use
+	 * multiple rings.
+	 */
+	for (i = nmd->first_rx_ring; i <= nmd->last_rx_ring; i++) {
+		struct netmap_ring *ring = NETMAP_RXRING(nmd->nifp, i);
+		uint64_t ring_receives_num;
+
+		for (ring_receives_num = ring_avail_packets(ring, g->pktm_len);
+		     ring_receives_num > 0 && packets_num > 0;
+		     --ring_receives_num, --packets_num) {
+			int frags = 0;
+
+			frags = read_one_packet(g, ring);
+			if (frags == -1) {
+				break; /* Truncated packet, skip this ring. */
+			}
+
+			if (ignore_received_frame(g)) {
+				verbose_print(g->verbosity_level,
+				              LV_DEBUG_SEND_RECV,
+				              "(ignoring packet with %u bytes "
+				              "and "
+				              "%d frags received from RX ring "
+				              "#%d)\n",
+				              g->pktr_len, frags, i);
+				elapsed_ms = 0;
+				/* We can go back there, because we're
+				 * decrementing packets_num each time, therefore
+				 * the we will wait only for the remaining
+				 * packets.
+				 */
+				goto again;
+			}
+
+			/* As soon as we find a packet which doesn't match our
+			 * packet model we exit with status EXIT_FAILURE.
+			 */
+			if (rx_check(g)) {
+				cleanup(g);
+				exit(EXIT_FAILURE);
+			}
+
+			if (g->sequential_fill == 1) {
+				g->filler = next_fill(g->filler);
+				build_packet(g);
+			}
+		}
+
+		if (packets_num == 0) {
+			break;
+		}
+	}
+
+	assert(packets_num == 0);
+	/* Once we're done we sync, freeing all slots at once. */
+	ioctl(nmd->fd, NIOCRXSYNC, NULL);
+	return 0;
+}
+
+static int
+parse_txrx_event(const char *opt, unsigned event_type, struct Event *event,
+                 int verbosity_level)
+{
+	char *strbuf = strdup(opt);
+	char *save   = strbuf;
+	int more;
+	char *c;
+	int ret = -1;
+
+	if (!strbuf || strlen(strbuf) == 0) {
+		goto out;
+	}
+
+	event->evtype = event_type;
+	event->filler = 'a';
+	event->num    = 1;
+
+	for (c = strbuf; *c != '\0' && *c != ':'; c++) {
+	}
+	more           = (*c == ':');
+	*c             = '\0';
+	event->pkt_len = atoi(strbuf);
+	if (event->pkt_len == 0) {
+		goto out;
+	}
+	if (more) {
+		strbuf = c + 1;
+		for (c = strbuf; *c != '\0' && *c != ':'; c++) {
+		}
+		more          = (*c == ':');
+		*c            = '\0';
+		event->filler = strbuf[0];
+	}
+	if (more) {
+		strbuf = c + 1;
+		for (c = strbuf; *c != '\0'; c++) {
+		}
+		event->num = atoi(strbuf);
+		if (event->num == 0) {
+			goto out;
+		}
+	}
+
+	ret = 0;
+	verbose_print(verbosity_level, LV_DEBUG_PARSE_ARGS, "parsed %u:%c:%u\n",
+	              event->pkt_len, event->filler, event->num);
+out:
+	if (save) {
+		free(save);
+	}
+	return ret;
+}
+
+static int
+parse_pause_event(const char *opt, struct Event *event, int verbosity_level)
+{
+	char *strbuf = strdup(opt);
+	char *save   = strbuf;
+	unsigned mul = 1000000;
+	int ret      = -1;
+
+	while (*strbuf != '\0' && isdigit(*strbuf)) {
+		strbuf++;
+	}
+	if (!strcmp(strbuf, "us")) {
+		mul = 1;
+	} else if (!strcmp(strbuf, "ms")) {
+		mul = 1000;
+	} else if (strcmp(strbuf, "s") && strcmp(strbuf, "")) {
+		goto out;
+	}
+
+	event->evtype = EVENT_TYPE_PAUSE;
+	event->usecs  = atoi(save);
+	if (event->usecs == 0) {
+		goto out;
+	}
+
+	event->usecs *= mul;
+	event->num = 1;
+	ret        = 0;
+	verbose_print(verbosity_level, LV_DEBUG_PARSE_ARGS,
+	              "parsed %llu usecs\n", event->usecs);
+out:
+	free(save);
+	return ret;
+}
+
+static struct Global _g;
+
+static void
+usage(FILE *stream)
+{
+	fprintf(stream,
+	        "usage: ./functional {-c | -o | -i | -I}\n"
+	        "Required:\n"
+	        "    -c (shuts down the fd server),\n"
+	        "    -o (starts the fd server),\n"
+	        "    -i NETMAP_PORT (requests the interface from the fd "
+	        "server),\n"
+	        "    -I NETMAP_PORT (directly opens the interface)\n"
+	        "Optional:\n"
+	        "    [-s SOURCE MAC ADDRESS (=0:0:0:0:0:0)]\n"
+	        "    [-d DESTINATION MAC ADDRESS (=FF:FF:FF:FF:FF:FF)]\n"
+	        "    [-F MAX_FRAGMENT_SIZE (=inf)]\n"
+	        "    [-T TIMEOUT_SECS (=1)]\n"
+	        "    [-w WAIT_FOR_LINK_SECS (=0)]\n"
+	        "    [-t LEN[:FILLCHAR[:NUM]] (transmit NUM packets with size "
+	        "LEN bytes)]\n"
+	        "    [-r LEN[:FILLCHAR[:NUM]] (expect to receive NUM packets "
+	        "with size LEN bytes)]\n"
+	        "    [-p NUM[us|ms|s]] (pause for NUM us/ms/s)]\n"
+	        "    [-g (ignore ethernet frames with unmatching Ethernet "
+	        "header)]\n"
+	        "    [-n (exit status = 0 <==> no frames were received)]\n"
+	        "    [-q (during multi-packets send/receive increments fill "
+	        "character after each operation)]\n"
+	        "    [-e NUM (use NUM extra buffers to send packets, "
+	        "can only be used when with -I)]\n"
+	        "    [-v (increment verbosity level)]\n"
+	        "    [-C [NUM (=1)] (how many times to run the events)]\n"
+	        "\nExample:\n"
+	        "    $ ./functional -i netmap:lo -t 100 -r 100 -t 40:b:2 -r "
+	        "40:b:2\n");
+}
+
+int
+connect_to_fd_server(struct Global *g)
+{
+	struct sockaddr_un name;
+	unsigned elapsed_ms = 0;
+	unsigned wait_ms    = 100;
+	int socket_fd;
+
+	socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (socket_fd == -1) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "socket()");
+		return -1;
+	}
+
+	memset(&name, 0, sizeof(name));
+	name.sun_family = AF_UNIX;
+	strncpy(name.sun_path, SOCKET_NAME, sizeof(name.sun_path) - 1);
+	name.sun_path[sizeof(name.sun_path) - 1] = '\0';
+	while (connect(socket_fd, (const struct sockaddr *)&name,
+	               sizeof(struct sockaddr_un)) == -1) {
+		if (elapsed_ms > g->timeout_secs * 1000) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "%s: Timeout\n", __func__);
+			return -1;
+		}
+
+		usleep(wait_ms * 1000);
+		elapsed_ms += wait_ms;
+	}
+
+	return socket_fd;
+}
+
+void
+start_fd_server(struct Global *g)
+{
+	int socket_fd;
+	pid_t pid;
+
+	pid = fork();
+	if (pid < 0) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "fork()");
+		exit(EXIT_FAILURE);
+	}
+	if (pid > 0) {
+		wait(NULL);
+		return;
+	}
+
+	if (execlp("fd_server", "fd_server", (char *)NULL)) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "exec()");
+		exit(EXIT_FAILURE);
+	}
+
+	socket_fd = connect_to_fd_server(g);
+	if (socket_fd == -1) {
+		verbose_print(g->verbosity_level, LV_ERROR_MSG,
+		              "Can't connect to fd_server\n");
+		exit(EXIT_FAILURE);
+	}
+	close(socket_fd);
+}
+
+int
+recv_fd(int socket, int *fd, void *buf, size_t buf_size)
+{
+	union {
+		char buf[CMSG_SPACE(sizeof(int))];
+		struct cmsghdr align;
+	} ancillary;
+	struct fd_response *res;
+	struct cmsghdr *cmsg;
+	struct iovec iov[1];
+	struct msghdr msg;
+	int amount;
+
+	errno           = 0;
+	iov[0].iov_base = buf;
+	iov[0].iov_len  = buf_size;
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov    = iov;
+	msg.msg_iovlen = 1;
+	memset(ancillary.buf, 0, sizeof(ancillary.buf));
+	msg.msg_control    = ancillary.buf;
+	msg.msg_controllen = sizeof(ancillary.buf);
+	cmsg               = CMSG_FIRSTHDR(&msg);
+	cmsg->cmsg_level   = SOL_SOCKET;
+	cmsg->cmsg_type    = SCM_RIGHTS;
+	cmsg->cmsg_len     = CMSG_LEN(sizeof(int));
+	amount             = recvmsg(socket, &msg, 0);
+	if (amount == -1) {
+		printf("recv_fd(): recvmsg failed\n");
+		return -1;
+	}
+
+	res = iov[0].iov_base;
+	if (res->result != 0) {
+		errno = res->result;
+		return -1;
+	}
+
+	/* If res->result == 0, we know for sure that a file descriptor has been
+	 * sent through the ancillary data.
+	 */
+	cmsg = CMSG_FIRSTHDR(&msg);
+	memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+
+	return amount;
+}
+
+struct nmport_d *
+get_if_fd(struct Global *g, const char *if_name)
+{
+	struct fd_response res;
+	struct fd_request req;
+	struct nmport_d *nmd;
+	int socket_fd;
+	int new_fd;
+	int ret;
+
+	socket_fd = connect_to_fd_server(g);
+	if (socket_fd == -1) {
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.action = FD_GET;
+	strncpy(req.if_name, if_name, sizeof(req.if_name)-1);
+	ret = send(socket_fd, &req, sizeof(req), 0);
+	if (ret < 0) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "send()");
+		return NULL;
+	}
+
+	memset(&res, 0, sizeof(res));
+	ret = recv_fd(socket_fd, &new_fd, &res, sizeof(res));
+	if (ret == -1) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "recv_fd()");
+		return NULL;
+	}
+	close(socket_fd);
+
+	nmd = nmport_new();
+	if (nmd == NULL) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "malloc()");
+		return NULL;
+	}
+
+	// unmarshal the response
+	nmd->hdr = res.hdr;
+	nmd->reg = res.reg;
+	nmd->hdr.nr_body = (uintptr_t)&nmd->reg;
+	nmd->fd = new_fd;
+	nmd->register_done = 1;
+
+	if (nmport_mmap(nmd) != 0) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "nm_mmap()");
+		return NULL;
+	}
+
+	return nmd;
+}
+
+void
+release_if_fd(struct Global *g, const char *if_name)
+{
+	struct fd_request req;
+	int socket_fd;
+	int ret;
+
+	socket_fd = connect_to_fd_server(g);
+	if (socket_fd == -1) {
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.action = FD_RELEASE;
+	strncpy(req.if_name, if_name, sizeof(req.if_name)-1);
+
+	ret = send(socket_fd, &req, sizeof(req), 0);
+	if (ret <= 0) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "send()");
+	}
+
+	close(socket_fd);
+}
+
+void
+stop_fd_server(struct Global *g)
+{
+	struct fd_request req;
+	int socket_fd;
+	int ret;
+
+	socket_fd = connect_to_fd_server(g);
+	if (socket_fd == -1) {
+		verbose_print(g->verbosity_level, LV_DEBUG_SEND_RECV,
+		              "fd_server already down\n");
+		return;
+	}
+	verbose_print(g->verbosity_level, LV_DEBUG_SEND_RECV,
+	              "Shutting down fd_server\n");
+
+	memset(&req, 0, sizeof(req));
+	req.action = FD_STOP;
+	ret        = send(socket_fd, &req, sizeof(req), 0);
+	if (ret == -1) {
+		verbose_perror(g->verbosity_level, LV_ERROR_MSG, "send()");
+	}
+	/* By calling recv() we synchronize with the fd_server closing the
+	 * socket.
+	 * This way we're sure that during the next call to ./functional
+	 * the fd_server has already closed its end and we avoid a possible race
+	 * condition. Otherwise the call to functional might connect to the
+	 * previous fd_server backlog.
+	 */
+	recv(socket_fd, &req, sizeof(req), 0);
+	close(socket_fd);
+}
+
+int
+parse_mac_address(const char *opt, char *mac)
+{
+	if (6 == sscanf(opt, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mac[0], &mac[1],
+	                &mac[2], &mac[3], &mac[4], &mac[5])) {
+		return 0;
+	}
+	return -1;
+}
+
+/* Uses the first adapter slot (any will do) to save the extra buffers indexes.
+ */
+int
+parse_extra_buffers_indexes(struct Global *g)
+{
+	struct netmap_if *nifp   = g->nmd->nifp;
+	struct netmap_ring *ring = NETMAP_TXRING(nifp, g->nmd->first_tx_ring);
+	struct netmap_slot *slot = &ring->slot[ring->head];
+	uint32_t extra_buf_index = nifp->ni_bufs_head;
+	uint32_t real_index      = slot->buf_idx;
+	struct extra_buffer *u_buf;
+	unsigned i;
+
+	verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "Parsing %u extra buffers:\n", g->extra_buffers_num);
+	for (i = 0; i < g->extra_buffers_num; i++) {
+		if (extra_buf_index == 0) {
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "   error, index = 0\n");
+			return -1;
+		}
+		verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "   index = %u\n", extra_buf_index);
+
+		u_buf = malloc(sizeof(*u_buf));
+		if (u_buf == NULL) {
+			verbose_perror(g->verbosity_level, LV_ERROR_MSG,
+			               "malloc()");
+			return -1;
+		}
+		u_buf->buf_idx = extra_buf_index;
+		TAILQ_INSERT_HEAD(&g->extra_buffers_head, u_buf, list_entry);
+		slot->buf_idx   = extra_buf_index;
+		extra_buf_index = *(uint32_t *)NETMAP_BUF(ring, slot->buf_idx);
+	}
+	slot->buf_idx = real_index;
+
+	return 0;
+}
+
+/* Loops through the adapter slots, swapping the default buffers with the
+ * extra buffers. Keeps going until we run out of extra buffers, or adapter
+ * slots.
+ */
+int
+swap_in_extra_buffers(struct Global *g)
+{
+	unsigned extra_buffers_num = g->extra_buffers_num;
+	unsigned int i;
+
+	for (i = g->nmd->first_tx_ring; i <= g->nmd->last_tx_ring; i++) {
+		struct netmap_ring *ring = NETMAP_TXRING(g->nmd->nifp, i);
+		unsigned head;
+
+		for (head = ring->head; head != ring->tail;
+		     head = nm_ring_next(ring, head)) {
+			struct extra_buffer *u_buf =
+			        TAILQ_FIRST(&g->extra_buffers_head);
+			struct netmap_slot *slot = &ring->slot[head];
+			uint32_t real_index      = slot->buf_idx;
+
+			if (u_buf == NULL) {
+				/* We finished swapping in extra buffers */
+				return 0;
+			}
+
+			slot->buf_idx = u_buf->buf_idx;
+			slot->flags |= NS_BUF_CHANGED;
+			u_buf->buf_idx = real_index;
+			TAILQ_REMOVE(&g->extra_buffers_head, u_buf, list_entry);
+			TAILQ_INSERT_TAIL(&g->extra_buffers_head, u_buf,
+			                 list_entry);
+
+			if (--extra_buffers_num == 0) {
+				return 0;
+			}
+		}
+	}
+
+	/* This is reached if the adapter has less slots than the number of
+	 * requested extra buffers. Nevertheless this is not a problem as the
+	 * not in use extra buffers will will be released during cleanup().
+	 */
+	return 0;
+}
+
+/* We only re-build the extra buffers list, as requested from netmap. We don't
+ * undo the swapping that we did at the start of the program to swap in the
+ * extra buffer. This probably leaves the netmap adapter in an inconsistent
+ * state, that's why we only support this option for interfaces requested
+ * directly.
+ */
+void
+release_extra_buffers(struct Global *g)
+{
+	struct netmap_if *nifp   = g->nmd->nifp;
+	struct netmap_ring *ring = NETMAP_TXRING(nifp, g->nmd->first_tx_ring);
+	struct netmap_slot *slot = &ring->slot[ring->head];
+	uint32_t real_index      = slot->buf_idx;
+	struct extra_buffer *u_buf;
+	uint32_t *next_extra_buffer;
+
+	verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "Releasing %u extra buffers:\n", g->extra_buffers_num);
+	if (TAILQ_EMPTY(&g->extra_buffers_head)) {
+		return;
+	}
+
+	u_buf = TAILQ_FIRST(&g->extra_buffers_head);
+	nifp->ni_bufs_head = u_buf->buf_idx;
+	verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "   head index %u\n", nifp->ni_bufs_head);
+	slot->buf_idx = u_buf->buf_idx;
+	TAILQ_REMOVE(&g->extra_buffers_head, u_buf, list_entry);
+	free(u_buf);
+
+	while (!TAILQ_EMPTY(&g->extra_buffers_head)) {
+		next_extra_buffer = (uint32_t *)NETMAP_BUF(ring, slot->buf_idx);
+		u_buf = TAILQ_FIRST(&g->extra_buffers_head);
+		verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "   index = %u\n", u_buf->buf_idx);
+		*next_extra_buffer = u_buf->buf_idx;
+		slot->buf_idx = u_buf->buf_idx;
+		TAILQ_REMOVE(&g->extra_buffers_head, u_buf, list_entry);
+		free(u_buf);
+	}
+	next_extra_buffer = (uint32_t *)NETMAP_BUF(ring, slot->buf_idx);
+	*next_extra_buffer = 0;
+	slot->buf_idx = real_index;
+}
+
+int
+main(int argc, char **argv)
+{
+	struct Global *g = &_g;
+	unsigned int i, c;
+	int opt;
+	int ret;
+
+	g->nmd            = NULL;
+	g->ifname         = NULL;
+	g->wait_link_secs = 0;
+	g->timeout_secs   = 1;
+	g->pktm_len       = 60;
+	g->max_frag_size  = ~0U; /* unlimited */
+	for (i = 0; i < ETH_ADDR_LEN; i++) {
+		g->src_mac[i] = 0x00;
+	}
+	for (i = 0; i < ETH_ADDR_LEN; i++) {
+		g->dst_mac[i] = 0xFF;
+	}
+	g->src_ip                 = 0x0A000005; /* 10.0.0.5 */
+	g->dst_ip                 = 0x0A000007; /* 10.0.0.7 */
+	g->filler                 = 'a';
+	g->num_events             = 0;
+	g->ignore_if_not_matching = /*false=*/0;
+	g->success_if_no_receive  = /*false=*/0;
+	g->request_from_fd_server = /*true=*/1;
+	g->sequential_fill        = /*false=*/0;
+	g->extra_buffers_num      = 0;
+	g->verbosity_level        = 0;
+	g->num_loops              = 1;
+	g->extra_buffers_num      = 0;
+	TAILQ_INIT(&g->extra_buffers_head);
+
+	while ((opt = getopt(argc, argv, "hconqe:s:d:i:I:w:F:T:t:r:gvp:C:")) !=
+	       -1) {
+		switch (opt) {
+		case 'h':
+			usage(stdout);
+			return 0;
+
+		/* TODO: move this option to fd_server */
+		case 'c':
+			stop_fd_server(g);
+			return 0;
+
+		/* TODO: move this option to fd_server */
+		case 'o':
+			start_fd_server(g);
+			return 0;
+
+		case 'n':
+			g->success_if_no_receive = /*true=*/1;
+			break;
+
+		case 'q':
+			g->sequential_fill = /*true=*/1;
+			break;
+
+		case 'e':
+			g->extra_buffers_num = atoi(optarg);
+			if (g->extra_buffers_num <= 0) {
+				verbose_print(
+				        g->verbosity_level, LV_ERROR_MSG,
+				        "Invalid number of extra buffers\n");
+				exit(EXIT_FAILURE);
+			};
+			verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "Requesting %u extra buffers\n", g->extra_buffers_num);
+			break;
+
+		case 's':
+			ret = parse_mac_address(optarg, g->src_mac);
+			if (ret == -1) {
+				verbose_print(g->verbosity_level, LV_ERROR_MSG,
+				              "Invalid source MAC address\n");
+				exit(EXIT_FAILURE);
+			}
+			break;
+
+		case 'd':
+			ret = parse_mac_address(optarg, g->dst_mac);
+			if (ret == -1) {
+				verbose_print(
+				        g->verbosity_level, LV_ERROR_MSG,
+				        "Invalid destination MAC address\n");
+				exit(EXIT_FAILURE);
+			}
+			break;
+
+		case 'i':
+			g->ifname = optarg;
+			break;
+
+		case 'I':
+			g->ifname                 = optarg;
+			g->request_from_fd_server = /*false=*/0;
+			break;
+
+		case 'F':
+			g->max_frag_size = atoi(optarg);
+			break;
+
+		case 'w':
+			g->wait_link_secs = atoi(optarg);
+			break;
+
+		case 'T':
+			g->timeout_secs = atoi(optarg);
+			break;
+
+		case 't':
+		case 'r':
+		case 'p': {
+			int ret = 0;
+
+			if (g->num_events >= MAX_EVENTS) {
+				verbose_print(g->verbosity_level, LV_ERROR_MSG,
+				              "Too many events\n");
+				exit(EXIT_FAILURE);
+			}
+
+			if (opt == 'p') {
+				ret = parse_pause_event(
+				        optarg, g->events + g->num_events,
+				        g->verbosity_level);
+			} else {
+				ret = parse_txrx_event(
+				        optarg,
+				        (opt == 't') ? EVENT_TYPE_TX
+				                     : EVENT_TYPE_RX,
+				        g->events + g->num_events,
+				        g->verbosity_level);
+			}
+			if (ret) {
+				verbose_print(g->verbosity_level, LV_ERROR_MSG,
+				              "Invalid event syntax '%s'\n",
+				              optarg);
+				usage(stderr);
+				exit(EXIT_FAILURE);
+			}
+			g->num_events++;
+			break;
+		}
+
+		case 'g':
+			g->ignore_if_not_matching = 1;
+			break;
+
+		case 'v':
+			g->verbosity_level++;
+			break;
+
+		case 'C':
+			g->num_loops = atoi(optarg);
+			if (g->num_loops == 0) {
+				verbose_print(g->verbosity_level, LV_ERROR_MSG,
+				              "Invalid -C option '%s'\n",
+				              optarg);
+				exit(EXIT_FAILURE);
+			}
+			break;
+
+		default:
+			verbose_print(g->verbosity_level, LV_ERROR_MSG,
+			              "Unrecognized option %c\n", optopt);
+			usage(stderr);
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (g->ifname == NULL) {
+		verbose_print(g->verbosity_level, LV_ERROR_MSG,
+		              "Missing ifname\n");
+		usage(stderr);
+		exit(EXIT_FAILURE);
+	}
+
+	if (g->request_from_fd_server == 1 && g->extra_buffers_num > 0) {
+		verbose_print(
+		        g->verbosity_level, LV_ERROR_MSG,
+		        "Extra buffers can only be used when requesting an "
+		        "interface directly\n");
+		exit(EXIT_FAILURE);
+	}
+
+	if (g->request_from_fd_server == 0) {
+		/* We directly open the file descriptor. */
+		g->nmd = nmport_prepare(g->ifname);
+		if (g->nmd == NULL) {
+			verbose_perror(g->verbosity_level, LV_ERROR_MSG, g->ifname);
+			exit(EXIT_FAILURE);
+		}
+		if (g->extra_buffers_num > 0) {
+			g->nmd->reg.nr_extra_bufs = g->extra_buffers_num;
+		}
+		if (nmport_open_desc(g->nmd) < 0) {
+			verbose_perror(g->verbosity_level, LV_ERROR_MSG, g->ifname);
+			exit(EXIT_FAILURE);
+		}
+	} else {
+		g->nmd = get_if_fd(g, g->ifname);
+	}
+	if (g->nmd == NULL) {
+		verbose_print(g->verbosity_level, LV_ERROR_MSG,
+		              "Failed to nmport_open(%s)\n", g->ifname);
+		exit(EXIT_FAILURE);
+	}
+
+	if (g->extra_buffers_num > 0) {
+		/* Stores the real number of extra buffers. */
+		g->extra_buffers_num = g->nmd->reg.nr_extra_bufs;
+		verbose_print(g->verbosity_level, LV_DEBUG_EXTRA_BUF, "Received %u extra buffers\n", g->extra_buffers_num);
+		ret = parse_extra_buffers_indexes(g);
+		if (ret == -1) {
+			cleanup(g);
+			exit(EXIT_FAILURE);
+		}
+
+		swap_in_extra_buffers(g);
+	}
+
+	if (g->wait_link_secs > 0) {
+		sleep(g->wait_link_secs);
+	}
+
+	for (c = 0; c < g->num_loops; c++) {
+		for (i = 0; i < g->num_events; i++) {
+			const struct Event *e = g->events + i;
+
+			if (e->evtype == EVENT_TYPE_TX ||
+			    e->evtype == EVENT_TYPE_RX) {
+				g->filler   = e->filler;
+				g->pktm_len = e->pkt_len;
+				build_packet(g);
+			}
+
+			switch (e->evtype) {
+			case EVENT_TYPE_TX:
+				if (tx(g, e->num)) {
+					cleanup(g);
+					exit(EXIT_FAILURE);
+				}
+				break;
+
+			case EVENT_TYPE_RX:
+				if (rx(g, e->num)) {
+					cleanup(g);
+					exit(EXIT_FAILURE);
+				}
+				break;
+
+			case EVENT_TYPE_PAUSE:
+				usleep(e->usecs);
+				break;
+			}
+		}
+	}
+
+	/* if we have sent something, wait for all tx to complete */
+	tx_flush(g);
+	cleanup(g);
+	return 0;
+}
diff --git a/utils/get_avail_tx_packets.c b/utils/get_avail_tx_packets.c
new file mode 100644
index 000000000..c811f4d02
--- /dev/null
+++ b/utils/get_avail_tx_packets.c
@@ -0,0 +1,84 @@
+/* Given an interface name and a packet length (optional), prints to stdout
+ * the maximum number of packets (each within that length) that fits in the
+ * currently available TX slots. If the packet length is not specified, it
+ * is assumed that any packet to be transmitted fits within a single netmap
+ * slot, hence printing the number of available TX slots.
+ * On error, "-1" is printed.
+ * Arguments:
+ *    $1 -> interface name
+ *    $2 -> packet length
+ */
+
+#include 
+#include 
+#include 
+
+#include 
+#define NETMAP_WITH_LIBS
+#include 
+
+uint64_t
+slots_per_packet(struct netmap_ring *ring, unsigned pkt_len)
+{
+	return (uint64_t)(ceil((double)pkt_len / (double)ring->nr_buf_size));
+}
+
+uint64_t
+ring_avail_tx_packets(struct netmap_ring *ring, unsigned pkt_len)
+{
+	if (pkt_len == 0) {
+		return nm_ring_space(ring);
+	}
+
+	return nm_ring_space(ring) / slots_per_packet(ring, pkt_len);
+}
+
+uint64_t
+nmport_avail_tx_packets(struct nm_desc *nmd, unsigned pkt_len)
+{
+	uint64_t total = 0;
+	unsigned int i;
+
+	for (i = nmd->first_tx_ring; i <= nmd->last_tx_ring; i++) {
+		struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, i);
+
+		total += ring_avail_tx_packets(ring, pkt_len);
+	}
+
+	return total;
+}
+
+int
+main(int argc, char **argv)
+{
+	uint64_t avail_tx_packets;
+	struct nm_desc *nmd;
+	const char *if_name;
+	uint64_t pkt_len;
+
+	if (argc == 2) {
+		pkt_len = 0;
+	} else if (argc == 3) {
+		pkt_len = atoi(argv[2]);
+		if (pkt_len == 0) {
+			printf("-1");
+			exit(EXIT_FAILURE);
+		}
+	} else {
+		printf("-1");
+		exit(EXIT_FAILURE);
+	}
+
+	fclose(stderr);
+	if_name = argv[1];
+	nmd     = nm_open(if_name, NULL, 0, NULL);
+	if (nmd == NULL) {
+		printf("-1");
+		exit(EXIT_FAILURE);
+	}
+
+	avail_tx_packets = nmport_avail_tx_packets(nmd, pkt_len);
+	printf("%" PRId64, avail_tx_packets);
+
+	return 0;
+}
diff --git a/utils/get_max_tx_packets.c b/utils/get_max_tx_packets.c
new file mode 100644
index 000000000..4ef03ebec
--- /dev/null
+++ b/utils/get_max_tx_packets.c
@@ -0,0 +1,83 @@
+/* Given an interface name and a packet length (optional), prints to stdout
+ * the maximum number of packets (each within that length) that fits in the
+ * transmit rings, assuming they are all empty. If the packet length is not
+ * specified, it is assumed that any packet to be transmitted fits within a
+ * single netmap slot, hence printing the total number of TX slots.
+ * On error, "-1" is printed.
+ * Arguments:
+ *    $1 -> interface name
+ *    $2 -> packet length
+ */
+#include 
+#include 
+#include 
+
+#include 
+#define NETMAP_WITH_LIBS
+#include 
+
+uint64_t
+slots_per_packet(struct netmap_ring *ring, unsigned pkt_len)
+{
+	return (uint64_t)(ceil((double)pkt_len / (double)ring->nr_buf_size));
+}
+
+uint64_t
+ring_max_tx_packets(struct netmap_ring *ring, unsigned pkt_len)
+{
+	if (pkt_len == 0) {
+		return nm_ring_space(ring) - 1;
+	}
+
+	return (ring->num_slots - 1) / slots_per_packet(ring, pkt_len);
+}
+
+uint64_t
+nmport_max_tx_packets(struct nm_desc *nmd, unsigned pkt_len)
+{
+	uint64_t total = 0;
+	unsigned int i;
+
+	for (i = nmd->first_tx_ring; i <= nmd->last_tx_ring; i++) {
+		struct netmap_ring *ring = NETMAP_TXRING(nmd->nifp, i);
+
+		total += ring_max_tx_packets(ring, pkt_len);
+	}
+
+	return total;
+}
+
+int
+main(int argc, char **argv)
+{
+	uint64_t max_tx_packets;
+	struct nm_desc *nmd;
+	const char *if_name;
+	uint64_t pkt_len;
+
+	if (argc == 2) {
+		pkt_len = 0;
+	} else if (argc == 3) {
+		pkt_len = atoi(argv[2]);
+		if (pkt_len == 0) {
+			printf("-1");
+			exit(EXIT_FAILURE);
+		}
+	} else {
+		printf("-1");
+		exit(EXIT_FAILURE);
+	}
+
+	fclose(stderr);
+	if_name = argv[1];
+	nmd     = nm_open(if_name, NULL, 0, NULL);
+	if (nmd == NULL) {
+		printf("-1");
+		exit(EXIT_FAILURE);
+	}
+
+	max_tx_packets = nmport_max_tx_packets(nmd, pkt_len);
+	printf("%" PRId64, max_tx_packets);
+
+	return 0;
+}
diff --git a/utils/producer.c b/utils/producer.c
index 8bafbd3cf..da2a05ceb 100644
--- a/utils/producer.c
+++ b/utils/producer.c
@@ -77,6 +77,7 @@ tsc_sleep_till(uint64_t when)
         barrier();
 #undef barrier
 }
+
 int main(int argc, char **argv)
 {
 	const char *ifname = "netmap:nmsink0";
diff --git a/utils/randomized_tests b/utils/randomized_tests
new file mode 100755
index 000000000..32a60c21a
--- /dev/null
+++ b/utils/randomized_tests
@@ -0,0 +1,171 @@
+#!/usr/bin/env bash
+
+################################################################################
+# Run all the tests using randomized values for number of packets, fill
+# character and packet length.
+################################################################################
+
+usage() {
+    cat < prints error messages
+		#    -vv   -> -v, send and receive actions
+		#    -vvv  -> -vv and packet building
+		#    -vvvv -> -vvv and arguments parsing
+		if [ -z "$VERB" ]; then
+			VERB="-"
+		fi
+		VERB="${VERB}v"
+		OUTPUT="/dev/stdout"
+		;;
+	"L")
+		functionals="functional"
+		;;
+
+	\?)
+		echo "Unknown option '$opt'"
+		echo ""
+		usage
+		exit 1
+		;;
+	esac
+done
+
+# Support for colored output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+CYAN='\033[0;36m'
+ORANGE='\033[0;33m'
+NOC='\033[0m' # No Color
+
+if [ -n "$LIST" ]; then
+	pushd $(pwd)
+	cd $(dirname $0)
+	echo "Available tests:"
+	i="1"
+	for t in tests/* ; do
+		printf "    #%03d: %s\n" $i $t
+		i=$((i + 1))
+	done
+	popd
+	exit 0
+fi
+
+if [ "$EUID" -ne "0" ]; then
+	echo "This script must be run as root"
+	exit 1
+fi
+
+which vale-ctl
+if [ "$?" != "0" ]; then
+	echo "vale-ctl program not not found"
+	exit 1
+fi
+
+which bc
+if [ "$?" != "0" ]; then
+	echo "bc program not not found"
+	exit 1
+fi
+
+random_num="$((65 + $RANDOM % 58))"
+# https://stackoverflow.com/a/10503163
+random_fill=$(printf \\$(printf '%03o' $random_num))
+# We need to make sure that we can send this number of packets with this length
+# at once. At the moment i just put max values which works for the defaults
+# number of rings and buffer size.
+random_len="$((50 + $RANDOM % 10000))"
+random_packet_num="$((1 + $RANDOM % 100))"
+# Use and empty string instead of "-q" if you don't want to perform a sequential
+# send/receive check
+seq_check="-q"
+
+echo "Running tests with"
+echo "   number of packets: ${random_packet_num}"
+echo "   fill character   : ${random_fill}"
+echo "   sequence check   : ${seq_check}"
+echo "   packet length    : ${random_len}"
+echo ""
+
+# Save current directory and enter the utils/ directory.
+pushd $(pwd)
+cd $(dirname $0)
+
+# Add the current directory (and build-utils) to the PATH. In this way we can
+# easily invoke functional, get_avail_tx_packets and the other executables
+# from the test scripts.
+PATH="$(pwd)/../build-utils:$(pwd):${PATH}"
+
+source test_lib
+
+netmap_load
+
+RET=0
+i=0
+for f in $functionals; do
+	export FUNCTIONAL=$f
+	echo -e "${ORANGE}>>> Running tests using $FUNCTIONAL"
+	for t in tests/* ; do
+		i=$((i + 1))
+
+		# Possibly filter tests by TESTID
+		[ -n "$TESTID" ] && [ "$i" != "$TESTID" ] && continue
+
+		# Run this test
+		echo -e "${ORANGE}>>> Running test #${i}: ${CYAN}\"${t}\"${NOC}"
+		restart_fd_server
+		$t $VERB -n $random_packet_num -l $random_len -f $random_fill "$seq_check" 2>${OUTPUT}
+		res="$?"
+		close_fd_server
+		if [ "$res" == 0 ]; then
+			echo -e "${GREEN}>>> Test #${i} PASSED${NOC}"
+		else
+			echo -e "${RED}>>> Test #${i} FAILED${NOC}"
+			RET="1"
+			break
+		fi
+	done
+	[ "$RET" == 0 ] || break
+done
+
+# Wait for the fd_server to terminate and release its references
+sleep 0.5
+
+netmap_unload
+
+popd
+exit ${RET}
diff --git a/utils/switch-modules/ovs-2.4.0/0001-datapath-netmap-VALE-support.patch b/utils/switch-modules/ovs-2.4.0/0001-datapath-netmap-VALE-support.patch
index e1fd4f2a7..dcf26b660 100644
--- a/utils/switch-modules/ovs-2.4.0/0001-datapath-netmap-VALE-support.patch
+++ b/utils/switch-modules/ovs-2.4.0/0001-datapath-netmap-VALE-support.patch
@@ -15,7 +15,7 @@ index 000000000..664078ed3
 +This file explains how to install and use the Open vSwitch with VALE.
 +
 +The VALE support for Open vSwitch is **experimental**: it has not
-+been throughly tested.
++been thoroughly tested.
 +It currently supports only netdev (including internal one) and VXLAN
 +vport types.
 +
@@ -60,7 +60,7 @@ index 000000000..664078ed3
 +# modprobe netmap
 +# modprobe openvswitch
 +
-+(2) Run the openvswitch deamon. This step may be performed differently on
++(2) Run the openvswitch daemon. This step may be performed differently on
 +different distributions. Refer to the OVS documentation.
 +
 +(3) Create an OVS bridge:
@@ -77,7 +77,7 @@ index 000000000..664078ed3
 +# ovs-vsctl add-port br0 vi0
 +# ovs-vsctl add-port br0 vi1
 +
-+(6) Add some OpenFlow rules, to forward traffic betweeen vi0 and vi1
++(6) Add some OpenFlow rules, to forward traffic between vi0 and vi1
 +(assuming the port identifiers are 1 and 2):
 +
 +# ovs-ofctl add-flow br0 in_port=1,actions=output:2
diff --git a/utils/switch-modules/ovs-2.6.1/0001-datapath-netmap-VALE-support.patch b/utils/switch-modules/ovs-2.6.1/0001-datapath-netmap-VALE-support.patch
index 1372e0dd5..e37518c56 100644
--- a/utils/switch-modules/ovs-2.6.1/0001-datapath-netmap-VALE-support.patch
+++ b/utils/switch-modules/ovs-2.6.1/0001-datapath-netmap-VALE-support.patch
@@ -15,7 +15,7 @@ index 000000000..7cf02e45e
 +This file explains how to install and use the Open vSwitch with VALE.
 +
 +The VALE support for Open vSwitch is **experimental**: it has not
-+been throughly tested.
++been thoroughly tested.
 +It currently supports only netdev (including internal one) and VXLAN
 +vport types.
 +
@@ -60,7 +60,7 @@ index 000000000..7cf02e45e
 +# modprobe netmap
 +# modprobe openvswitch
 +
-+(2) Run the openvswitch deamon. This step may be performed differently on
++(2) Run the openvswitch daemon. This step may be performed differently on
 +different distributions. Refer to the OVS documentation.
 +
 +(3) Create an OVS bridge:
@@ -77,7 +77,7 @@ index 000000000..7cf02e45e
 +# ovs-vsctl add-port br0 vi0
 +# ovs-vsctl add-port br0 vi1
 +
-+(6) Add some OpenFlow rules, to forward traffic betweeen vi0 and vi1
++(6) Add some OpenFlow rules, to forward traffic between vi0 and vi1
 +(assuming the port identifiers are 1 and 2):
 +
 +# ovs-ofctl add-flow br0 in_port=1,actions=output:2
diff --git a/utils/sync_kloop_test.c b/utils/sync_kloop_test.c
new file mode 100644
index 000000000..0b9069f19
--- /dev/null
+++ b/utils/sync_kloop_test.c
@@ -0,0 +1,558 @@
+#include 
+#include 
+#include 
+#include 
+#define NETMAP_WITH_LIBS
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifdef __linux__
+#include 
+#endif /* __linux__ */
+
+#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
+
+static int stop = 0;
+
+static void
+sigint_handler(int signum)
+{
+	(void)signum;
+	ACCESS_ONCE(stop) = 1;
+}
+
+struct eventfds {
+	int ioeventfd;
+	int irqfd;
+};
+
+struct context {
+	int fd; /* netmap file descriptor */
+	struct nm_csb_atok *atok_base;
+	struct nm_csb_ktoa *ktoa_base;
+	int sleep_us;
+	int verbose;
+	int batch;
+	int num_entries;
+	struct eventfds *eventfds;
+};
+
+static void *
+kloop_worker(void *opaque)
+{
+	struct nmreq_opt_sync_kloop_eventfds *opt = NULL;
+	struct context *ctx                       = opaque;
+	struct nmreq_sync_kloop_start req;
+	struct nmreq_header hdr;
+	int ret;
+
+	if (ctx->eventfds) {
+		size_t opt_size = sizeof(*opt) +
+		                  ctx->num_entries * sizeof(opt->eventfds[0]);
+		int i;
+
+		opt = malloc(opt_size);
+		memset(opt, 0, opt_size);
+		opt->nro_opt.nro_next    = 0;
+		opt->nro_opt.nro_reqtype = NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS;
+		opt->nro_opt.nro_status  = 0;
+		opt->nro_opt.nro_size    = opt_size;
+		for (i = 0; i < ctx->num_entries; i++) {
+			opt->eventfds[i].ioeventfd = ctx->eventfds[i].ioeventfd;
+			opt->eventfds[i].irqfd     = ctx->eventfds[i].irqfd;
+		}
+	}
+
+	/* The ioctl() returns on failure or when some other thread
+	 * stops the kernel loop. */
+	memset(&hdr, 0, sizeof(hdr));
+	hdr.nr_version = NETMAP_API;
+	hdr.nr_reqtype = NETMAP_REQ_SYNC_KLOOP_START;
+	hdr.nr_body    = (uintptr_t)&req;
+	hdr.nr_options = (uintptr_t)opt;
+	memset(&req, 0, sizeof(req));
+	req.sleep_us = (uint32_t)ctx->sleep_us;
+	ret          = ioctl(ctx->fd, NIOCCTRL, &hdr);
+	if (ret) {
+		perror("ioctl(/dev/netmap, NIOCCTRL, SYNC_KLOOP_START)");
+		exit(EXIT_FAILURE);
+	}
+
+	return NULL;
+}
+
+static inline int
+ringspace(struct netmap_ring *ring, uint32_t head)
+{
+	int space = ring->tail - head;
+
+	if (space < 0) {
+		space += ring->num_slots;
+	}
+
+	return space;
+}
+
+static void
+usage(const char *progname)
+{
+	printf("%s\n"
+	       "[-h (show this help and exit)]\n"
+	       "[-f FUNCTION (rx,tx)]\n"
+	       "[-v (be more verbose)]\n"
+	       "[-R RATE_PPS (0 = infinite)]\n"
+	       "[-b BATCH_SIZE (in packets)]\n"
+	       "[-u KLOOP_SLEEP_US (in microseconds)]\n"
+	       "[-k (use eventfd-based notifications)]\n"
+	       "-i NETMAP_PORT\n",
+	       progname);
+}
+
+typedef enum {
+	F_TX = 0,
+	F_RX,
+} function_t;
+
+int
+main(int argc, char **argv)
+{
+	struct nm_csb_atok *atok_base = NULL;
+	struct nm_csb_ktoa *ktoa_base = NULL;
+	struct eventfds *eventfds_base = NULL;
+	int num_tx_entries, num_rx_entries;
+	unsigned long long bytes = 0;
+	unsigned long long pkts  = 0;
+	const char *ifname       = NULL;
+	void *csb                = NULL;
+	uint16_t first_ring, last_ring;
+	struct netmap_if *nifp = NULL;
+	struct context ctx;
+
+	double target_rate         = 0.0 /* pps */;
+	unsigned int period_us     = 0;
+	unsigned int period_budget = 0;
+	struct timeval next_time;
+	int packet_budget;
+	struct timeval loop_begin, loop_end;
+	int use_eventfds = 0;
+
+	int init_tx_payload = 1;
+	function_t func;
+	pthread_t th;
+	int opt;
+	int ret;
+
+	/* Register a signal handler to stop the program on SIGINT. */
+	{
+		struct sigaction sa;
+
+		sa.sa_handler = sigint_handler;
+		sigemptyset(&sa.sa_mask);
+		sa.sa_flags = SA_RESTART;
+		if (sigaction(SIGINT, &sa, NULL)) {
+			perror("sigaction(SIGINT)");
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	memset(&ctx, 0, sizeof(ctx));
+	func         = F_RX;
+	ctx.verbose  = 0;
+	ctx.batch    = 1;
+	ctx.sleep_us = 100;
+
+	while ((opt = getopt(argc, argv, "hi:f:vR:b:u:k")) != -1) {
+		switch (opt) {
+		case 'h':
+			usage(argv[0]);
+			return 0;
+
+		case 'i':
+			ifname = optarg;
+			break;
+
+		case 'f':
+			if (!strcmp(optarg, "tx")) {
+				func = F_TX;
+			} else if (!strcmp(optarg, "rx")) {
+				func = F_RX;
+			} else {
+				printf("    Unknown function %s\n", optarg);
+			}
+			break;
+
+		case 'v':
+			ctx.verbose++;
+			break;
+
+		case 'R':
+			target_rate = atof(optarg);
+			if (target_rate < 0.0) {
+				printf("    Invalid rate %s\n", optarg);
+				return -1;
+			}
+			break;
+
+		case 'b':
+			ctx.batch = atoi(optarg);
+			if (ctx.batch <= 0) {
+				printf("    Invalid batch %s\n", optarg);
+				return -1;
+			}
+			break;
+
+		case 'u':
+			ctx.sleep_us = atoi(optarg);
+			if (ctx.sleep_us < 0) {
+				printf("    Invalid sleep_us %s\n", optarg);
+				return -1;
+			}
+			break;
+
+		case 'k':
+			use_eventfds = 1;
+			break;
+
+		default:
+			printf("    Unrecognized option %c\n", opt);
+			usage(argv[0]);
+			return -1;
+		}
+	}
+
+	if (ifname == NULL) {
+		printf("No netmap port specified\n");
+		usage(argv[0]);
+		return -1;
+	}
+
+	ctx.fd = open("/dev/netmap", O_RDWR);
+	if (ctx.fd < 0) {
+		perror("open(/dev/netmap)");
+		return ctx.fd;
+	}
+
+	/* Get the number of TX and RX rings. */
+	{
+		struct nmreq_port_info_get req;
+		struct nmreq_header hdr;
+
+		memset(&hdr, 0, sizeof(hdr));
+		hdr.nr_version = NETMAP_API;
+		strncpy(hdr.nr_name, ifname, sizeof(hdr.nr_name) - 1);
+		hdr.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
+		hdr.nr_body    = (uintptr_t)&req;
+		memset(&req, 0, sizeof(req));
+		ret = ioctl(ctx.fd, NIOCCTRL, &hdr);
+		if (ret) {
+			perror("ioctl(/dev/netmap, NIOCCTRL, PORT_INFO_GET)");
+			return ret;
+		}
+
+		num_tx_entries = req.nr_tx_rings;
+		num_rx_entries = req.nr_rx_rings;
+		ctx.num_entries = num_tx_entries + num_rx_entries;
+	}
+
+	/* Allocate CSB entries. */
+	{
+		size_t csb_size;
+
+		printf("Number of CSB entries = %d\n", (int)ctx.num_entries);
+		csb_size = (sizeof(struct nm_csb_atok) +
+		            sizeof(struct nm_csb_ktoa)) *
+		           ctx.num_entries;
+		assert(csb_size > 0);
+		ret = posix_memalign(&csb, sizeof(struct nm_csb_atok),
+		                     csb_size);
+		if (ret) {
+			printf("Failed to allocate CSB memory\n");
+			return -1;
+		}
+		memset(csb, 0, csb_size);
+
+		atok_base = ctx.atok_base = (struct nm_csb_atok *)csb;
+		ktoa_base                 = ctx.ktoa_base =
+		        (struct nm_csb_ktoa *)(ctx.atok_base + ctx.num_entries);
+	}
+
+	{
+		/* Open the netmap port with NR_EXCLUSIVE and with
+		 * the CSB option. */
+		struct nmreq_register req;
+		struct nmreq_opt_csb opt;
+		struct nmreq_header hdr;
+		void *mem;
+
+		memset(&opt, 0, sizeof(opt));
+		opt.nro_opt.nro_reqtype = NETMAP_REQ_OPT_CSB;
+		opt.csb_atok = (uintptr_t)atok_base;
+		opt.csb_ktoa = (uintptr_t)ktoa_base;
+
+		memset(&hdr, 0, sizeof(hdr));
+		hdr.nr_version = NETMAP_API;
+		strncpy(hdr.nr_name, ifname, sizeof(hdr.nr_name) - 1);
+		hdr.nr_reqtype = NETMAP_REQ_REGISTER;
+		hdr.nr_body    = (uintptr_t)&req;
+		hdr.nr_options = (uintptr_t)&opt.nro_opt;
+		memset(&req, 0, sizeof(req));
+		req.nr_mode       = NR_REG_ALL_NIC;
+		req.nr_flags      |= NR_EXCLUSIVE;
+		ret               = ioctl(ctx.fd, NIOCCTRL, &hdr);
+		if (ret) {
+			perror("ioctl(/dev/netmap, NIOCCTRL, REGISTER)");
+			return ret;
+		}
+
+		mem = mmap(0, req.nr_memsize, PROT_WRITE | PROT_READ,
+				MAP_SHARED, ctx.fd, 0);
+		if (mem == MAP_FAILED) {
+			perror("mmap()");
+			return -1;
+		}
+		nifp = NETMAP_IF(mem, req.nr_offset);
+	}
+
+	/* Allocate eventfds. */
+	if (use_eventfds) {
+#ifdef __linux__
+		int i;
+
+		ctx.eventfds =
+		        malloc(ctx.num_entries * sizeof(ctx.eventfds[0]));
+		for (i = 0; i < ctx.num_entries; i++) {
+			int efd;
+
+			efd = eventfd(0, 0);
+			if (efd < 0) {
+				perror("eventfd()");
+			}
+			ctx.eventfds[i].ioeventfd = efd;
+			efd                       = eventfd(0, 0);
+			if (efd < 0) {
+				perror("eventfd()");
+			}
+			ctx.eventfds[i].irqfd = efd;
+		}
+#else  /* !__linux__ */
+		printf("Eventfds not supported on this platform\n");
+		return -1;
+#endif /* !__linux__ */
+	}
+
+	/* Start the kernel worker thread. */
+	ret = pthread_create(&th, NULL, kloop_worker, &ctx);
+	if (ret) {
+		printf("pthread_create() failed: %s\n", strerror(ret));
+		return -1;
+	}
+
+	/* Compute variables for rate limiting. */
+	if (target_rate != 0.0) {
+		double us = 1000000.0 / target_rate;
+		double b  = 1.0;
+#define MIN_USLEEP 50.0
+		if (us < MIN_USLEEP) {
+			b = ceil(MIN_USLEEP / us);
+			us *= b;
+		}
+		period_us     = (unsigned int)us;
+		period_budget = (unsigned int)b;
+#undef MIN_USLEEP
+	}
+#if 0
+	printf("period us %u batch %u\n", period_us, period_budget);
+#endif
+	eventfds_base = ctx.eventfds;
+	if (func == F_RX) {
+		atok_base += num_tx_entries;
+		ktoa_base += num_tx_entries;
+		eventfds_base += num_tx_entries;
+		first_ring = 0;
+		last_ring  = num_tx_entries-1;
+	} else {
+		first_ring = 0;
+		last_ring  = num_rx_entries-1;
+	}
+
+	gettimeofday(&next_time, NULL);
+	loop_begin    = next_time;
+	packet_budget = 0;
+
+	/* Run the application loop. */
+	while (!ACCESS_ONCE(stop)) {
+		uint16_t r;
+
+		if (period_us == 0) {
+			packet_budget = 0xfffffff; /* infinite */
+		} else {
+			struct timeval now, diff;
+
+			next_time.tv_usec += period_us;
+			if (next_time.tv_usec > 1000000) {
+				next_time.tv_usec -= 1000000;
+				next_time.tv_sec++;
+			}
+			packet_budget = period_budget;
+			if (period_budget > 1) {
+				/* Busy wait. */
+				for (;;) {
+					gettimeofday(&now, NULL);
+					/* if now >= next_time */
+					if (!timercmp(&now, &next_time, <)) {
+						break;
+					}
+				}
+			} else {
+				/* Sleep. */
+				gettimeofday(&now, NULL);
+				/* if now < next_time ... */
+				if (timercmp(&now, &next_time, <)) {
+					/* diff = next_time - now */
+					timersub(&next_time, &now, &diff);
+					usleep(diff.tv_usec);
+				}
+			}
+		}
+
+		for (r = first_ring; r <= last_ring; r++) {
+			struct eventfds *evfds = ctx.eventfds ?
+					(eventfds_base + r) : NULL;
+			struct nm_csb_atok *atok = atok_base + r;
+			struct nm_csb_ktoa *ktoa = ktoa_base + r;
+			struct netmap_ring *ring;
+			struct netmap_slot *slot;
+			uint32_t head;
+			int batch;
+
+			if (func == F_TX) {
+				ring = NETMAP_TXRING(nifp, r);
+			} else {
+				ring = NETMAP_RXRING(nifp, r);
+			}
+
+			head = atok->head;
+			/* For convenience we reuse the netmap_ring
+			 * header to store hwtail and hwcur, since the
+			 * cur, head and tail fields are not used. */
+			nm_sync_kloop_appl_read(ktoa,
+			                        /*hwtail=*/&ring->tail,
+			                        /*hwcur=*/&ring->cur);
+			batch = ringspace(ring, head);
+			if (batch > packet_budget) { /* rate limiting */
+				batch = packet_budget;
+			}
+			if (batch == 0) {
+				continue;
+			}
+			if (batch > ctx.batch) {
+				batch = ctx.batch;
+			}
+
+			pkts += batch;
+			packet_budget -= batch;
+			while (--batch >= 0) {
+				slot = ring->slot + head;
+				if (func == F_TX) {
+					slot->len = 60;
+					if ((slot->flags & NS_BUF_CHANGED) ||
+					    init_tx_payload) {
+						char *buf = NETMAP_BUF(
+						        ring, slot->buf_idx);
+						memset(buf, 0xFF, 6);
+						memset(buf + 6, 0, 6);
+						buf[12] = 0x08;
+						buf[13] = 0x00;
+						memset(buf + 14, 'x',
+						       slot->len - 14);
+						/* Drop the copy once we are
+						 * confident that we have filled
+						 * all the buffers in the TX
+						 * ring. */
+						if (pkts > 20000) {
+							printf("Stop to init "
+							       "packets\n");
+							init_tx_payload = 0;
+						}
+					}
+					slot->flags = 0;
+				} else {
+					if (ctx.verbose) {
+						char *buf = NETMAP_BUF(
+						        ring, slot->buf_idx);
+						int i;
+						for (i = 0; i < slot->len;
+						     i++) {
+							printf(" %02x",
+							       (unsigned char)
+							               buf[i]);
+						}
+						printf("\n");
+					}
+				}
+				bytes += slot->len;
+				head = nm_ring_next(ring, head);
+			}
+			/* Write updated information for the kernel. */
+			nm_sync_kloop_appl_write(atok, head, head);
+			/* Notify the kernel if needed. */
+			if (evfds && ACCESS_ONCE(ktoa->kern_need_kick)) {
+				uint64_t x = 1;
+				int n = write(evfds->ioeventfd, &x, sizeof(x));
+
+				assert(n == sizeof(x));
+				if (ctx.verbose) {
+					printf("Kernel notified\n");
+				}
+			}
+			if (ctx.verbose) {
+				printf("ring #%u, hwcur %u, head %u, hwtail "
+				       "%u\n",
+				       (unsigned int)r, ring->cur, head,
+				       ring->tail);
+			}
+		}
+	}
+
+	/* Measure average rate. */
+	gettimeofday(&loop_end, NULL);
+	{
+		struct timeval duration;
+		unsigned long udiff;
+		double measured_rate;
+
+		timersub(&loop_end, &loop_begin, &duration);
+		udiff         = duration.tv_sec * 1000000 + duration.tv_usec;
+		measured_rate = (double)pkts / (double)udiff;
+		printf("Measured rate: %.6f Mpps\n", measured_rate);
+	}
+
+	/* Stop the kernel worker thread. */
+	{
+		struct nmreq_header hdr;
+		int ret;
+
+		memset(&hdr, 0, sizeof(hdr));
+		hdr.nr_version = NETMAP_API;
+		hdr.nr_reqtype = NETMAP_REQ_SYNC_KLOOP_STOP;
+		ret            = ioctl(ctx.fd, NIOCCTRL, &hdr);
+		if (ret) {
+			perror("ioctl(/dev/netmap, NIOCCTRL, SYNC_KLOOP_STOP)");
+		}
+	}
+
+	/* Release the allocated resources. */
+	ret = pthread_join(th, NULL);
+	if (ret) {
+		printf("pthread_join() failed: %s\n", strerror(ret));
+	}
+
+	free(csb);
+
+	return 0;
+}
diff --git a/utils/test_lib b/utils/test_lib
new file mode 100755
index 000000000..059730413
--- /dev/null
+++ b/utils/test_lib
@@ -0,0 +1,355 @@
+#!/usr/bin/env bash
+
+FUNCTIONAL=${FUNCTIONAL:-functional}
+export FUNCTIONAL
+
+################################################################################
+# Creates the new file descriptor "3" and redirects it to stdout, then redirects
+# stderr e stdout to /dev/null. This way only the stuff we redirect to fd 3 is
+# printed simplifying the work required to filter our output.
+# Arguments:
+#   None
+################################################################################
+function redirect_std() {
+	exec 3>&1
+	exec 1>/dev/null
+	exec 2>&1
+}
+
+################################################################################
+# Calls echo on the correct file descriptor (it needs to match the new file
+# descriptor created by redirect_std()).
+# Arguments:
+#   None
+################################################################################
+function stdout_echo() {
+	echo "$@"
+}
+
+################################################################################
+# Closes the file descriptor server.
+# Arguments:
+#   None
+################################################################################
+function close_fd_server() {
+	$FUNCTIONAL -c
+	check_success "$?" "close_fd_server"
+}
+
+################################################################################
+# Starts the file descriptor server.
+# Arguments:
+#   None
+################################################################################
+function start_fd_server() {
+	$FUNCTIONAL -o
+	check_success "$?" "start_fd_server"
+}
+
+################################################################################
+# Restarts the file descriptor server.
+# Arguments:
+#   None
+################################################################################
+function restart_fd_server() {
+	close_fd_server
+	start_fd_server
+}
+
+################################################################################
+# Can be called, when a test script successfully terminates, to perform a
+# specific action.
+# Arguments:
+#   $1 -> string containing the test script name
+################################################################################
+function test_successful() {
+	local test_name="$1"
+	stdout_echo "$test_name: success."
+}
+
+################################################################################
+# Checks the exit value, if it's different from the expected exit value
+# terminates the script.
+# Arguments:
+#   $1 -> exit value to check
+#   $2 -> expected exit value to check against
+#   $3 -> string printed
+#   $4 -> string containing the test script name
+################################################################################
+function check_exit() {
+	local exit_value="$1"
+	local expected_value="$2"
+	local string_to_print="$3"
+	local test_name="$0"
+
+	if [ $exit_value != $expected_value ] ; then
+		stdout_echo "$test_name: $string_to_print FAIL($exit_value != $expected_value)."
+		exit 1
+	fi
+}
+
+################################################################################
+# Checks the exit value, if it's different from 0 terminates the script.
+# Arguments:
+#   $1 -> exit value to check
+#   $2 -> string printed
+################################################################################
+function check_success() {
+	local exit_value="$1"
+	local string_to_print="$2"
+
+	check_exit "$exit_value" 0 "$string_to_print"
+}
+
+################################################################################
+# Checks the exit value, if it's different from 1 (only 1 is considered failure
+# at the moment) terminates the script.
+# Arguments:
+#   $1 -> exit value to check
+#   $2 -> string printed
+################################################################################
+function check_failure() {
+	local exit_value="$1"
+	local string_to_print="$2"
+
+	check_exit "$exit_value" 1 "$string_to_print"
+}
+
+################################################################################
+# Set a trap while maintaining the one currently set. The new one will be
+# executed first.
+# Arguments:
+#   $1 -> new command to execute during the trap
+#   $2 -> signal to trap
+################################################################################
+function cumulative_trap() {
+	local new_command="$1"
+	local signal="$2"
+	local current_command=""
+
+	# If we run "trap -p SIGNAL" in a subshell we read the traps for that
+	# subshell, instead of the current one.
+	# https://unix.stackexchange.com/a/334593
+	shopt -s lastpipe
+	trap -p "$signal" | read current_command
+	shopt -u lastpipe
+
+	current_command="$(echo $current_command | awk -F\' '{print $2}')"
+	new_command="$new_command; $current_command"
+	trap "$new_command" "$signal"
+}
+
+################################################################################
+# Creates a VALE persistent port and sets a cleanup handler which will be
+# called when the script exits.
+# Arguments:
+#   $1 -> name of the VALE persistent port
+#   $2 -> expected exit value of vale-ctl (optional, default = 0)
+################################################################################
+function create_vale_persistent_port() {
+	local if_name="$1"
+	local create_exit_value="$2"
+	create_exit_value="${create_exit_value:-0}"
+
+	vale-ctl -n "$if_name"
+	check_exit "$?" "$create_exit_value" "create $if_name"
+	cumulative_trap "vale-ctl -r $if_name" "EXIT"
+	check_success "$?" "trap-remove $if_name"
+}
+
+################################################################################
+# Destroys a VALE persistent port.
+# Arguments:
+#   $1 -> name of the VALE persistent port
+#   $2 -> expected exit value of vale-ctl (optional, default = 0)
+################################################################################
+function destroy_vale_persistent_port() {
+	local if_name="$1"
+	local destroy_exit_value="$2"
+	destroy_exit_value="${destroy_exit_value:-0}"
+
+	vale-ctl -r "$if_name"
+	check_exit "$?" "$destroy_exit_value" "create $if_name"
+}
+
+################################################################################
+# Attaches an interface registered to the os to a VALE bridge and sets a cleanup
+# handler which will be called when the script exits.
+# Arguments:
+#   $1 -> name of the VALE bridge that the port will be to be attached to
+#   $2 -> name of the interface
+#   $3 -> expected exit value of vale-ctl (optional, default = 0)
+################################################################################
+function attach_to_vale_bridge() {
+	local bdg_name="$1"
+	local if_name="$2"
+	local attach_exit_value="$3"
+	attach_exit_value="${attach_exit_value:-0}"
+
+	vale-ctl -a "$bdg_name:$if_name"
+	check_exit "$?" "$attach_exit_value" "attach $bdg_name:$if_name"
+	cumulative_trap "vale-ctl -d $bdg_name:$if_name" "EXIT"
+	# We first need to close the file descriptor of the interface, otherwise
+	# the detach will fail. To accomplish that we shut down fd_server.
+	cumulative_trap "close_fd_server" "EXIT"
+	check_success "$?" "trap-detach $bdg_name:$if_name"
+}
+
+################################################################################
+# Detaches an interface registered to the os from a VALE bridge.
+# Arguments:
+#   $1 -> name of the VALE bridge that the port will be to be attached to
+#   $2 -> name of the interface
+#   $3 -> expected exit value of vale-ctl (optional, default = 0)
+################################################################################
+function detach_from_vale_bridge() {
+	local bdg_name="$1"
+	local if_name="$2"
+	local detach_exit_value="$3"
+	detach_exit_value="${detach_exit_value:-0}"
+
+	vale-ctl -d "$bdg_name:$if_name"
+	check_exit "$?" "$detach_exit_value" "detach $bdg_name:$if_name"
+}
+
+################################################################################
+# Creates a pair of veth interfaces and sets a cleanup handler which will be
+# called when the script exits.
+# Arguments:
+#   $1 -> base name of the veth devices
+################################################################################
+function create_veth_interfaces() {
+	local if_name="$1"
+	local if_name1="${if_name}A"
+	local if_name2="${if_name}B"
+
+	ip link add "$if_name1" type veth peer name "$if_name2"
+	check_success "$?" "create $if_name"
+	# We first need to close the file descriptor of the interfaces,
+	# otherwise the delete will fail. To accomplish that we shut down
+	# fd_server.
+	cumulative_trap "ip link delete $if_name1" "EXIT"
+	check_success "$?" "trap-delete $if_name1"
+	cumulative_trap "close_fd_server" "EXIT"
+	check_success "$?" "trap-detach $bdg_name:$if_name"
+}
+
+################################################################################
+# Prints accepted command line arguments.
+# Arguments:
+#   None
+################################################################################
+function send_recv_usage() {
+	stdout_echo "usage: [-h] "
+	stdout_echo "       [-l packet_length]"
+	stdout_echo "       [-f fill_character]"
+	stdout_echo "       [-n packets_to_send]"
+	stdout_echo "       [-q (sequential send/read)]"
+	stdout_echo "       [-v (increases verbosity level)]"
+}
+
+################################################################################
+# Parse the command line arguments of the calling script.
+# Arguments:
+#   It must be always called like like this 'parse_arguments "$@"'
+################################################################################
+function parse_send_recv_arguments() {
+	while getopts "hvql:f:n:" opt; do
+		case $opt in
+			l) len="$OPTARG"
+			;;
+			f) fill="$OPTARG"
+			;;
+			n) num="$OPTARG"
+			;;
+			q) seq="-q"
+			;;
+			v) if [ -z $verbosity ] ; then
+					verbosity="-v"
+				else
+					verbosity="${verbosity}v"
+			   fi
+			;;
+			h) send_recv_usage ; exit 0
+			;;
+			\?) send_recv_usage ; exit 1
+			;;
+		esac
+	done
+}
+
+################################################################################
+# Prints to stdout the hexadecimal representation of the received integer.
+# Arguments:
+#   $1 -> integer to translate
+################################################################################
+# https://superuser.com/a/218349
+int_to_hex() {
+	local my_int=$1
+	echo $(echo "obase=16; $my_int" | bc)
+}
+
+################################################################################
+# Prints to stdout a random MAC address. FF:FF:FF:FF:FF:FF and 0:0:0:0:0:0 are
+# excluded.
+# Arguments:
+#   None
+################################################################################
+# https://superuser.com/a/218349
+function get_random_MAC() {
+	local range=256
+	local MAC1=$(int_to_hex $((RANDOM % range)))
+	local MAC2=$(int_to_hex $((RANDOM % range)))
+	local MAC3=$(int_to_hex $((RANDOM % range)))
+	local MAC4=$(int_to_hex $((RANDOM % range)))
+	local MAC5=$(int_to_hex $((RANDOM % range)))
+	local MAC6=$(int_to_hex $((RANDOM % range)))
+	local MAC="${MAC1}:${MAC2}:${MAC3}:${MAC4}:${MAC5}:${MAC6}"
+	if [ $MAC = "FF:FF:FF:FF:FF:FF" ] || [ $MAC = "0:0:0:0:0:0" ] ; then
+		get_random_MAC
+	else
+		echo "$MAC"
+	fi
+}
+
+################################################################################
+# Loads the netmap module, if needed.
+# Arguments:
+#   None
+################################################################################
+function netmap_load() {
+	os=$(uname -s)
+	case $os in
+		Linux)
+			modprobe netmap
+			;;
+		FreeBSD)
+			# Nothing to do. We assume it is built in-kernel.
+			;;
+		*)
+			echo "$os not supported"
+			exit 1
+			;;
+	esac
+}
+
+################################################################################
+# Unloads the netmap module, if needed.
+# Arguments:
+#   None
+################################################################################
+function netmap_unload() {
+	os=$(uname -s)
+	case $os in
+		Linux)
+			rmmod netmap
+			;;
+		FreeBSD)
+			# Nothing to do.
+			;;
+		*)
+			echo "$os not supported"
+			exit 1
+			;;
+	esac
+}
diff --git a/utils/test_select.c b/utils/test_select.c
index ed737c3b8..644c0b60b 100644
--- a/utils/test_select.c
+++ b/utils/test_select.c
@@ -12,8 +12,9 @@
 #include 
 #include 
 
-enum { M_SELECT =0 , M_POLL, M_USLEEP };
+enum { M_SELECT = 0 , M_POLL, M_USLEEP };
 static const char *names[] = { "select", "poll", "usleep" };
+
 int
 main(int argc, char *argv[])
 {
diff --git a/utils/testmmap.c b/utils/testmmap.c
index 484724c14..c045f2881 100644
--- a/utils/testmmap.c
+++ b/utils/testmmap.c
@@ -1,29 +1,30 @@
 #define TEST_NETMAP
 
+#include 
+#include 
+#include  /* O_RDWR */
 #include 
-#include 	/* ULONG_MAX */
+#include 
+#include 
+#include 
 #include 
 #include 
 #include 
-#include 
-#include 
+#include   /* PROT_* */
+#include  /* ULONG_MAX */
 #include 
 #include 
-#include 	/* PROT_* */
-#include 	/* O_RDWR */
-#include 
-#include 
-#include 
-
+#include 
 
 #define MAX_VARS 100
 
 char *variables[MAX_VARS];
 int curr_var;
 
-#define VAR_FAILED ((void*)1)
+#define VAR_FAILED ((void *)1)
 
-char *firstarg(char *buf)
+char *
+firstarg(char *buf)
 {
 	int v;
 	char *arg = strtok(buf, " \t\n");
@@ -32,7 +33,7 @@ char *firstarg(char *buf)
 		return NULL;
 	if (arg[0] != '$' && arg[0] != '?')
 		return arg;
-	v = atoi(arg+1);
+	v = atoi(arg + 1);
 	if (v < 0 || v >= MAX_VARS)
 		return "";
 	ret = variables[v];
@@ -48,45 +49,49 @@ char *firstarg(char *buf)
 	return ret;
 }
 
-char *nextarg()
+char *
+nextarg()
 {
 	return firstarg(NULL);
 }
 
-char *restofline()
+char *
+restofline()
 {
 	return strtok(NULL, "\n");
 }
 
-void resetvar(int v, char *b)
+void
+resetvar(int v, char *b)
 {
 	if (variables[v] != VAR_FAILED)
 		free(variables[v]);
 	variables[v] = b;
 }
 
-#define outecho(format, args...) \
-	do {\
-		printf("%u:%lu: " format "\n", getpid(), (unsigned long) pthread_self(), ##args);\
-		fflush(stdout);\
+#define outecho(format, args...)                                               \
+	do {                                                                   \
+		printf("%u:%lu: " format "\n", getpid(),                       \
+		       (unsigned long)pthread_self(), ##args);                 \
+		fflush(stdout);                                                \
 	} while (0)
 
-#define output(format, args...) \
-	do {\
-		resetvar(curr_var, (char*)malloc(1024));\
-		snprintf(variables[curr_var], 1024, format, ##args);\
-		outecho(format, ##args);\
+#define output(format, args...)                                                \
+	do {                                                                   \
+		resetvar(curr_var, (char *)malloc(1024));                      \
+		snprintf(variables[curr_var], 1024, format, ##args);           \
+		outecho(format, ##args);                                       \
 	} while (0)
 
-#define output_err(ret, format, args...)\
-	do {\
-		if (ret < 0) {\
-			resetvar(curr_var, VAR_FAILED);\
-			outecho(format, ##args);\
-			outecho("error: %s", strerror(errno));\
-		} else {\
-			output(format, ##args);\
-		}\
+#define output_err(ret, format, args...)                                       \
+	do {                                                                   \
+		if ((ret) < 0) {                                               \
+			resetvar(curr_var, VAR_FAILED);                        \
+			outecho(format, ##args);                               \
+			outecho("error: %s", strerror(errno));                 \
+		} else {                                                       \
+			output(format, ##args);                                \
+		}                                                              \
 	} while (0)
 
 struct chan {
@@ -95,7 +100,8 @@ struct chan {
 	pthread_t tid;
 };
 
-int chan_search_free(struct chan* c[], int max)
+int
+chan_search_free(struct chan *c[], int max)
 {
 	int i;
 
@@ -105,7 +111,8 @@ int chan_search_free(struct chan* c[], int max)
 	return i;
 }
 
-void chan_clear_all(struct chan *c[], int max)
+void
+chan_clear_all(struct chan *c[], int max)
 {
 	int i;
 
@@ -118,44 +125,51 @@ void chan_clear_all(struct chan *c[], int max)
 	}
 }
 
-int last_fd = -1;
-size_t last_memsize = 0;
-void* last_mmap_addr = NULL;
-char* last_access_addr = NULL;
+int last_fd	    = -1;
+size_t last_memsize    = 0;
+void *last_mmap_addr   = NULL;
+char *last_access_addr = NULL;
 
-
-void do_open()
+void
+do_open()
 {
 	last_fd = open("/dev/netmap", O_RDWR);
 	output_err(last_fd, "open(\"/dev/netmap\", O_RDWR)=%d", last_fd);
 }
 
-void do_close()
+void
+do_close()
 {
 	int ret, fd;
 	char *arg = nextarg();
-	fd = arg ? atoi(arg) : last_fd;
-	ret = close(fd);
+	fd	= arg ? atoi(arg) : last_fd;
+	ret       = close(fd);
 	output_err(ret, "close(%d)=%d", fd, ret);
 }
 
 #ifdef TEST_NETMAP
-#include 
-#include 
 #include 
 #include 
+#include 
+#include 
+#include 
 
-struct nmreq curr_nmr = { .nr_version = NETMAP_API, .nr_flags = NR_REG_ALL_NIC, };
-char nmr_name[64];
+/* legacy */
+struct nmreq curr_nmr = {
+	.nr_version = 11,
+	.nr_flags   = NR_REG_ALL_NIC,
+};
+char nmr_name[256];
 
-void parse_nmr_config(char* w, struct nmreq *nmr)
+void
+parse_nmr_config(char *w, struct nmreq *nmr)
 {
 	char *tok;
 	int i, v;
 
 	nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
 	nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
-	if (w == NULL || ! *w)
+	if (w == NULL || !*w)
 		return;
 	for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
 		v = atoi(tok);
@@ -178,7 +192,8 @@ void parse_nmr_config(char* w, struct nmreq *nmr)
 	}
 }
 
-void do_getinfo()
+void
+do_getinfo_legacy()
 {
 	int ret;
 	char *arg, *name;
@@ -189,7 +204,7 @@ void do_getinfo()
 
 	name = nextarg();
 	if (name) {
-		strncpy(curr_nmr.nr_name, name, sizeof(curr_nmr.nr_name));
+		strncpy(curr_nmr.nr_name, name, sizeof(curr_nmr.nr_name)-1);
 	} else {
 		name = "any";
 	}
@@ -205,14 +220,14 @@ void do_getinfo()
 	parse_nmr_config(arg, &curr_nmr);
 
 doit:
-	ret = ioctl(fd, NIOCGINFO, &curr_nmr);
+	ret	  = ioctl(fd, NIOCGINFO, &curr_nmr);
 	last_memsize = curr_nmr.nr_memsize;
 	output_err(ret, "ioctl(%d, NIOCGINFO) for %s: region %d memsize=%zu",
-		fd, name, curr_nmr.nr_arg2, last_memsize);
+		   fd, name, curr_nmr.nr_arg2, last_memsize);
 }
 
-
-void do_regif()
+void
+do_regif_legacy()
 {
 	int ret;
 	char *arg, *name;
@@ -226,8 +241,8 @@ void do_regif()
 
 	bzero(&curr_nmr, sizeof(curr_nmr));
 	curr_nmr.nr_version = NETMAP_API;
-	curr_nmr.nr_flags = NR_REG_ALL_NIC;
-	strncpy(curr_nmr.nr_name, name, sizeof(curr_nmr.nr_name));
+	curr_nmr.nr_flags   = NR_REG_ALL_NIC;
+	strncpy(curr_nmr.nr_name, name, sizeof(curr_nmr.nr_name)-1);
 
 	arg = nextarg();
 	if (!arg) {
@@ -239,18 +254,18 @@ void do_regif()
 	parse_nmr_config(arg, &curr_nmr);
 
 doit:
-	ret = ioctl(fd, NIOCREGIF, &curr_nmr);
+	ret	  = ioctl(fd, NIOCREGIF, &curr_nmr);
 	last_memsize = curr_nmr.nr_memsize;
 	output_err(ret, "ioctl(%d, NIOCREGIF) for %s: region %d memsize=%zu",
-		fd, name, curr_nmr.nr_arg2, last_memsize);
+		   fd, name, curr_nmr.nr_arg2, last_memsize);
 }
 
 void
 do_txsync()
 {
 	char *arg = nextarg();
-	int fd = arg ? atoi(arg) : last_fd;
-	int ret = ioctl(fd, NIOCTXSYNC, NULL);
+	int fd    = arg ? atoi(arg) : last_fd;
+	int ret   = ioctl(fd, NIOCTXSYNC, NULL);
 	output_err(ret, "ioctl(%d, NIOCTXSYNC)=%d", fd, ret);
 }
 
@@ -258,15 +273,14 @@ void
 do_rxsync()
 {
 	char *arg = nextarg();
-	int fd = arg ? atoi(arg) : last_fd;
-	int ret = ioctl(fd, NIOCRXSYNC, NULL);
+	int fd    = arg ? atoi(arg) : last_fd;
+	int ret   = ioctl(fd, NIOCRXSYNC, NULL);
 	output_err(ret, "ioctl(%d, NIOCRXSYNC)=%d", fd, ret);
 }
 #endif /* TEST_NETMAP */
 
-
-volatile char tmp1;
-void do_access()
+void
+do_rd()
 {
 	char *arg = nextarg();
 	char *p;
@@ -280,13 +294,39 @@ void do_access()
 		p = (char *)strtoul((void *)arg, NULL, 0);
 	}
 	last_access_addr = p + 4096;
-	tmp1 = *p;
+	output("%2x", *p);
 }
 
-void do_dup()
+char *last_wr_byte = "x";
+void
+do_wr()
 {
 	char *arg = nextarg();
-	int fd = last_fd;
+	char *p;
+	if (!arg) {
+		if (!last_access_addr) {
+			output("missing address");
+			return;
+		}
+		p = last_access_addr;
+		last_access_addr += 4096;
+	} else {
+		p = (char *)strtoul((void *)arg, NULL, 0);
+	}
+	arg = nextarg();
+	if (!arg) {
+		arg = last_wr_byte;
+	}
+	for (; arg; arg = nextarg()) {
+		*p++ = strtoul((void *)arg, NULL, 0);
+	}
+}
+
+void
+do_dup()
+{
+	char *arg = nextarg();
+	int fd    = last_fd;
 	int ret;
 
 	if (arg) {
@@ -294,10 +334,10 @@ void do_dup()
 	}
 	ret = dup(fd);
 	output_err(ret, "dup(%d)=%d", fd, ret);
-
 }
 
-void do_mmap()
+void
+do_mmap()
 {
 	size_t memsize;
 	off_t off = 0;
@@ -307,33 +347,64 @@ void do_mmap()
 	arg = nextarg();
 	if (!arg) {
 		memsize = last_memsize;
-		fd = last_fd;
+		fd      = last_fd;
 		goto doit;
 	}
 	memsize = atoi(arg);
-	arg = nextarg();
+	arg     = nextarg();
 	if (!arg) {
 		fd = last_fd;
 		goto doit;
 	}
-	fd = atoi(arg);
+	fd  = atoi(arg);
 	arg = nextarg();
 	if (arg) {
 		off = (off_t)atol(arg);
 	}
 doit:
-	last_mmap_addr = mmap(0, memsize,
-			PROT_WRITE | PROT_READ,
-			MAP_SHARED, fd, off);
+	last_mmap_addr =
+		mmap(0, memsize, PROT_WRITE | PROT_READ, MAP_SHARED, fd, off);
 	if (last_access_addr == NULL)
 		last_access_addr = last_mmap_addr;
 	output_err(last_mmap_addr == MAP_FAILED ? -1 : 0,
-		"mmap(0, %zu, PROT_WRITE|PROT_READ, MAP_SHARED, %d, %jd)=%p",
-		memsize, fd, (intmax_t)off, last_mmap_addr);
+		   "mmap(0, %zu, PROT_WRITE|PROT_READ, MAP_SHARED, %d, %jd)=%p",
+		   memsize, fd, (intmax_t)off, last_mmap_addr);
+}
 
+#ifndef MAP_HUGETLB
+#define MAP_HUGETLB 0x40000
+#endif
+
+void
+do_anon_mmap()
+{
+	size_t memsize;
+	char *arg;
+	int flags = 0;
+
+	arg = nextarg();
+	if (!arg) {
+		memsize = last_memsize;
+		goto doit;
+	}
+	memsize = atoi(arg);
+	arg     = nextarg();
+	if (!arg)
+		goto doit;
+	flags |= MAP_HUGETLB;
+doit:
+	last_mmap_addr = mmap(0, memsize, PROT_WRITE | PROT_READ,
+			      MAP_SHARED | MAP_ANONYMOUS | flags, -1, 0);
+	if (last_access_addr == NULL)
+		last_access_addr = last_mmap_addr;
+	output_err(last_mmap_addr == MAP_FAILED ? -1 : 0,
+		   "mmap(0, %zu, PROT_WRITE|PROT_READ, "
+		   "MAP_SHARED|MAP_ANONYMOUS%s, -1, 0)=%p",
+		   memsize, (flags ? "|MAP_HUGETLB" : ""), last_mmap_addr);
 }
 
-void do_munmap()
+void
+do_munmap()
 {
 	void *mmap_addr;
 	size_t memsize;
@@ -343,11 +414,11 @@ void do_munmap()
 	arg = nextarg();
 	if (!arg) {
 		mmap_addr = last_mmap_addr;
-		memsize = last_memsize;
+		memsize   = last_memsize;
 		goto doit;
 	}
-	mmap_addr = (void*)strtoul(arg, NULL, 0);
-	arg = nextarg();
+	mmap_addr = (void *)strtoul(arg, NULL, 0);
+	arg       = nextarg();
 	if (!arg) {
 		memsize = last_memsize;
 		goto doit;
@@ -358,7 +429,8 @@ void do_munmap()
 	output_err(ret, "munmap(%p, %zu)=%d", mmap_addr, memsize, ret);
 }
 
-void do_poll()
+void
+do_poll()
 {
 	/* timeout fd fd... */
 	nfds_t nfds = 0, allocated_fds = 10, i;
@@ -375,11 +447,12 @@ void do_poll()
 		output_err(-1, "out of memory");
 		return;
 	}
-	while ( (arg = nextarg()) ) {
+	while ((arg = nextarg())) {
 		if (nfds >= allocated_fds) {
 			struct pollfd *new_fds;
 			allocated_fds *= 2;
-			new_fds = realloc(fds, allocated_fds * sizeof(struct pollfd));
+			new_fds = realloc(fds, allocated_fds *
+						       sizeof(struct pollfd));
 			if (new_fds == NULL) {
 				free(fds);
 				output_err(-1, "out of memory");
@@ -387,25 +460,23 @@ void do_poll()
 			}
 			fds = new_fds;
 		}
-		fds[nfds].fd = atoi(arg);
+		fds[nfds].fd     = atoi(arg);
 		fds[nfds].events = POLLIN;
 		nfds++;
 	}
 	ret = poll(fds, nfds, timeout);
 	for (i = 0; i < nfds; i++) {
 		output("poll(%d)=%s%s%s%s%s", fds[i].fd,
-			(fds[i].revents & POLLIN) ? "IN  " : "-   ",
-			(fds[i].revents & POLLOUT)? "OUT " : "-   ",
-			(fds[i].revents & POLLERR)? "ERR " : "-   ",
-			(fds[i].revents & POLLHUP)? "HUP " : "-   ",
-			(fds[i].revents & POLLNVAL)?"NVAL" : "-");
-
+		       (fds[i].revents & POLLIN) ? "IN  " : "-   ",
+		       (fds[i].revents & POLLOUT) ? "OUT " : "-   ",
+		       (fds[i].revents & POLLERR) ? "ERR " : "-   ",
+		       (fds[i].revents & POLLHUP) ? "HUP " : "-   ",
+		       (fds[i].revents & POLLNVAL) ? "NVAL" : "-");
 	}
 	output_err(ret, "poll(...)=%d", ret);
 	free(fds);
 }
 
-
 void
 do_expr()
 {
@@ -415,7 +486,7 @@ do_expr()
 	int err = 0;
 
 	stack[10] = ULONG_MAX;
-	while ( (arg = nextarg()) ) {
+	while ((arg = nextarg())) {
 		errno = 0;
 		char *rest;
 		unsigned long n = strtoul(arg, &rest, 0);
@@ -430,7 +501,7 @@ do_expr()
 		if (top <= 8) {
 			unsigned long n1 = stack[top++];
 			unsigned long n2 = stack[top++];
-			unsigned long r = 0;
+			unsigned long r  = 0;
 			switch (arg[0]) {
 			case '+':
 				r = n1 + n2;
@@ -446,7 +517,7 @@ do_expr()
 					r = n1 / n2;
 				else {
 					errno = EDOM;
-					err = -1;
+					err   = -1;
 				}
 				break;
 			default:
@@ -462,8 +533,6 @@ do_expr()
 	output_err(err, "expr=%lu", stack[top]);
 }
 
-
-
 void
 do_echo()
 {
@@ -481,7 +550,7 @@ do_vars()
 		const char *v = variables[i];
 		if (v == NULL)
 			continue;
-		printf("?%d\t%s\n", i, v == VAR_FAILED ?  "FAILED" : v);
+		printf("?%d\t%s\n", i, v == VAR_FAILED ? "FAILED" : v);
 	}
 }
 
@@ -493,12 +562,12 @@ get_if()
 	char *arg;
 
 	/* defaults */
-	off = curr_nmr.nr_offset;
+	off       = curr_nmr.nr_offset;
 	mmap_addr = last_mmap_addr;
 
 	/* first arg: if offset */
 	arg = nextarg();
-	if (!arg) {
+	if (!arg || (strcmp(arg, "-") == 0)) {
 		goto doit;
 	}
 	off = strtoul(arg, NULL, 0);
@@ -507,7 +576,7 @@ get_if()
 	if (!arg) {
 		goto doit;
 	}
-	mmap_addr = (void*)strtoul(arg, NULL, 0);
+	mmap_addr = (void *)strtoul(arg, NULL, 0);
 doit:
 	return NETMAP_IF(mmap_addr, off);
 }
@@ -531,12 +600,14 @@ do_if()
 		printf(" ]");
 	}
 	printf("\n");
-	printf("tx_rings   %u\n", nifp->ni_tx_rings);
-	printf("rx_rings   %u\n", nifp->ni_rx_rings);
-	printf("bufs_head  %u\n", nifp->ni_bufs_head);
-	for (i = 0; i < 5; i++)
+	printf("tx_rings        %u\n", nifp->ni_tx_rings);
+	printf("rx_rings        %u\n", nifp->ni_rx_rings);
+	printf("bufs_head       %u\n", nifp->ni_bufs_head);
+	printf("host_tx_rings   %u\n", nifp->ni_host_tx_rings);
+	printf("host_rx_rings   %u\n", nifp->ni_host_rx_rings);
+	for (i = 0; i < 3; i++)
 		printf("spare1[%d]  %u\n", i, nifp->ni_spare1[i]);
-	for (i = 0; i < (nifp->ni_tx_rings + nifp->ni_rx_rings + 2); i++)
+	for (i = 0; i < (nifp->ni_tx_rings + nifp->ni_rx_rings + nifp->ni_host_tx_rings + nifp->ni_host_rx_rings); i++)
 		printf("ring_ofs[%d] %zd\n", i, nifp->ring_ofs[i]);
 }
 
@@ -560,15 +631,10 @@ get_ring()
 	return NETMAP_TXRING(nifp, ringid);
 }
 
-
 void
-do_ring()
+dump_ring(struct netmap_ring *ring)
 {
-	struct netmap_ring *ring;
-
-	ring = get_ring();
-
-	printf("buf_ofs     %"PRId64"\n", ring->buf_ofs);
+	printf("buf_ofs     %" PRId64 "\n", ring->buf_ofs);
 	printf("num_slots   %u\n", ring->num_slots);
 	printf("nr_buf_size %u\n", ring->nr_buf_size);
 	printf("ringid      %d\n", ring->ringid);
@@ -586,8 +652,8 @@ do_ring()
 	}
 	printf("]\n");
 	printf("head        %u\n", ring->head);
-	printf("cur         %u\n", ring->head);
-	printf("tail        %u\n", ring->head);
+	printf("cur         %u\n", ring->cur);
+	printf("tail        %u\n", ring->tail);
 	printf("flags       %x", ring->flags);
 	if (ring->flags) {
 		printf(" [");
@@ -600,28 +666,51 @@ do_ring()
 		printf(" ]");
 	}
 	printf("\n");
-	printf("ts          %ld:%ld\n",
-			(long int)ring->ts.tv_sec, (long int)ring->ts.tv_usec);
+	printf("ts          %ld:%ld\n", (long int)ring->ts.tv_sec,
+	       (long int)ring->ts.tv_usec);
 }
 
 void
-do_slot()
+do_ring()
 {
 	struct netmap_ring *ring;
-	struct netmap_slot *slot;
-	long int index;
 	char *arg;
+	int upd = -1;
+	unsigned int v;
 
-	/* defaults */
-	index = 0;
+	ring = get_ring();
 
 	arg = nextarg();
-	if (!arg)
-		goto doit;
-	index = strtoll(arg, NULL, 0);
-doit:
-	ring = get_ring();
-	slot = ring->slot + index;
+	if (!arg) {
+		dump_ring(ring);
+		return;
+	}
+	if (strcmp(arg, "head") == 0) {
+		upd = 1;
+	} else if (strcmp(arg, "cur") == 0) {
+		upd = 2;
+	} else if (strcmp(arg, "both") == 0) {
+		upd = 3;
+	} else {
+		return;
+	}
+	arg = nextarg();
+	if (!arg) {
+		v = ring->cur + 1;
+		if (ring->cur >= ring->num_slots)
+			ring->cur = 0;
+	} else {
+		v = strtoul((void *)arg, NULL, 0);
+	}
+	if (upd & 1)
+		ring->head = v;
+	if (upd & 2)
+		ring->cur = v;
+}
+
+void
+dump_slot(struct netmap_slot *slot)
+{
 	printf("buf_idx       %u\n", slot->buf_idx);
 	printf("len           %u\n", slot->len);
 	printf("flags         %x", slot->flags);
@@ -645,12 +734,55 @@ do_slot()
 		if (slot->flags & NS_MOREFRAG) {
 			printf(" MOREFRAG");
 		}
+		if (NS_RFRAGS(slot)) {
+			printf(" fragments=%u", NS_RFRAGS(slot));
+		}
 		printf(" ]");
 	}
 	printf("\n");
 	printf("ptr           %lx\n", (long)slot->ptr);
 }
 
+void
+do_slot()
+{
+	struct netmap_ring *ring;
+	struct netmap_slot *slot;
+	long int index;
+	char *arg;
+
+	/* defaults */
+	index = 0;
+
+	arg = nextarg();
+	if (!arg)
+		goto doit;
+	index = strtoll(arg, NULL, 0);
+doit:
+	ring = get_ring();
+	slot = ring->slot + index;
+	arg  = nextarg();
+	if (!arg) {
+		dump_slot(slot);
+		return;
+	}
+	if (strcmp(arg, "buf_idx") == 0) {
+		arg = nextarg();
+		if (!arg) {
+			output("buf_idx=%u", slot->buf_idx);
+			return;
+		}
+		slot->buf_idx = strtoul((void *)arg, NULL, 0);
+	} else if (strcmp(arg, "len") == 0) {
+		arg = nextarg();
+		if (!arg) {
+			output("len=%u", slot->len);
+			return;
+		}
+		slot->len = strtoul((void *)arg, NULL, 0);
+	}
+}
+
 static void
 dump_payload(char *p, int len)
 {
@@ -658,15 +790,15 @@ dump_payload(char *p, int len)
 	int i, j, i0;
 
 	/* hexdump routine */
-	for (i = 0; i < len; ) {
-		memset(buf, sizeof(buf), ' ');
+	for (i = 0; i < len;) {
+		memset(buf, ' ', sizeof(buf));
 		sprintf(buf, "%5d: ", i);
 		i0 = i;
-		for (j=0; j < 16 && i < len; i++, j++)
-			sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i]));
+		for (j = 0; j < 16 && i < len; i++, j++)
+			sprintf(buf + 7 + j * 3, "%02x ", (uint8_t)(p[i]));
 		i = i0;
-		for (j=0; j < 16 && i < len; i++, j++)
-			sprintf(buf+7+j + 48, "%c",
+		for (j = 0; j < 16 && i < len; i++, j++)
+			sprintf(buf + 7 + j + 48, "%c",
 				isprint(p[i]) ? p[i] : '.');
 		printf("%s\n", buf);
 	}
@@ -681,7 +813,7 @@ do_buf()
 
 	/* defaults */
 	buf_idx = 2;
-	len = 64;
+	len     = 64;
 
 	arg = nextarg();
 	if (!arg)
@@ -694,7 +826,9 @@ do_buf()
 	len = strtoll(arg, NULL, 0);
 doit:
 	ring = get_ring();
-	buf = NETMAP_BUF(ring, buf_idx);
+	buf  = NETMAP_BUF(ring, buf_idx);
+	output("buf=%p", buf);
+	last_access_addr = buf;
 	dump_payload(buf, len);
 }
 
@@ -703,7 +837,8 @@ struct cmd_def {
 	void (*f)(void);
 };
 
-int _find_command(const struct cmd_def *cmds, int ncmds, const char* cmd)
+int
+_find_command(const struct cmd_def *cmds, int ncmds, const char *cmd)
 {
 	int i;
 	for (i = 0; i < ncmds; i++) {
@@ -713,11 +848,93 @@ int _find_command(const struct cmd_def *cmds, int ncmds, const char* cmd)
 	return i;
 }
 
+struct pools_info_field {
+	char *name;
+	size_t off;
+	size_t size;
+};
+#define PIFD(n, f)                                                             \
+	{                                                                      \
+		n, offsetof(struct nmreq_pools_info, nr_##f),                  \
+			sizeof(((struct nmreq_pools_info *)0)->nr_##f)         \
+	}
+struct pools_info_field pools_info_fields[] = {
+	PIFD("memsize", memsize),
+	PIFD("mem_id", mem_id),
+	PIFD("if-off", if_pool_offset),
+	PIFD("if-tot", if_pool_objtotal),
+	PIFD("if-siz", if_pool_objsize),
+	PIFD("ring-off", ring_pool_offset),
+	PIFD("ring-tot", ring_pool_objtotal),
+	PIFD("ring-siz", ring_pool_objsize),
+	PIFD("buf-off", buf_pool_offset),
+	PIFD("buf-tot", buf_pool_objtotal),
+	PIFD("buf-siz", buf_pool_objsize),
+	{NULL, 0, 0}};
+#define PIF(t, p, o) (*(t *)((void *)((char *)(p) + (o))))
+void
+pools_info_dump(int tab, struct nmreq_pools_info *upi)
+{
+	static const char space[] = "        ";
+	struct pools_info_field *f;
+	for (f = pools_info_fields; f->name; f++) {
+		printf("%.*s%-12s", tab, space, f->name);
+		switch (f->size) {
+		case 8:
+			printf("%" PRIu64 "\n", PIF(uint64_t, upi, f->off));
+			break;
+		case 4:
+			printf("%" PRIu32 "\n", PIF(uint32_t, upi, f->off));
+			break;
+		case 2:
+			printf("%" PRIu16 "\n", PIF(uint16_t, upi, f->off));
+			break;
+		}
+	}
+}
+
+static struct nmreq_pools_info curr_pools_info;
+
+/* prepare the curr_pools_info */
+void
+do_pools_info()
+{
+	char *cmd = nextarg();
+	unsigned long long v;
+
+	if (cmd == NULL) {
+		pools_info_dump(0, &curr_pools_info);
+		return;
+	}
+	struct pools_info_field *f = NULL;
+	for (f = pools_info_fields; f->name; f++) {
+		if (strcmp(f->name, cmd) == 0)
+			break;
+	}
+	if (f == NULL)
+		return;
+	cmd = nextarg();
+	if (cmd == NULL)
+		return;
+	v = strtoll(cmd, NULL, 0);
+	switch (f->size) {
+	case 8:
+		PIF(uint64_t, &curr_pools_info, f->off) = v;
+		break;
+	case 4:
+		PIF(uint32_t, &curr_pools_info, f->off) = v;
+		break;
+	case 2:
+		PIF(uint16_t, &curr_pools_info, f->off) = v;
+		break;
+	}
+}
+
 typedef void (*nmr_arg_interp_fun)();
 
-#define nmr_arg_unexpected(n) \
-	printf("arg%d:      %d%s\n", n, curr_nmr.nr_arg ## n, \
-		(curr_nmr.nr_arg ## n ? "???" : ""))
+#define nmr_arg_unexpected(n)                                                  \
+	printf("arg%d:      %d%s\n", n, curr_nmr.nr_arg##n,                    \
+	       (curr_nmr.nr_arg##n ? "???" : ""))
 
 void
 nmr_arg_bdg_attach()
@@ -799,17 +1016,17 @@ nmr_arg_error()
 void
 nmr_arg_extra()
 {
-	printf("arg1:      %d [%sextra rings]\n", curr_nmr.nr_arg1,
-		(curr_nmr.nr_arg1 ? "" : "no "));
+	printf("arg1:      %d [reserved]\n", curr_nmr.nr_arg1);
 	printf("arg2:      %d [%s memory allocator]\n", curr_nmr.nr_arg2,
-		(curr_nmr.nr_arg2 == 0 ? "default" :
-		 curr_nmr.nr_arg2 == 1 ? "global"  : "private"));
+	       (curr_nmr.nr_arg2 == 0
+			? "default"
+			: curr_nmr.nr_arg2 == 1 ? "global" : "private"));
 	printf("arg3:      %d [%sextra buffers]\n", curr_nmr.nr_arg3,
-		(curr_nmr.nr_arg3 ? "" : "no "));
+	       (curr_nmr.nr_arg3 ? "" : "no "));
 }
 
 void
-do_nmr_dump()
+do_nmr_legacy_dump()
 {
 	u_int ringid = curr_nmr.nr_ringid & NETMAP_RING_MASK;
 	nmr_arg_interp_fun arg_interp;
@@ -819,11 +1036,11 @@ do_nmr_dump()
 	printf("name:      %s\n", nmr_name);
 	printf("version:   %d\n", curr_nmr.nr_version);
 	printf("offset:    %d\n", curr_nmr.nr_offset);
-	printf("memsize:   %d [", curr_nmr.nr_memsize);
-	if (curr_nmr.nr_memsize < (1<<20)) {
-		printf("%d KiB", curr_nmr.nr_memsize >> 10);
+	printf("memsize:   %u [", curr_nmr.nr_memsize);
+	if (curr_nmr.nr_memsize < (1 << 20)) {
+		printf("%u KiB", curr_nmr.nr_memsize >> 10);
 	} else {
-		printf("%d MiB", curr_nmr.nr_memsize >> 20);
+		printf("%u MiB", curr_nmr.nr_memsize >> 20);
 	}
 	printf("]\n");
 	printf("tx_slots:  %d\n", curr_nmr.nr_tx_slots);
@@ -944,25 +1161,24 @@ do_nmr_dump()
 	if (curr_nmr.nr_flags & NR_EXCLUSIVE) {
 		printf(", EXCLUSIVE");
 	}
-	if (curr_nmr.nr_flags & NR_PTNETMAP_HOST) {
-		printf(", PTNETMAP_HOST");
-	}
 	printf("]\n");
 	printf("spare2[0]: %x\n", curr_nmr.spare2[0]);
 }
 
 void
-do_nmr_reset()
+do_nmr_legacy_reset()
 {
 	bzero(&curr_nmr, sizeof(curr_nmr));
+	curr_nmr.nr_version = NETMAP_API;
+	curr_nmr.nr_flags   = NR_REG_ALL_NIC;
 }
 
 void
-do_nmr_name()
+do_nmr_legacy_name()
 {
 	char *name = nextarg();
 	if (name) {
-		strncpy(curr_nmr.nr_name, name, IFNAMSIZ);
+		strncpy(curr_nmr.nr_name, name, IFNAMSIZ-1);
 	}
 	strncpy(nmr_name, curr_nmr.nr_name, IFNAMSIZ);
 	nmr_name[IFNAMSIZ] = '\0';
@@ -970,7 +1186,7 @@ do_nmr_name()
 }
 
 void
-do_nmr_ringid()
+do_nmr_legacy_ringid()
 {
 	char *arg;
 	uint16_t ringid = curr_nmr.nr_ringid;
@@ -995,7 +1211,7 @@ do_nmr_ringid()
 }
 
 void
-do_nmr_cmd()
+do_nmr_legacy_cmd()
 {
 	char *arg = nextarg();
 	if (arg == NULL)
@@ -1029,7 +1245,7 @@ do_nmr_cmd()
 }
 
 void
-do_nmr_flags()
+do_nmr_legacy_flags()
 {
 	char *arg;
 	uint32_t flags = curr_nmr.nr_flags;
@@ -1061,8 +1277,6 @@ do_nmr_flags()
 			flags |= NR_ZCOPY_MON;
 		} else if (strcmp(arg, "exclusive") == 0) {
 			flags |= NR_EXCLUSIVE;
-		} else if (strcmp(arg, "ptnetmap-host") == 0) {
-			flags |= NR_PTNETMAP_HOST;
 		} else if (strcmp(arg, "default") == 0) {
 			flags = 0;
 		}
@@ -1072,130 +1286,761 @@ do_nmr_flags()
 	output("flags=%x", curr_nmr.nr_flags);
 }
 
-struct cmd_def nmr_commands[] = {
-	{ "dump",	do_nmr_dump },
-	{ "reset",	do_nmr_reset },
-	{ "name",	do_nmr_name },
-	{ "ringid",	do_nmr_ringid },
-	{ "cmd",	do_nmr_cmd },
-	{ "flags",	do_nmr_flags },
+struct cmd_def nmr_legacy_commands[] = {
+	{"dump", do_nmr_legacy_dump}, {"reset", do_nmr_legacy_reset},
+	{"name", do_nmr_legacy_name}, {"ringid", do_nmr_legacy_ringid},
+	{"cmd", do_nmr_legacy_cmd},   {"flags", do_nmr_legacy_flags},
 };
 
-const int N_NMR_CMDS = sizeof(nmr_commands) / sizeof(struct cmd_def);
+const int N_NMR_LEGACY_CMDS =
+	sizeof(nmr_legacy_commands) / sizeof(struct cmd_def);
 
 int
-find_nmr_command(const char *cmd)
-{
-	return _find_command(nmr_commands, N_NMR_CMDS, cmd);
-}
-
-#define nmr_arg_update(f) 				\
-	({						\
-		int __ret = 0;				\
-		if (strcmp(cmd, #f) == 0) {		\
-			char *arg = nextarg();		\
-			if (arg) {			\
-				curr_nmr.nr_##f = strtol(arg, NULL, 0); \
-			}				\
-			output(#f "=%d", curr_nmr.nr_##f);	\
-			__ret = 1;			\
-		} 					\
-		__ret;					\
+find_nmr_legacy_command(const char *cmd)
+{
+	return _find_command(nmr_legacy_commands, N_NMR_LEGACY_CMDS, cmd);
+}
+
+#define __nmr_arg_update(nmr, f)                                               \
+	({                                                                     \
+		int __ret = 0;                                                 \
+		if (strcmp(cmd, #f) == 0) {                                    \
+			char *arg = nextarg();                                 \
+			if (arg) {                                             \
+				curr_##nmr.nr_##f = strtol(arg, NULL, 0);      \
+			}                                                      \
+			output(#f "=%llu",                                     \
+			       (unsigned long long)curr_##nmr.nr_##f);         \
+			__ret = 1;                                             \
+		}                                                              \
+		__ret;                                                         \
 	})
 
+#define nmr_arg_update(f) __nmr_arg_update(nmr, f)
+
 /* prepare the curr_nmr */
 void
-do_nmr()
+do_nmr_legacy()
 {
 	char *cmd = nextarg();
 	int i;
 
 	if (cmd == NULL) {
-		do_nmr_dump();
+		do_nmr_legacy_dump();
 		return;
 	}
 	if (cmd[0] == '.') {
 		cmd++;
 	} else {
-		i = find_nmr_command(cmd);
-		if (i < N_NMR_CMDS) {
-			nmr_commands[i].f();
+		i = find_nmr_legacy_command(cmd);
+		if (i < N_NMR_LEGACY_CMDS) {
+			nmr_legacy_commands[i].f();
 			return;
 		}
 	}
-	if (nmr_arg_update(version) ||
-	    nmr_arg_update(offset) ||
-	    nmr_arg_update(memsize) ||
-	    nmr_arg_update(tx_slots) ||
-	    nmr_arg_update(rx_slots) ||
-	    nmr_arg_update(tx_rings) ||
-	    nmr_arg_update(rx_rings) ||
-	    nmr_arg_update(ringid) ||
-	    nmr_arg_update(cmd) ||
-	    nmr_arg_update(arg1) ||
-	    nmr_arg_update(arg2) ||
-	    nmr_arg_update(arg3) ||
+	if (nmr_arg_update(version) || nmr_arg_update(offset) ||
+	    nmr_arg_update(memsize) || nmr_arg_update(tx_slots) ||
+	    nmr_arg_update(rx_slots) || nmr_arg_update(tx_rings) ||
+	    nmr_arg_update(rx_rings) || nmr_arg_update(ringid) ||
+	    nmr_arg_update(cmd) || nmr_arg_update(arg1) ||
+	    nmr_arg_update(arg2) || nmr_arg_update(arg3) ||
 	    nmr_arg_update(flags))
 		return;
 	output("unknown field: %s", cmd);
 }
 
+/****************************************************************
+ * new API							*
+ ****************************************************************/
 
+static struct nmreq_header curr_hdr = {.nr_version = NETMAP_API};
+static struct nmreq_register curr_register;
+static struct nmreq_port_info_get curr_port_info_get;
+static struct nmreq_vale_attach curr_vale_attach;
+static struct nmreq_vale_list curr_vale_list;
+static struct nmreq_port_hdr curr_port_hdr;
+static struct nmreq_vale_newif curr_vale_newif;
+static struct nmreq_vale_polling curr_vale_polling;
+
+typedef void (*nmr_body_dump_fun)(void *);
+
+static void
+nmr_body_dump_register(void *b)
+{
+	struct nmreq_register *r = b;
+	int flags		 = 0;
+	printf("offset:         %" PRIu64 "\n", r->nr_offset);
+	printf("memsize:        %" PRIu64 " [", r->nr_memsize);
+	if (r->nr_memsize < (1 << 20)) {
+		printf("%" PRIu64 " KiB", r->nr_memsize >> 10);
+	} else {
+		printf("%" PRIu64 " MiB", r->nr_memsize >> 20);
+	}
+	printf("]\n");
+	printf("tx_slots:       %" PRIu16 "\n", r->nr_tx_slots);
+	printf("rx_slots:       %" PRIu16 "\n", r->nr_rx_slots);
+	printf("tx_rings:       %" PRIu16 "\n", r->nr_tx_rings);
+	printf("rx_rings:       %" PRIu16 "\n", r->nr_rx_rings);
+	printf("host_tx_rings:  %" PRIu16 "\n", r->nr_host_tx_rings);
+	printf("host_rx_rings:  %" PRIu16 "\n", r->nr_host_rx_rings);
+	printf("mem_id:         %" PRIu16 " [%s memory region]\n", r->nr_mem_id,
+	       (r->nr_mem_id == 0 ? "default"
+				  : r->nr_mem_id == 1 ? "global" : "private"));
+	printf("ringid          %" PRIu16 "\n", r->nr_ringid);
+	printf("mode            %" PRIu32 " [", r->nr_mode);
+	switch (r->nr_mode) {
+	case NR_REG_DEFAULT:
+		printf("*DEFAULT");
+		break;
+	case NR_REG_ALL_NIC:
+		printf("ALL_NIC");
+		break;
+	case NR_REG_SW:
+		printf("SW");
+		break;
+	case NR_REG_NIC_SW:
+		printf("NIC_SW");
+		break;
+	case NR_REG_ONE_NIC:
+		printf("ONE_NIC(%" PRIu16 ")", r->nr_ringid);
+		break;
+	case NR_REG_PIPE_MASTER:
+		printf("*PIPE_MASTER(%d)", r->nr_ringid);
+		break;
+	case NR_REG_PIPE_SLAVE:
+		printf("*PIPE_SLAVE(%d)", r->nr_ringid);
+		break;
+	case NR_REG_NULL:
+		printf("NULL");
+		break;
+	case NR_REG_ONE_SW:
+		printf("ONE_SW(%d)", r->nr_ringid);
+		break;
+	default:
+		printf("???");
+		break;
+	}
+	printf("]\n");
+	printf("flags:     %" PRIx64 " [", r->nr_flags);
+#define pflag(f)                                                               \
+	if (r->nr_flags & NR_##f) {                                            \
+		printf("%s" #f, flags++ ? ", " : "");                          \
+	}
+	pflag(MONITOR_TX);
+	pflag(MONITOR_RX);
+	pflag(ZCOPY_MON);
+	pflag(EXCLUSIVE);
+	pflag(RX_RINGS_ONLY);
+	pflag(TX_RINGS_ONLY);
+	pflag(ACCEPT_VNET_HDR);
+	pflag(DO_RX_POLL);
+	pflag(NO_TX_POLL);
+#undef pflag
+	printf("]\n");
+	printf("extra_bufs %" PRIu32 "\n", r->nr_extra_bufs);
+}
 
-struct cmd_def commands[] = {
-	{ "open",	do_open,	},
-	{ "close", 	do_close,	},
+static void
+do_register_dump()
+{
+	nmr_body_dump_register(&curr_register);
+}
+
+static void
+do_register_reset()
+{
+	memset(&curr_register, 0, sizeof(curr_register));
+}
+
+static void
+do_register_mode()
+{
+	char *mode = nextarg();
+
+	if (mode == NULL)
+		goto out;
+
+	if (strcmp(mode, "default") == 0) {
+		curr_register.nr_mode = NR_REG_DEFAULT;
+	} else if (strcmp(mode, "all-nic") == 0) {
+		curr_register.nr_mode = NR_REG_ALL_NIC;
+	} else if (strcmp(mode, "sw") == 0) {
+		curr_register.nr_mode = NR_REG_SW;
+	} else if (strcmp(mode, "nic-sw") == 0) {
+		curr_register.nr_mode = NR_REG_NIC_SW;
+	} else if (strcmp(mode, "one-nic") == 0) {
+		curr_register.nr_mode = NR_REG_ONE_NIC;
+	} else if (strcmp(mode, "pipe-master") == 0) {
+		curr_register.nr_mode = NR_REG_PIPE_MASTER;
+	} else if (strcmp(mode, "pipe-slave") == 0) {
+		curr_register.nr_mode = NR_REG_PIPE_SLAVE;
+	} else if (strcmp(mode, "null") == 0) {
+		curr_register.nr_mode = NR_REG_NULL;
+	} else if (strcmp(mode, "one-sw") == 0) {
+		curr_register.nr_mode = NR_REG_ONE_SW;
+	}
+
+out:
+	output("mode=%" PRIu32, curr_register.nr_mode);
+}
+
+void
+do_register_flags()
+{
+	char *arg;
+	uint64_t flags = curr_register.nr_flags;
+	int n;
+	for (n = 0, arg = nextarg(); arg; arg = nextarg(), n++) {
+		if (strcmp(arg, "monitor-tx") == 0) {
+			flags |= NR_MONITOR_TX;
+		} else if (strcmp(arg, "monitor-rx") == 0) {
+			flags |= NR_MONITOR_RX;
+		} else if (strcmp(arg, "zcopy-mon") == 0) {
+			flags |= NR_ZCOPY_MON;
+		} else if (strcmp(arg, "exclusive") == 0) {
+			flags |= NR_EXCLUSIVE;
+		} else if (strcmp(arg, "rx-rings-only") == 0) {
+			flags |= NR_RX_RINGS_ONLY;
+		} else if (strcmp(arg, "tx-rings-only") == 0) {
+			flags |= NR_TX_RINGS_ONLY;
+		} else if (strcmp(arg, "accept-vnet-hdr") == 0) {
+			flags |= NR_ACCEPT_VNET_HDR;
+		} else if (strcmp(arg, "do-rx-poll") == 0) {
+			flags |= NR_DO_RX_POLL;
+		} else if (strcmp(arg, "no-tx-poll") == 0) {
+			flags |= NR_NO_TX_POLL;
+		} else if (strcmp(arg, "reset") == 0) {
+			flags = 0;
+		}
+	}
+	if (n)
+		curr_register.nr_flags = flags;
+	output("flags=%" PRIx64, curr_register.nr_flags);
+}
+
+struct cmd_def register_commands[] = {
+	{"dump", do_register_dump},
+	{"reset", do_register_reset},
+	{"mode", do_register_mode},
+	{"flags", do_register_flags},
+};
+
+const int N_REGISTER_CMDS = sizeof(register_commands) / sizeof(struct cmd_def);
+
+int
+find_register_command(const char *cmd)
+{
+	return _find_command(register_commands, N_REGISTER_CMDS, cmd);
+}
+
+#define register_update(f) __nmr_arg_update(register, f)
+
+void
+do_register()
+{
+	char *cmd = nextarg();
+	int i;
+
+	if (cmd == NULL) {
+		do_register_dump();
+		return;
+	}
+	if (cmd[0] == '.') {
+		cmd++;
+	} else {
+		i = find_register_command(cmd);
+		if (i < N_REGISTER_CMDS) {
+			register_commands[i].f();
+			return;
+		}
+	}
+	if (register_update(offset) || register_update(memsize) ||
+	    register_update(tx_slots) || register_update(rx_slots) ||
+	    register_update(tx_rings) || register_update(rx_rings) ||
+	    register_update(host_tx_rings) || register_update(host_rx_rings) ||
+	    register_update(mem_id) || register_update(ringid) ||
+	    register_update(mode) || register_update(flags) ||
+	    register_update(extra_bufs))
+		return;
+	output("unknown field: %s", cmd);
+}
+
+static void
+nmr_body_dump_port_info_get(void *b)
+{
+	struct nmreq_port_info_get *r = b;
+	int i;
+
+	printf("memsize:        %" PRIu64 " [", r->nr_memsize);
+	if (r->nr_memsize < (1 << 20)) {
+		printf("%" PRIu64 " KiB", r->nr_memsize >> 10);
+	} else {
+		printf("%" PRIu64 " MiB", r->nr_memsize >> 20);
+	}
+	printf("]\n");
+	printf("tx_slots:       %" PRIu16 "\n", r->nr_tx_slots);
+	printf("rx_slots:       %" PRIu16 "\n", r->nr_rx_slots);
+	printf("tx_rings:       %" PRIu16 "\n", r->nr_tx_rings);
+	printf("rx_rings:       %" PRIu16 "\n", r->nr_rx_rings);
+	printf("host_tx_rings:  %" PRIu16 "\n", r->nr_host_tx_rings);
+	printf("host_rx_rings:  %" PRIu16 "\n", r->nr_host_rx_rings);
+	printf("mem_id:         %" PRIu16 " [%s memory region]\n", r->nr_mem_id,
+	       (r->nr_mem_id == 0 ? "default"
+				  : r->nr_mem_id == 1 ? "global" : "private"));
+	for (i = 0; i < 3; i++)
+		printf("pad[%d]         %" PRIu16 "\n", i, r->pad[i]);
+}
+
+static void
+nmr_body_dump_vale_attach(void *b)
+{
+	(void)b;
+}
+
+static void
+nmr_body_dump_vale_list(void *b)
+{
+	(void)b;
+}
+
+static void
+nmr_body_dump_port_hdr(void *b)
+{
+	(void)b;
+}
+
+static void
+nmr_body_dump_vale_newif(void *b)
+{
+	(void)b;
+}
+
+static void
+nmr_body_dump_vale_polling(void *b)
+{
+	(void)b;
+}
+
+static void
+nmr_body_dump_pools_info_get(void *b)
+{
+	(void)b;
+}
+
+typedef void (*nmr_option_dump_fun)(struct nmreq_option *);
+
+static void
+nmr_option_dump_extmem(struct nmreq_option *opt)
+{
+	struct nmreq_opt_extmem *e = (struct nmreq_opt_extmem *)opt;
+
+	printf("usrptr: %p\n", (void *)(uintptr_t)e->nro_usrptr);
+	printf("info:\n");
+	pools_info_dump(4, &e->nro_info);
+}
+
+static void
+nmr_option_dump(struct nmreq_option *opt)
+{
+	nmr_option_dump_fun d = NULL;
+
+	printf("next: %p\n", (void *)(uintptr_t)opt->nro_next);
+	printf("type: %" PRIu32 " [", opt->nro_reqtype);
+	switch (opt->nro_reqtype) {
+	case NETMAP_REQ_OPT_EXTMEM:
+		printf("extmem");
+		d = nmr_option_dump_extmem;
+		break;
+	default:
+#ifdef NETMAP_OPT_DEBUG
+		if (opt->nro_reqtype & NETMAP_REQ_OPT_DEBUG) {
+			printf("debug: %u",
+			       (opt->nro_reqtype & ~NETMAP_REQ_OPT_DEBUG));
+			break;
+		}
+#endif /* NETMAP_OPT_DEBUG */
+		printf("???");
+	}
+	printf("]\n");
+	printf("status: %" PRIu32 " [%s]\n", opt->nro_status,
+	       strerror(opt->nro_status));
+	if (d)
+		d(opt);
+}
+
+static void
+do_hdr_dump()
+{
+	struct nmreq_option *opt;
+	nmr_body_dump_fun body_dump = NULL;
+
+	snprintf(nmr_name, NETMAP_REQ_IFNAMSIZ + 1, "%s", curr_hdr.nr_name);
+	nmr_name[NETMAP_REQ_IFNAMSIZ] = '\0';
+	printf("version:   %d\n", curr_hdr.nr_version);
+	printf("reqtype:   %d [", curr_hdr.nr_reqtype);
+	switch (curr_hdr.nr_reqtype) {
+	case NETMAP_REQ_REGISTER:
+		printf("register");
+		body_dump = nmr_body_dump_register;
+		break;
+	case NETMAP_REQ_PORT_INFO_GET:
+		printf("info-get");
+		body_dump = nmr_body_dump_port_info_get;
+		break;
+	case NETMAP_REQ_VALE_ATTACH:
+		printf("vale-attach");
+		body_dump = nmr_body_dump_vale_attach;
+		break;
+	case NETMAP_REQ_VALE_DETACH:
+		printf("vale-detach");
+		break;
+	case NETMAP_REQ_VALE_LIST:
+		printf("vale-list");
+		body_dump = nmr_body_dump_vale_list;
+		break;
+	case NETMAP_REQ_PORT_HDR_SET:
+		printf("port-hdr-set");
+		body_dump = nmr_body_dump_port_hdr;
+		break;
+	case NETMAP_REQ_PORT_HDR_GET:
+		printf("port-hdr-get");
+		body_dump = nmr_body_dump_port_hdr;
+		break;
+	case NETMAP_REQ_VALE_NEWIF:
+		printf("vale-newif");
+		body_dump = nmr_body_dump_vale_newif;
+		break;
+	case NETMAP_REQ_VALE_DELIF:
+		printf("vale-delif");
+		break;
+	case NETMAP_REQ_VALE_POLLING_ENABLE:
+		printf("vale-polliing-enable");
+		body_dump = nmr_body_dump_vale_polling;
+		break;
+	case NETMAP_REQ_VALE_POLLING_DISABLE:
+		printf("vale-polling-disable");
+		body_dump = nmr_body_dump_vale_polling;
+		break;
+	case NETMAP_REQ_POOLS_INFO_GET:
+		printf("pools-info-get");
+		body_dump = nmr_body_dump_pools_info_get;
+		break;
+	default:
+		printf("???");
+		break;
+	}
+	printf("]\n");
+	printf("name: %s\n", nmr_name);
+	opt = (struct nmreq_option *)(uintptr_t)curr_hdr.nr_options;
+	printf("options:   %p\n", opt);
+	while (opt) {
+		nmr_option_dump(opt);
+		opt = (struct nmreq_option *)(uintptr_t)opt->nro_next;
+	}
+	printf("body:	   %p\n", (void *)(uintptr_t)curr_hdr.nr_body);
+	if (body_dump)
+		body_dump((void *)(uintptr_t)curr_hdr.nr_body);
+}
+
+static void
+do_hdr_reset()
+{
+	struct nmreq_option *opt = (struct nmreq_option *)(uintptr_t)curr_hdr.nr_options;
+	while (opt) {
+		struct nmreq_option *next =
+			(struct nmreq_option *)(uintptr_t)opt->nro_next;
+		free(opt);
+		opt = next;
+	}
+	memset(&curr_hdr, 0, sizeof(curr_hdr));
+	curr_hdr.nr_version = NETMAP_API;
+}
+
+void
+do_hdr_name()
+{
+	char *name = nextarg();
+	if (name) {
+		strncpy(curr_hdr.nr_name, name, NETMAP_REQ_IFNAMSIZ-1);
+	}
+	strncpy(nmr_name, curr_hdr.nr_name, NETMAP_REQ_IFNAMSIZ);
+	nmr_name[NETMAP_REQ_IFNAMSIZ] = '\0';
+	output("name=%s", nmr_name);
+}
+
+static void
+do_hdr_type()
+{
+	char *type = nextarg();
+
+	if (strcmp(type, "register") == 0) {
+		curr_hdr.nr_reqtype = NETMAP_REQ_REGISTER;
+		curr_hdr.nr_body    = (uintptr_t)&curr_register;
+	} else if (strcmp(type, "info-get") == 0) {
+		curr_hdr.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
+		curr_hdr.nr_body    = (uintptr_t)&curr_port_info_get;
+	} else if (strcmp(type, "vale-attach") == 0) {
+		curr_hdr.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
+		curr_hdr.nr_body    = (uintptr_t)&curr_vale_attach;
+	} else if (strcmp(type, "vale-detach") == 0) {
+		curr_hdr.nr_reqtype = NETMAP_REQ_VALE_DETACH;
+	} else if (strcmp(type, "vale-list") == 0) {
+		curr_hdr.nr_reqtype = NETMAP_REQ_VALE_LIST;
+		curr_hdr.nr_body    = (uintptr_t)&curr_vale_list;
+	} else if (strcmp(type, "port-hdr-set") == 0) {
+		curr_hdr.nr_reqtype = NETMAP_REQ_PORT_HDR_SET;
+		curr_hdr.nr_body    = (uintptr_t)&curr_port_hdr;
+	} else if (strcmp(type, "port-hdr-get") == 0) {
+		curr_hdr.nr_reqtype = NETMAP_REQ_PORT_HDR_GET;
+		curr_hdr.nr_body    = (uintptr_t)&curr_port_hdr;
+	} else if (strcmp(type, "vale-newif") == 0) {
+		curr_hdr.nr_reqtype = NETMAP_REQ_VALE_NEWIF;
+		curr_hdr.nr_body    = (uintptr_t)&curr_vale_newif;
+	} else if (strcmp(type, "vale-delif") == 0) {
+		curr_hdr.nr_reqtype = NETMAP_REQ_VALE_DELIF;
+	} else if (strcmp(type, "vale-polliing-enable") == 0) {
+		curr_hdr.nr_reqtype = NETMAP_REQ_VALE_POLLING_ENABLE;
+		curr_hdr.nr_body    = (uintptr_t)&curr_vale_polling;
+	} else if (strcmp(type, "vale-polling-disable") == 0) {
+		curr_hdr.nr_reqtype = NETMAP_REQ_VALE_POLLING_DISABLE;
+		curr_hdr.nr_body    = (uintptr_t)&curr_vale_polling;
+	} else if (strcmp(type, "pools-info-get") == 0) {
+		curr_hdr.nr_reqtype = NETMAP_REQ_POOLS_INFO_GET;
+		curr_hdr.nr_body    = (uintptr_t)&curr_pools_info;
+	} else {
+		output("unknown type: %s", type);
+	}
+	output("type=%u", curr_hdr.nr_reqtype);
+}
+
+typedef void (*nmreq_opt_init)(struct nmreq_option *);
+
+static void
+nmreq_opt_extmem_init(struct nmreq_option *opt)
+{
+	struct nmreq_opt_extmem *e = (struct nmreq_opt_extmem *)opt;
+	e->nro_usrptr		   = (uintptr_t)last_mmap_addr;
+	e->nro_info.nr_memsize     = last_memsize;
+}
+
+static void
+do_hdr_option()
+{
+	char *type;
+	struct nmreq_option **ptr = (struct nmreq_option **)&curr_hdr
+					    .nr_options,
+			    *old = *ptr;
+	size_t sz		 = sizeof(struct nmreq_option);
+	nmreq_opt_init init      = NULL;
+
+	while ((type = nextarg())) {
+		uint16_t reqtype = 0;
+
+		if (strcmp(type, "extmem") == 0) {
+			reqtype = NETMAP_REQ_OPT_EXTMEM;
+			sz      = sizeof(struct nmreq_opt_extmem);
+			init    = nmreq_opt_extmem_init;
+#ifdef NETMAP_OPT_DEBUG
+		} else {
+			reqtype = strtol(type, NULL, 0) | NETMAP_REQ_OPT_DEBUG;
+#endif /* NETMAP_OPT_DEBUG */
+		}
+		*ptr = malloc(sz);
+		if (*ptr == NULL) {
+			output_err(-1, "malloc");
+		}
+		memset(*ptr, 0, sz);
+		(*ptr)->nro_reqtype = reqtype;
+		if (init)
+			init(*ptr);
+		ptr = (struct nmreq_option **)&(*ptr)->nro_next;
+	}
+	*ptr = old;
+}
+
+struct cmd_def hdr_commands[] = {
+	{"dump", do_hdr_dump}, {"reset", do_hdr_reset},   {"name", do_hdr_name},
+	{"type", do_hdr_type}, {"option", do_hdr_option},
+};
+
+const int N_HDR_CMDS = sizeof(hdr_commands) / sizeof(struct cmd_def);
+
+int
+find_hdr_command(const char *cmd)
+{
+	return _find_command(hdr_commands, N_HDR_CMDS, cmd);
+}
+
+static void
+do_hdr()
+{
+	char *cmd = nextarg();
+	int i;
+
+	if (cmd == NULL) {
+		do_hdr_dump();
+		return;
+	}
+	i = find_hdr_command(cmd);
+	if (i < N_HDR_CMDS) {
+		hdr_commands[i].f();
+		return;
+	}
+	output("unknown command: %s", cmd);
+}
+
+static void
+do_ctrl()
+{
+	char *arg;
+	int fd, ret;
+
+	arg = nextarg();
+	if (!arg) {
+		fd = last_fd;
+		goto doit;
+	}
+	last_fd = fd = atoi(arg);
+doit:
+	ret = ioctl(fd, NIOCCTRL, &curr_hdr);
+	switch (curr_hdr.nr_reqtype) {
+	case NETMAP_REQ_REGISTER:
+		last_memsize = curr_register.nr_memsize;
+		break;
+	case NETMAP_REQ_PORT_INFO_GET:
+		last_memsize = curr_port_info_get.nr_memsize;
+		break;
+	default:
+		break;
+	}
+	output_err(ret, "ioctl(%d, NIOCCTL, %p)=%d", fd, &curr_hdr, ret);
+}
+
+struct cmd_def commands[] = {{
+				     "open",
+				     do_open,
+			     },
+			     {
+				     "close",
+				     do_close,
+			     },
 #ifdef TEST_NETMAP
-	{ "getinfo",	do_getinfo,	},
-	{ "regif",	do_regif,	},
-	{ "txsync",	do_txsync,	},
-	{ "rxsync",	do_rxsync,	},
+			     {
+				     "getinfo-legacy",
+				     do_getinfo_legacy,
+			     },
+			     {
+				     "regif-legacy",
+				     do_regif_legacy,
+			     },
+			     {
+				     "txsync",
+				     do_txsync,
+			     },
+			     {
+				     "rxsync",
+				     do_rxsync,
+			     },
 #endif /* TEST_NETMAP */
-	{ "dup",	do_dup,		},
-	{ "mmap",	do_mmap,	},
-	{ "access",	do_access,	},
-	{ "munmap",	do_munmap,	},
-	{ "poll",	do_poll,	},
-	{ "expr",	do_expr,	},
-	{ "echo",	do_echo,	},
-	{ "vars",	do_vars,	},
-	{ "if",         do_if,          },
-	{ "ring",       do_ring,        },
-	{ "slot",       do_slot,        },
-	{ "buf",        do_buf,         },
-	{ "nmr",	do_nmr,		}
-};
+			     {
+				     "dup",
+				     do_dup,
+			     },
+			     {
+				     "mmap",
+				     do_mmap,
+			     },
+			     {
+				     "anon-mmap",
+				     do_anon_mmap,
+			     },
+			     {
+				     "rd",
+				     do_rd,
+			     },
+			     {
+				     "wr",
+				     do_wr,
+			     },
+			     {
+				     "munmap",
+				     do_munmap,
+			     },
+			     {
+				     "poll",
+				     do_poll,
+			     },
+			     {
+				     "expr",
+				     do_expr,
+			     },
+			     {
+				     "echo",
+				     do_echo,
+			     },
+			     {
+				     "vars",
+				     do_vars,
+			     },
+			     {
+				     "if",
+				     do_if,
+			     },
+			     {
+				     "ring",
+				     do_ring,
+			     },
+			     {
+				     "slot",
+				     do_slot,
+			     },
+			     {
+				     "buf",
+				     do_buf,
+			     },
+			     {
+				     "nmr-legacy",
+				     do_nmr_legacy,
+			     },
+			     {
+				     "hdr",
+				     do_hdr,
+			     },
+			     {"ctrl", do_ctrl},
+			     {"register", do_register}};
 
 const int N_CMDS = sizeof(commands) / sizeof(struct cmd_def);
 
-int find_command(const char* cmd)
+int
+find_command(const char *cmd)
 {
 	return _find_command(commands, N_CMDS, cmd);
 }
 
 #define MAX_CHAN 10
 
-void prompt()
+void
+prompt(FILE *f)
 {
-	if (isatty(STDIN_FILENO)) {
+	if (isatty(fileno(f))) {
 		printf("> ");
 	}
 }
 
 struct chan *channels[MAX_CHAN];
 
-void*
+void *
 thread_cmd_loop(void *arg)
 {
 	char buf[1024];
-	FILE *in = (FILE*)arg;
+	FILE *in = (FILE *)arg;
 
 	while (fgets(buf, 1024, in)) {
 		char *cmd;
 		int i;
 
 		cmd = firstarg(buf);
-		i = find_command(cmd);
+		i   = find_command(cmd);
 		if (i < N_CMDS) {
 			commands[i].f();
 			continue;
@@ -1206,13 +2051,14 @@ thread_cmd_loop(void *arg)
 	return NULL;
 }
 
-void do_exit()
+void
+do_exit()
 {
 	output("quit");
 }
 
 void
-cmd_loop()
+cmd_loop(FILE *input)
 {
 	char buf[1024];
 	int i;
@@ -1222,7 +2068,7 @@ cmd_loop()
 
 	atexit(do_exit);
 
-	for (prompt(); fgets(buf, 1024, stdin); prompt()) {
+	for (prompt(input); fgets(buf, 1024, input); prompt(input)) {
 		char *cmd;
 		int slot;
 
@@ -1241,16 +2087,17 @@ cmd_loop()
 		}
 
 		if (strcmp(cmd, "fork") == 0) {
-			int slot = chan_search_free(channels, MAX_CHAN);
+			int slot       = chan_search_free(channels, MAX_CHAN);
 			struct chan *c = NULL;
 			pid_t pid;
-			int p1[2] = { -1, -1};
+			int p1[2] = {-1, -1};
 
 			if (slot == MAX_CHAN) {
 				output("too many channels");
 				continue;
 			}
-			c = channels[slot] = (struct chan*)malloc(sizeof(struct chan));
+			c = channels[slot] =
+				(struct chan *)malloc(sizeof(struct chan));
 			if (c == NULL) {
 				output_err(-1, "malloc");
 				continue;
@@ -1271,13 +2118,8 @@ cmd_loop()
 				output_err(-1, "fork");
 				goto clean1;
 			case 0:
-				fclose(stdin);
-				if (dup(p1[0]) < 0) {
-					output_err(-1, "dup");
-					exit(1);
-				}
 				close(p1[1]);
-				stdin = fdopen(0, "r");
+				input = fdopen(p1[0], "r");
 				chan_clear_all(channels, MAX_CHAN);
 				goto out;
 			default:
@@ -1310,7 +2152,7 @@ cmd_loop()
 				output("invalid slot: %s", cmd);
 				continue;
 			}
-			c = channels[slot];
+			c   = channels[slot];
 			ret = kill(c->pid, SIGTERM);
 			output_err(ret, "kill(%d, SIGTERM)=%d", c->pid, ret);
 			if (ret != -1) {
@@ -1322,10 +2164,10 @@ cmd_loop()
 			continue;
 		}
 		if (strcmp(cmd, "thread") == 0) {
-			int slot = chan_search_free(channels, MAX_CHAN);
+			int slot       = chan_search_free(channels, MAX_CHAN);
 			struct chan *c = NULL;
 			pthread_t tid;
-			int p1[2] = { -1, -1};
+			int p1[2] = {-1, -1};
 			int ret;
 			FILE *in = NULL;
 
@@ -1333,7 +2175,8 @@ cmd_loop()
 				output("too many channels");
 				continue;
 			}
-			c = channels[slot] = (struct chan*)malloc(sizeof(struct chan));
+			c = channels[slot] =
+				(struct chan *)malloc(sizeof(struct chan));
 			bzero(c, sizeof(*c));
 			if (pipe(p1) < 0) {
 				output_err(-1, "pipe");
@@ -1351,7 +2194,7 @@ cmd_loop()
 			}
 			ret = pthread_create(&tid, NULL, thread_cmd_loop, in);
 			output_err(ret, "pthread_create() tid=%lu slot=%d",
-				(unsigned long) tid, slot);
+				   (unsigned long)tid, slot);
 			if (ret < 0)
 				goto clean2;
 			c->pid = getpid();
@@ -1382,13 +2225,16 @@ cmd_loop()
 			fclose(c->out);
 			ret = pthread_join(c->tid, NULL);
 			output_err(ret, "pthread_join(%lu)=%d",
-				(unsigned long) c->tid, ret);
+				   (unsigned long)c->tid, ret);
 			if (ret > 0) {
 				free(c);
 				channels[slot] = NULL;
 			}
 			continue;
 		}
+		if (strcmp(cmd, "next") == 0) {
+			return;
+		}
 		i = find_command(cmd);
 		if (i < N_CMDS) {
 			commands[i].f();
@@ -1413,9 +2259,25 @@ cmd_loop()
 int
 main(int argc, char **argv)
 {
-	(void) argc;
-	(void) argv;
-	printf("testmmap\n");
-	cmd_loop();
+	int i;
+	if (argc > 1) {
+		for (i = 1; i < argc; i++) {
+			FILE *f;
+			if (!strcmp(argv[i], "-")) {
+				f = stdin;
+			} else {
+				f = fopen(argv[i], "r");
+				if (f == NULL) {
+					perror(argv[i]);
+					continue;
+				}
+			}
+			cmd_loop(f);
+			if (f != stdin)
+				fclose(f);
+		}
+	} else {
+		cmd_loop(stdin);
+	}
 	return 0;
 }
diff --git a/utils/testmod/kern_test.c b/utils/testmod/kern_test.c
index 8773a6c2f..408c4092e 100644
--- a/utils/testmod/kern_test.c
+++ b/utils/testmod/kern_test.c
@@ -90,7 +90,7 @@ SYSCTL_ULONG(_kern_test, OID_AUTO, count,
 SYSCTL_ULONG(_kern_test, OID_AUTO, cycles,
     CTLFLAG_RW, &t_delta, 0, "runtime");
 SYSCTL_STRING(_kern_test, OID_AUTO, name,
-	CTLFLAG_RW, &test_name, sizeof(test_name), "");
+	CTLFLAG_RW, &test_name, sizeof(test_name), "name of the test");
 SYSCTL_PROC(_kern_test, OID_AUTO, run,
     CTLTYPE_U64 | CTLFLAG_RW, 0, 0, test_run,
     "U64", "run the test");
diff --git a/utils/tests/001_exclusive_open_ephemeral_vale_port_test b/utils/tests/001_exclusive_open_ephemeral_vale_port_test
new file mode 100755
index 000000000..7f8598fc5
--- /dev/null
+++ b/utils/tests/001_exclusive_open_ephemeral_vale_port_test
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check that an ephemeral VALE port opened with the exclusive
+#                 flag can't be opened again.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+
+bridge="vale0"
+port="v0"
+
+# We open ${bridge}:${port} with the exclusive flag from the file descriptor.
+$FUNCTIONAL $verbosity -i "${bridge}:${port}/x"
+check_success $? "exclusive-open ${bridge}:${port}/x"
+
+# Then we open the same interface again, this time without requesting it from
+# the file descriptor, causing a second nm_open().
+$FUNCTIONAL $verbosity -I "${bridge}:${port}"
+check_failure $? "no-open ${bridge}:${port}"
+
+# Check that another exclusive open request fails.
+$FUNCTIONAL $verbosity -I "${bridge}:${port}/x"
+check_failure $? "no-open ${bridge}:${port}/x"
diff --git a/utils/tests/002_exclusive_open_persistent_vale_port_test b/utils/tests/002_exclusive_open_persistent_vale_port_test
new file mode 100755
index 000000000..04a6bc762
--- /dev/null
+++ b/utils/tests/002_exclusive_open_persistent_vale_port_test
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check that an persistent VALE port opened with the exclusive
+#                 flag can't be opened again.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+
+bridge="vale0"
+port="v0"
+
+create_vale_persistent_port "$port"
+attach_to_vale_bridge "$bridge" "$port"
+
+# We open the persistent port with the exclusive flag from the file descriptor.
+$FUNCTIONAL $verbosity -i "${bridge}:${port}/x"
+check_success $? "exclusive-open ${bridge}:${port}/x"
+
+# Then we open the same interface again, this time without requesting it from
+# the file descriptor, causing a second nm_open().
+$FUNCTIONAL $verbosity -I "${bridge}:${port}"
+check_failure $? "no-open ${bridge}:${port}"
+
+# Check that another exclusive open request fails.
+$FUNCTIONAL $verbosity -I "${bridge}:${port}/x"
+check_failure $? "no-open ${bridge}:${port}/x"
diff --git a/utils/tests/003_exclusive_open_pipe_test b/utils/tests/003_exclusive_open_pipe_test
new file mode 100755
index 000000000..bf627651d
--- /dev/null
+++ b/utils/tests/003_exclusive_open_pipe_test
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check that a netmap pipe opened with the exclusive flag can't
+#                 be opened again.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+
+pipe="pipeA{1"
+
+# We open pipeA{1 with the exclusive flag from the file descriptor.
+$FUNCTIONAL $verbosity -i "netmap:${pipe}/x"
+check_success $? "exclusive-open netmap:${pipe}/x"
+
+# Then we open the same interface again, this time without requesting it from
+# the file descriptor, causing a second nm_open().
+$FUNCTIONAL $verbosity -I "netmap:${pipe}"
+check_failure $? "no-open netmap:${pipe}"
+
+# Check that another exclusive open request fails.
+$FUNCTIONAL $verbosity -I "netmap:${pipe}/x"
+check_failure $? "no-open netmap:${pipe}/x"
diff --git a/utils/tests/004_extra_buf_send_rec_ephemeral_vale_ports_test b/utils/tests/004_extra_buf_send_rec_ephemeral_vale_ports_test
new file mode 100755
index 000000000..05c256f35
--- /dev/null
+++ b/utils/tests/004_extra_buf_send_rec_ephemeral_vale_ports_test
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if we can send packets through ephimeral VALE ports
+#                 while using extra buffers.
+# Operations:
+# 1) send from vale0:v0 using extra buffers, and check that both, vale0:v1 and
+#    vale0:v2, receive.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-c}"
+len="${len:-274}"
+num="${num:-1}"
+seq="${seq:-}"
+
+e_buf_num="${e_buf_num:-12}"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "vale0:v1"
+check_success $? "pre-open vale0:v1"
+$FUNCTIONAL $verbosity -i "vale0:v2"
+check_success $? "pre-open vale0:v2"
+
+# v0 ---> v1, v2
+$FUNCTIONAL $verbosity -i "vale0:v1" -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i "vale0:v2" -r "${len}:${fill}:${num}" $seq &
+p2=$!
+$FUNCTIONAL $verbosity -I "vale0:v0" -t "${len}:${fill}:${num}" -e "$e_buf_num" $seq
+e3=$?
+wait $p1
+e1=$?
+wait $p2
+e2=$?
+check_success $e1 "receive-${num} vale0:v1"
+check_success $e2 "receive-${num} vale0:v2"
+check_success $e3 "send-${num} vale0:v0"
diff --git a/utils/tests/005_extra_buf_send_rec_persistent_vale_ports_test b/utils/tests/005_extra_buf_send_rec_persistent_vale_ports_test
new file mode 100755
index 000000000..878b35643
--- /dev/null
+++ b/utils/tests/005_extra_buf_send_rec_persistent_vale_ports_test
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if we can send packets through persistent VALE ports
+#                 while using extra buffers.
+# Operations:
+# 1) create 3 persistent VALE ports (v0, v1, v2) and attach them to vale0.
+# 2) send from vale0:v2 using extra buffers, and check that both, vale0:v0 and
+#    vale0:v1, receive.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-c}"
+len="${len:-274}"
+num="${num:-1}"
+seq="${seq:-}"
+
+e_buf_num="${e_buf_num:-12}"
+
+create_vale_persistent_port "v0"
+create_vale_persistent_port "v1"
+create_vale_persistent_port "v2"
+attach_to_vale_bridge "vale0" "v0"
+attach_to_vale_bridge "vale0" "v1"
+attach_to_vale_bridge "vale0" "v2"
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "vale0:v0"
+check_success $? "pre-open vale0:v0"
+$FUNCTIONAL $verbosity -i "vale0:v1"
+check_success $? "pre-open vale0:v1"
+
+# v2 ---> v0, v1
+$FUNCTIONAL $verbosity -i "vale0:v0" -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i "vale0:v1" -r "${len}:${fill}:${num}" $seq &
+p2=$!
+$FUNCTIONAL $verbosity -I "vale0:v2" -t "${len}:${fill}:${num}" -e "$e_buf_num" $seq
+e3=$?
+wait $p1
+e1=$?
+wait $p2
+e2=$?
+check_success $e1 "receive-${num} vale0:v0"
+check_success $e2 "receive-${num} vale0:v1"
+check_success $e3 "send-${num} vale0:v2"
diff --git a/utils/tests/006_extra_buf_send_rec_pipe_test b/utils/tests/006_extra_buf_send_rec_pipe_test
new file mode 100755
index 000000000..469988002
--- /dev/null
+++ b/utils/tests/006_extra_buf_send_rec_pipe_test
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if we can send packets through netmap pipes while using
+#                 extra buffers.
+# Operations:
+# 1) create a pair of netmap pipes (pipeA{1, pipeA}1).
+# 2) send from pipeA{1 using extra buffers and check if pipeA}1 receives.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-c}"
+len="${len:-274}"
+num="${num:-1}"
+seq="${seq:-}"
+
+e_buf_num="${e_buf_num:-12}"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "netmap:pipeA{1"
+check_success $? "pre-open netmap:pipeA{1"
+
+# pipeA}1 ---> pipeA{1
+$FUNCTIONAL $verbosity -i "netmap:pipeA{1" -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -I "netmap:pipeA}1" -t "${len}:${fill}:${num}" -e "$e_buf_num" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "receive-${num} netmap:pipeA{1"
+check_success $e2 "send-${num} netmap:pipeA}1"
diff --git a/utils/tests/007_learning_bridge_test b/utils/tests/007_learning_bridge_test
new file mode 100755
index 000000000..29708562e
--- /dev/null
+++ b/utils/tests/007_learning_bridge_test
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if the switch learning algorithm is working.
+# Operations:
+# 1) connect 3 ephemeral VALE ports (v0, v1, v2) to the same VALE switch.
+# 2) send from v2 specifying a source MAC address and check that v0 and v1
+#    receive the frame.
+# 3) send from v0 using the previous MAC address as the destination MAC address
+#    and check that v2 receives the frame while v1 does not.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+
+s_MAC=$(get_random_MAC)
+d_MAC="FF:FF:FF:FF:FF:FF"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i vale0:v0
+check_success $? "pre-open vale0:v0"
+$FUNCTIONAL $verbosity -i vale0:v1
+check_success $? "pre-open vale0:v1"
+$FUNCTIONAL $verbosity -i vale0:v2
+check_success $? "pre-open vale0:v2"
+
+# First send, every port should receive the frame.
+$FUNCTIONAL $verbosity -i vale0:v0 -r "${len}:${fill}" -s "$s_MAC" -d "$d_MAC" &
+p1=$!
+$FUNCTIONAL $verbosity -i vale0:v1 -r "${len}:${fill}" -s "$s_MAC" -d "$d_MAC" &
+p2=$!
+$FUNCTIONAL $verbosity -i vale0:v2 -t "${len}:${fill}" -s "$s_MAC" -d "$d_MAC"
+e3=$?
+wait $p1
+e1=$?
+wait $p2
+e2=$?
+check_success $e1 "receive vale0:v0"
+check_success $e2 "receive vale0:v1"
+check_success $e3 "send vale0:v2"
+
+# Second send, only v2 should receive the frame.
+$FUNCTIONAL $verbosity -i vale0:v2 -r "${len}:${fill}" -d "$s_MAC"    &
+p4=$!
+$FUNCTIONAL $verbosity -i vale0:v1 -r "${len}:${fill}" -d "$s_MAC" -n &
+p5=$!
+$FUNCTIONAL $verbosity -i vale0:v0 -t "${len}:${fill}" -d "$s_MAC"
+e6=$?
+wait $p4
+e4=$?
+wait $p5
+e5=$?
+check_success $e1 "receive vale0:v0"
+check_success $e2 "receive vale0:v1"
+check_success $e3 "send vale0:v2"
diff --git a/utils/tests/008_partial_read_pipe_test b/utils/tests/008_partial_read_pipe_test
new file mode 100755
index 000000000..058421747
--- /dev/null
+++ b/utils/tests/008_partial_read_pipe_test
@@ -0,0 +1,67 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check that if the receiving pipe receives less than the
+#                 sending pipe sent, the non-received slot are left inside the
+#                 sending pipe ring.
+# Operations:
+# 1) create a pair of netmap pipes (pipe{1, pipe}1).
+# 2) send X packets to pipe}1 and receive X-Y packets from pipe{1.
+# 2) check that pipe{1 still has X-Y slots pending for transmission.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+seq="${seq:-}"
+
+fill='h'
+len=274
+num_send=10
+num_recv=7
+pipe="pipeA"
+
+# Pre-open netmap ports for the test. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "netmap:${pipe}{1"
+check_success $? "pre-open netmap:${pipe}{1"
+$FUNCTIONAL $verbosity -i "netmap:${pipe}}1"
+check_success $? "pre-open netmap:${pipe}}1"
+
+$FUNCTIONAL $verbosity -i "netmap:${pipe}{1" -r "${len}:${fill}:${num_recv}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i "netmap:${pipe}}1" -t "${len}:${fill}:${num_send}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "receive-7 netmap:${pipe}{1"
+check_success $e2 "send-10 netmap:${pipe}}1"
+
+# At the moment get_max_tx_packets and get_avail_tx_packets do not get the
+# netmap port fd from fd_server. They request it directly through nm_open().
+# However, they still read the correct values stored inside each struct
+# netmap ring, because the netmap ports have not been closed in the meanwhile.
+exit_status=0
+max_packets=$(get_max_tx_packets "netmap:${pipe}}1" "$len")
+if [ "$max_packets" == -1 ] ; then
+	exit_status=1
+fi
+check_success $exit_status "get_max_tx_packets netmap:${pipe}}1 $len"
+
+exit_status=0
+avail_packets=$(get_avail_tx_packets "netmap:${pipe}}1" "$len")
+if [ "$avail_packets" == -1 ] ; then
+	exit_status=1
+fi
+check_success $exit_status "get_avail_tx_packets netmap:${pipe}}1 $len"
+
+exit_status=0
+pending_packets="$(($max_packets - $avail_packets))"
+pending_transmissions="$(($num_send - $num_recv))"
+if [ $pending_packets != $pending_transmissions ] ; then
+	exit_status=1
+fi
+check_exit $pending_transmissions $pending_packets "pending_transmissions=pending_packets"
+
+num_send="$(($avail_packets + 1))"
+$FUNCTIONAL $verbosity -i "netmap:${pipe}}1" -t "${len}:${fill}:${num_send}" $seq
+check_failure $? "send-${num_send} netmap:${pipe}}1"
diff --git a/utils/tests/009_persistent_vale_port_destroy b/utils/tests/009_persistent_vale_port_destroy
new file mode 100755
index 000000000..48f94f7fa
--- /dev/null
+++ b/utils/tests/009_persistent_vale_port_destroy
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check that we can't create two persistent VALE ports with the
+#                 same name.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+
+bridge="vale0"
+bridgeA="${bridge}A"
+bridgeB="${bridge}B"
+port="v0"
+
+create_vale_persistent_port "$port" 0
+destroy_vale_persistent_port "$port" 0
+
+create_vale_persistent_port "$port" 0
+
+attach_to_vale_bridge "$bridgeA" "$port" 0
+destroy_vale_persistent_port "$port" 1
+
+attach_to_vale_bridge "$bridgeB" "$port" 0
+destroy_vale_persistent_port "$port" 1
+
+detach_from_vale_bridge "$bridgeB" "$port" 0
+destroy_vale_persistent_port "$port" 1
+
+detach_from_vale_bridge "$bridgeA" "$port" 0
+destroy_vale_persistent_port "$port" 0
diff --git a/utils/tests/010_persistent_vale_port_double_attach b/utils/tests/010_persistent_vale_port_double_attach
new file mode 100755
index 000000000..8b2d260ad
--- /dev/null
+++ b/utils/tests/010_persistent_vale_port_double_attach
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check that a persistent VALE port can't be attached 2 times
+#                 to the same VALE bridge, but can be attached to 2 different
+#                 bridges.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+
+bridge="vale0"
+bridgeA="${bridge}A"
+bridgeB="${bridge}B"
+port="v0"
+
+create_vale_persistent_port "$port" 0
+attach_to_vale_bridge "$bridgeA" "$port" 0
+attach_to_vale_bridge "$bridgeA" "$port" 1
+attach_to_vale_bridge "$bridgeB" "$port" 0
diff --git a/utils/tests/011_persistent_vale_port_double_create b/utils/tests/011_persistent_vale_port_double_create
new file mode 100755
index 000000000..6da7c5cc9
--- /dev/null
+++ b/utils/tests/011_persistent_vale_port_double_create
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check that we can't create two persistent VALE ports with the
+#                 same name.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+
+bridge="vale0"
+bridgeA="${bridge}A"
+bridgeB="${bridge}B"
+port="v0"
+
+create_vale_persistent_port "$port" 0
+create_vale_persistent_port "$port" 1
+
+attach_to_vale_bridge "$bridgeA" "$port" 0
+create_vale_persistent_port "$port" 1
+
+attach_to_vale_bridge "$bridgeB" "$port" 0
+create_vale_persistent_port "$port" 1
diff --git a/utils/tests/012_rec_cp_mon_ephemeral_vale_port_test b/utils/tests/012_rec_cp_mon_ephemeral_vale_port_test
new file mode 100755
index 000000000..6e58966c9
--- /dev/null
+++ b/utils/tests/012_rec_cp_mon_ephemeral_vale_port_test
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if a receive copy monitor receives frames when its
+#                 monitored ephemeral VALE port is receiving, even if the
+#                 monitored port hasn't yet read the frame.
+# Operations:
+# 1) connect two ephemeral VALE ports (v0, v1) to the same VALE switch.
+# 2) open a receiving copy monitor vo/r for v0.
+# 3) send from v1 and don't read from v0, check that v0/r receives the frame.
+# 4) receive from v0.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i vale0:v0
+check_success $? "pre-open vale0:v0"
+$FUNCTIONAL $verbosity -i vale0:v0/r
+check_success $? "pre-open vale0:v0/r"
+$FUNCTIONAL $verbosity -i vale0:v1
+check_success $? "pre-open vale0:v1"
+
+# First we send without reading from v0
+$FUNCTIONAL $verbosity -i vale0:v0/r -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i vale0:v1    -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "receive-${num} vale0:v0/r"
+check_success $e2 "send-${num} vale0:v1"
+
+# Then we read from v0
+$FUNCTIONAL $verbosity -i vale0:v0 -r "${len}:${fill}:${num}" $seq
+e3=$?
+check_success $e3 "receive-${num} vale0:v0"
diff --git a/utils/tests/013_rec_cp_mon_persistent_vale_port_test b/utils/tests/013_rec_cp_mon_persistent_vale_port_test
new file mode 100755
index 000000000..72e1c82a7
--- /dev/null
+++ b/utils/tests/013_rec_cp_mon_persistent_vale_port_test
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if a receive copy monitor receives frames when its
+#                 monitored persistent VALE port is receiving, even if the
+#                 monitored port hasn't yet read the frame.
+# Operations:
+# 1) create a persistent VALE port (v0).
+# 2) connect v0 and a VALE ephimeral port (v1) to the same VALE switch.
+# 3) open a receiving copy monitor vo/r for v0.
+# 4) send from v1 and don't read from v0, check that v0/r receives the frame.
+# 5) receive from v0.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+create_vale_persistent_port "v0"
+attach_to_vale_bridge "vale0" "v0"
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i vale0:v0
+check_success $? "pre-open vale0:v0"
+$FUNCTIONAL $verbosity -i netmap:v0/r
+check_success $? "pre-open netmap:v0/r"
+$FUNCTIONAL $verbosity -i vale0:v1
+check_success $? "pre-open vale0:v1"
+
+# First we send without reading from v0
+$FUNCTIONAL $verbosity -i netmap:v0/r -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i vale0:v1    -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "receive-${num} netmap:v0/r"
+check_success $e2 "send-${num} vale0:v1"
+
+# Then we read from v0
+$FUNCTIONAL $verbosity -i vale0:v0 -r "${len}:${fill}:${num}" $seq
+e3=$?
+check_success $e3 "receive-${num} vale0:v0"
diff --git a/utils/tests/014_rec_cp_mon_pipe_test b/utils/tests/014_rec_cp_mon_pipe_test
new file mode 100755
index 000000000..7c9e17c63
--- /dev/null
+++ b/utils/tests/014_rec_cp_mon_pipe_test
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if a receive copy monitor receives frames when its
+#                 monitored netmap pipe is receiving, even if the monitored pipe
+#                 hasn't yet read the frame.
+# Operations:
+# 1) create a pair of netmap pipes (pipe{1, pipe}1).
+# 2) open a receive copy monitor pipe{1/r for pipe{1.
+# 3) send from pipe}1 and don't read from pipe{1, check that pipe{1/r receives
+#    the frame.
+# 4) receive from pipe{1.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "netmap:pipe{1"
+check_success $? "pre-open netmap:pipe{1"
+$FUNCTIONAL $verbosity -i "netmap:pipe{1/r"
+check_success $? "pre-open netmap:pipe{1/r"
+$FUNCTIONAL $verbosity -i "netmap:pipe}1"
+check_success $? "pre-open netmap:pipe}1"
+
+# First we send without reading from pipe{1
+$FUNCTIONAL $verbosity -i "netmap:pipe{1/r" -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i "netmap:pipe}1"   -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "receive-${num}${seq} netmap:pipe{1/r"
+check_success $e2 "send-${num}${seq} netmap:pipe}1"
+
+# Then we read from pipe{1
+$FUNCTIONAL $verbosity -i "netmap:pipe{1"   -r "${len}:${fill}:${num}" $seq
+e3=$?
+check_success $e3 "receive-${num}${seq} netmap:pipe{1"
diff --git a/utils/tests/015_rec_zcp_mon_ephemeral_vale_port_test b/utils/tests/015_rec_zcp_mon_ephemeral_vale_port_test
new file mode 100755
index 000000000..78b9a5fc0
--- /dev/null
+++ b/utils/tests/015_rec_zcp_mon_ephemeral_vale_port_test
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if a zero-copy monitor is correctly blocked until the
+#                 monitored ephemeral VALE port reads the frame.
+# Operations:
+# 1) connect two ephemeral VALE ports (v0, v1) to the same VALE switch.
+# 2) open a zero-copy monitor v0/z for v0.
+# 3) send from v1 without receiving from v0, check that v0/z doesn't receive the
+#    rame.
+# 4) receive from v0, check that v0/z receives the frame.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "vale0:v0"
+check_success $? "pre-open vale0:v0"
+$FUNCTIONAL $verbosity -i "vale0:v0/z"
+check_success $? "pre-open vale0:v0/z"
+$FUNCTIONAL $verbosity -i "vale0:v1"
+check_success $? "pre-open vale0:v1"
+
+# Initially we don't receive with the monitored VALE port v0, therefore the
+# monitor should not receive the frame.
+$FUNCTIONAL $verbosity -i vale0:v0/z -r "${len}:${fill}:${num}" $seq -n &
+p1=$!
+$FUNCTIONAL $verbosity -i vale0:v1   -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "no-receive-${num} vale0:v0/z"
+check_success $e2 "send-${num} vale0:v0"
+
+# Now we receive with the monitored VALE port v0, therefore the monitor should
+# receive the frame.
+$FUNCTIONAL $verbosity -i "vale0:v0"   -r "${len}:${fill}:${num}" $seq &
+p3=$!
+$FUNCTIONAL $verbosity -i "vale0:v0/z" -r "${len}:${fill}:${num}" $seq
+e4=$?
+wait $p3
+e3=$?
+check_success $e3 "receive-${num} vale0:v0"
+check_success $e4 "receive-${num} vale0:v0/z"
diff --git a/utils/tests/016_rec_zcp_mon_persistent_vale_port_test b/utils/tests/016_rec_zcp_mon_persistent_vale_port_test
new file mode 100755
index 000000000..0edbe9460
--- /dev/null
+++ b/utils/tests/016_rec_zcp_mon_persistent_vale_port_test
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if a zero-copy monitor is correctly blocked until the
+#                 monitored persistent VALE port reads the frame.
+# Operations:
+# 1) create a persistent VALE port (v0).
+# 2) connect v0 and a VALE ephimeral port (v1) to the same VALE switch (vale0).
+# 3) open a zero-copy monitor v0/z for v0.
+# 4) send from v1 without receiving from v0, check that v0/z doesn't receive the
+#    rame.
+# 5) receive from v0, check that v0/z receives the frame.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+create_vale_persistent_port "v0"
+attach_to_vale_bridge "vale0" "v0"
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "vale0:v0"
+check_success $? "pre-open vale0:v0"
+$FUNCTIONAL $verbosity -i "netmap:v0/z"
+check_success $? "pre-open netmap:v0/z"
+$FUNCTIONAL $verbosity -i "vale0:v1"
+check_success $? "pre-open vale0:v1"
+
+# Initially we don't receive with the monitored VALE port v0, therefore the
+# monitor should not receive the frame.
+$FUNCTIONAL $verbosity -i netmap:v0/z -r "${len}:${fill}:${num}" $seq -n &
+p1=$!
+$FUNCTIONAL $verbosity -i vale0:v1    -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "no-receive-${num} netmap:v0/z"
+check_success $e2 "send-${num} vale0:v0"
+
+# Now we receive with the monitored VALE port v0, therefore the monitor should
+# receive the frame.
+$FUNCTIONAL $verbosity -i "vale0:v0"    -r "${len}:${fill}:${num}" $seq &
+p3=$!
+$FUNCTIONAL $verbosity -i "netmap:v0/z" -r "${len}:${fill}:${num}" $seq
+e4=$?
+wait $p3
+e3=$?
+check_success $e3 "receive-${num} vale0:v0"
+check_success $e4 "receive-${num} netmap:v0/z"
diff --git a/utils/tests/017_rec_zcp_mon_pipe_test b/utils/tests/017_rec_zcp_mon_pipe_test
new file mode 100755
index 000000000..f44929015
--- /dev/null
+++ b/utils/tests/017_rec_zcp_mon_pipe_test
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if a zero-copy monitor is correctly blocked until the
+#                 monitored netmap pipe reads the frame.
+# Operations:
+# 1) create a pair of netmap pipes (pipe{1, pipe}1).
+# 2) open a zero-copy monitor pipe{1/z for pipe{1.
+# 3) send from pipe}1 without receiving from pipe{1, check that pipe{1/z doesn't
+#    receive the frame.
+# 4) receive from pipe{1, check that pipe{1/z receives the frame.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "netmap:pipe{1"
+check_success $? "pre-open netmap:pipe{1"
+$FUNCTIONAL $verbosity -i "netmap:pipe{1/z"
+check_success $? "pre-open netmap:pipe{1/z"
+$FUNCTIONAL $verbosity -i "netmap:pipe}1"
+check_success $? "pre-open netmap:pipe}1"
+
+# Initially we don't receive with the monitored pipe pipe{1, therefore the
+# monitor should not receive the frame.
+$FUNCTIONAL $verbosity -i "netmap:pipe{1/z" -r "${len}:${fill}:${num}" $seq -n &
+p1=$!
+$FUNCTIONAL $verbosity -i "netmap:pipe}1"   -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "no-receive-${num} netmap:pipe{1"
+check_success $e2 "send-${num} netmap:pipe{1"
+
+# Now we receive with the monitored pipe pipe{1, therefore the monitor should
+# receive the frame.
+$FUNCTIONAL $verbosity -i "netmap:pipe{1"   -r "${len}:${fill}:${num}" $seq &
+p3=$!
+$FUNCTIONAL $verbosity -i "netmap:pipe{1/z" -r "${len}:${fill}:${num}" $seq
+e4=$?
+wait $p3
+e3=$?
+check_success $e3 "receive-${num} netmap:pipe{1"
+check_success $e4 "receive-${num} netmap:pipe{1/z"
diff --git a/utils/tests/018_send_cp_mon_ephemeral_vale_port_test b/utils/tests/018_send_cp_mon_ephemeral_vale_port_test
new file mode 100755
index 000000000..17b8309c4
--- /dev/null
+++ b/utils/tests/018_send_cp_mon_ephemeral_vale_port_test
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if a send copy monitor receives frames when its
+#                 monitored ephemeral VALE port is sending.
+# Operations:
+# 1) create a persistent VALE port (v0).
+# 2) open a send copy monitor v0/t for v0.
+# 3) send from v0, check that both v0/t and v1 receive the frame.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i vale0:v0
+check_success $? "pre-open vale0:v0"
+$FUNCTIONAL $verbosity -i vale0:v0/t
+check_success $? "pre-open vale0:v0/t"
+$FUNCTIONAL $verbosity -i vale0:v1
+check_success $? "pre-open vale0:v1"
+
+# First we send without reading from v1
+$FUNCTIONAL $verbosity -i vale0:v0/t  -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i vale0:v0    -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "receive-${num} vale0:v0/t"
+check_success $e2 "send-${num} vale0:v0"
+
+# Then we read from v1
+$FUNCTIONAL $verbosity -i vale0:v1 -r "${len}:${fill}:${num}" $seq
+e3=$?
+check_success $e3 "receive-${num} vale0:v1"
diff --git a/utils/tests/019_send_cp_mon_persistent_vale_port_test b/utils/tests/019_send_cp_mon_persistent_vale_port_test
new file mode 100755
index 000000000..52f17f6ee
--- /dev/null
+++ b/utils/tests/019_send_cp_mon_persistent_vale_port_test
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if a send copy monitor receives frames when its
+#                 monitored persistent VALE port is sending.
+# Operations:
+# 1) create a persistent VALE port (v0).
+# 2) connect v0 and a VALE ephimeral port (v1) to the same VALE switch.
+# 3) open a send copy monitor v0/t for v0.
+# 4) send from v0, check that both v0/t and v1 receive the frame.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+create_vale_persistent_port "v0"
+attach_to_vale_bridge "vale0" "v0"
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i vale0:v0
+check_success $? "pre-open vale0:v0"
+$FUNCTIONAL $verbosity -i netmap:v0/t
+check_success $? "pre-open netmap:v0/t"
+$FUNCTIONAL $verbosity -i vale0:v1
+check_success $? "pre-open vale0:v1"
+
+# First we send without reading from v1
+$FUNCTIONAL $verbosity -i netmap:v0/t -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i vale0:v0    -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "receive-${num} netmap:v0/t"
+check_success $e2 "send-${num} vale0:v0"
+
+# Then we read from v1
+$FUNCTIONAL $verbosity -i vale0:v1 -r "${len}:${fill}:${num}" $seq
+e3=$?
+check_success $e3 "receive-${num} vale0:v1"
diff --git a/utils/tests/020_send_cp_mon_pipe_test b/utils/tests/020_send_cp_mon_pipe_test
new file mode 100755
index 000000000..9b12b7ff9
--- /dev/null
+++ b/utils/tests/020_send_cp_mon_pipe_test
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if a send copy monitor receives frames when its
+#                 monitored netmap pipe is sending, even if the non-monitored
+#                 port hasn't yet read the frame.
+# Operations:
+# 1) create a pair of netmap pipes (pipe{1, pipe}1).
+# 2) open a send copy monitor pipe{1/t for pipe{1.
+# 3) send from pipe{1 and don't read from pipe}1, check that pipe{1/t receives
+#    the frame.
+# 4) receive from pipe}1.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "netmap:pipe{1"
+check_success $? "pre-open netmap:pipe{1"
+$FUNCTIONAL $verbosity -i "netmap:pipe{1/t"
+check_success $? "pre-open netmap:pipe{1/t"
+$FUNCTIONAL $verbosity -i "netmap:pipe}1"
+check_success $? "pre-open netmap:pipe}1"
+
+# First we send without reading from pipe}1
+$FUNCTIONAL $verbosity -i "netmap:pipe{1/t" -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i "netmap:pipe{1"   -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "receive-${num} netmap:pipe{1/t"
+check_success $e2 "send-${num} netmap:pipe{1"
+
+# Then we read from pipe}1
+$FUNCTIONAL $verbosity -i "netmap:pipe}1" -r "${len}:${fill}:${num}" $seq
+e3=$?
+check_success $e3 "receive-${num} netmap:pipe}1"
diff --git a/utils/tests/021_send_rec_ephemeral_vale_ports_test b/utils/tests/021_send_rec_ephemeral_vale_ports_test
new file mode 100755
index 000000000..720758135
--- /dev/null
+++ b/utils/tests/021_send_rec_ephemeral_vale_ports_test
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if we can send and receive packets through ephimeral
+#                 VALE ports.
+# Operations:
+# 1) send from vale0:v2 and check that both, vale0:v0 and vale0:v1, receive.
+# 2) send from vale0:v0 and check that both, vale0:v1 and vale0:v2, receive.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-c}"
+len="${len:-274}"
+num="${num:-1}"
+seq="${seq:-}"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "vale0:v0"
+check_success $? "pre-open vale0:v0"
+$FUNCTIONAL $verbosity -i "vale0:v1"
+check_success $? "pre-open vale0:v1"
+$FUNCTIONAL $verbosity -i "vale0:v2"
+check_success $? "pre-open vale0:v2"
+
+# v2 ---> v0, v1
+$FUNCTIONAL $verbosity -i "vale0:v0" -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i "vale0:v1" -r "${len}:${fill}:${num}" $seq &
+p2=$!
+$FUNCTIONAL $verbosity -i "vale0:v2" -t "${len}:${fill}:${num}" $seq
+e3=$?
+wait $p1
+e1=$?
+wait $p2
+e2=$?
+check_success $e1 "receive-${num} vale0:v0"
+check_success $e2 "receive-${num} vale0:v1"
+check_success $e3 "send-${num} vale0:v2"
+
+# v0 ---> v1, v2
+$FUNCTIONAL $verbosity -i "vale0:v1" -r "${len}:${fill}:${num}" $seq &
+p4=$!
+$FUNCTIONAL $verbosity -i "vale0:v2" -r "${len}:${fill}:${num}" $seq &
+p5=$!
+$FUNCTIONAL $verbosity -i "vale0:v0" -t "${len}:${fill}:${num}" $seq
+e6=$?
+wait $p4
+e4=$?
+wait $p5
+e5=$?
+check_success $e4 "receive-${num} vale0:v1"
+check_success $e5 "receive-${num} vale0:v2"
+check_success $e6 "send-${num} vale0:v0"
diff --git a/utils/tests/022_send_rec_persistent_vale_ports_test b/utils/tests/022_send_rec_persistent_vale_ports_test
new file mode 100755
index 000000000..7683572ee
--- /dev/null
+++ b/utils/tests/022_send_rec_persistent_vale_ports_test
@@ -0,0 +1,62 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if we can send and receive packets through persistent
+#                 VALE ports.
+# Operations:
+# 1) create 3 persistent VALE ports (v0, v1, v2) and attach them to vale0.
+# 2) send from vale0:v2 and check that both, vale0:v0 and vale0:v1, receive.
+# 3) send from vale0:v0 and check that both, vale0:v1 and vale0:v2, receive.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-c}"
+len="${len:-274}"
+num="${num:-1}"
+seq="${seq:-}"
+
+create_vale_persistent_port "v0"
+create_vale_persistent_port "v1"
+create_vale_persistent_port "v2"
+attach_to_vale_bridge "vale0" "v0"
+attach_to_vale_bridge "vale0" "v1"
+attach_to_vale_bridge "vale0" "v2"
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "vale0:v0"
+check_success $? "pre-open vale0:v0"
+$FUNCTIONAL $verbosity -i "vale0:v1"
+check_success $? "pre-open vale0:v1"
+$FUNCTIONAL $verbosity -i "vale0:v2"
+check_success $? "pre-open vale0:v2"
+
+# v2 ---> v0, v1
+$FUNCTIONAL $verbosity -i "vale0:v0" -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i "vale0:v1" -r "${len}:${fill}:${num}" $seq &
+p2=$!
+$FUNCTIONAL $verbosity -i "vale0:v2" -t "${len}:${fill}:${num}" $seq
+e3=$?
+wait $p1
+e1=$?
+wait $p2
+e2=$?
+check_success $e1 "receive-${num} vale0:v0"
+check_success $e2 "receive-${num} vale0:v1"
+check_success $e3 "send-${num} vale0:v2"
+
+# v0 ---> v1, v2
+$FUNCTIONAL $verbosity -i "vale0:v1" -r "${len}:${fill}:${num}" $seq &
+p4=$!
+$FUNCTIONAL $verbosity -i "vale0:v2" -r "${len}:${fill}:${num}" $seq &
+p5=$!
+$FUNCTIONAL $verbosity -i "vale0:v0" -t "${len}:${fill}:${num}" $seq
+e6=$?
+wait $p4
+e4=$?
+wait $p5
+e5=$?
+check_success $e4 "receive-${num} vale0:v1"
+check_success $e5 "receive-${num} vale0:v2"
+check_success $e6 "send-${num} vale0:v0"
diff --git a/utils/tests/023_send_rec_pipe_test b/utils/tests/023_send_rec_pipe_test
new file mode 100755
index 000000000..d7845e6bd
--- /dev/null
+++ b/utils/tests/023_send_rec_pipe_test
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if we can send and receive packets through netmap pipes.
+# Operations:
+# 1) create a pair of netmap pipes (pipeA{1, pipeA}1).
+# 2) send from pipeA{1 and check if pipeA}1 receives.
+# 3) send from pipeA}1 and check if pipeA{1 receives.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-c}"
+len="${len:-274}"
+num="${num:-1}"
+seq="${seq:-}"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "netmap:pipeA{1"
+check_success $? "pre-open netmap:pipeA{1"
+$FUNCTIONAL $verbosity -i "netmap:pipeA}1"
+check_success $? "pre-open netmap:pipeA}1"
+
+# pipeA}1 ---> pipeA{1
+$FUNCTIONAL $verbosity -i "netmap:pipeA{1" -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i "netmap:pipeA}1" -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "receive-${num} netmap:pipeA{1"
+check_success $e2 "send-${num} netmap:pipeA}1"
+
+# pipeA{1 ---> pipeA}1
+$FUNCTIONAL $verbosity -i "netmap:pipeA}1" -r "${len}:${fill}:${num}" $seq &
+p3=$!
+$FUNCTIONAL $verbosity -i "netmap:pipeA{1" -t "${len}:${fill}:${num}" $seq
+e4=$?
+wait $p3
+e2=$?
+check_success $e2 "receive-${num} netmap:pipeA}1"
+check_success $e4 "send-${num} netmap:pipeA{1"
diff --git a/utils/tests/024_send_rec_veth_test b/utils/tests/024_send_rec_veth_test
new file mode 100755
index 000000000..4888b1f46
--- /dev/null
+++ b/utils/tests/024_send_rec_veth_test
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if we can send and receive through veth interfaces.
+# Operations:
+# 1) create a pair of veth interfaces (veth1A, veth1B).
+# 2) send from veth1B and check if veth1A receives.
+# 3) send from veth1A and check if veth1B receives.
+################################################################################
+source test_lib
+
+exit 0 # Test currently disabled.
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+create_veth_interfaces "veth1"
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i netmap:veth1A
+check_success $? "pre-open netmap:veth1A"
+$FUNCTIONAL $verbosity -i netmap:veth1B
+check_success $? "pre-open netmap:veth1B"
+
+# veth1B --> veth1A
+$FUNCTIONAL $verbosity -i netmap:veth1A -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i netmap:veth1B -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "receive-${num} netmap:veth1A"
+check_success $e2 "send-${num} netmap:veth1B"
+
+# veth1A --> veth1B
+$FUNCTIONAL $verbosity -i netmap:veth1B -r "${len}:${fill}:${num}" $seq &
+p3=$!
+$FUNCTIONAL $verbosity -i netmap:veth1A -t "${len}:${fill}:${num}" $seq
+e4=$?
+wait $p3
+e3=$?
+check_success $e3 "receive-${num} netmap:veth1B"
+check_success $e4 "send-${num} netmap:veth1A"
diff --git a/utils/tests/025_send_zcp_mon_ephemeral_vale_port_test b/utils/tests/025_send_zcp_mon_ephemeral_vale_port_test
new file mode 100755
index 000000000..c81d123ae
--- /dev/null
+++ b/utils/tests/025_send_zcp_mon_ephemeral_vale_port_test
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if a zero-copy monitor receives frames when its
+#                 monitored ephemeral VALE port is sending, even if the
+#                 destination port hasn't yet read the frame (this happens
+#                 because VALE switches do not use zero-copy).
+# Operations:
+# 1) connect two ephemeral VALE ports (v0, v1) to the same VALE switch.
+# 2) open a zero-copy monitor v0/z for v0.
+# 3) send from v0 and don't read from v1, check that v0/z receives the frame.
+# 4) receive from v1.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i vale0:v0
+check_success $? "pre-open vale0:v0"
+$FUNCTIONAL $verbosity -i vale0:v0/z
+check_success $? "pre-open vale0:v0/z"
+$FUNCTIONAL $verbosity -i vale0:v1
+check_success $? "pre-open vale0:v1"
+
+# First we send without reading from v1
+$FUNCTIONAL $verbosity -i vale0:v0/z -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i vale0:v0   -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "receive-${num} vale0:v0/z"
+check_success $e2 "send-${num} vale0:v0"
+
+# Then we read from v1
+$FUNCTIONAL $verbosity -i vale0:v1 -r "${len}:${fill}:${num}" $seq
+e3=$?
+check_success $e3 "receive-${num} vale0:v1"
diff --git a/utils/tests/026_send_zcp_mon_persistent_vale_port_test b/utils/tests/026_send_zcp_mon_persistent_vale_port_test
new file mode 100755
index 000000000..95b2f54c7
--- /dev/null
+++ b/utils/tests/026_send_zcp_mon_persistent_vale_port_test
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if a zero-copy monitor receives frames when its
+#                 monitored persistent VALE port is sending, even if the
+#                 destination port hasn't yet read the frame (this happens
+#                 because VALE switches do not use zero-copy).
+# Operations:
+# 1) create a persistent VALE port (v0).
+# 2) connect v0 and a VALE ephimeral port (v1) to the same VALE switch.
+# 3) open a zero-copy monitor v0/z for v0.
+# 4) send from v0 and don't read from v1, check that v0/z receives the frame.
+# 5) receive from v1.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+create_vale_persistent_port "v0"
+attach_to_vale_bridge "vale0" "v0"
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i vale0:v0
+check_success $? "pre-open vale0:v0"
+$FUNCTIONAL $verbosity -i netmap:v0/z
+check_success $? "pre-open netmap:v0/z"
+$FUNCTIONAL $verbosity -i vale0:v1
+check_success $? "pre-open vale0:v1"
+
+# First we send without reading from v1
+$FUNCTIONAL $verbosity -i netmap:v0/z -r "${len}:${fill}:${num}" $seq &
+p1=$!
+$FUNCTIONAL $verbosity -i vale0:v0    -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "receive-${num} netmap:v0/z"
+check_success $e2 "send-${num} vale0:v0"
+
+# Then we read from v1
+$FUNCTIONAL $verbosity -i vale0:v1 -r "${len}:${fill}:${num}" $seq
+e3=$?
+check_success $e3 "receive-${num} vale0:v1"
diff --git a/utils/tests/027_send_zcp_mon_pipe_test b/utils/tests/027_send_zcp_mon_pipe_test
new file mode 100755
index 000000000..75318fb1e
--- /dev/null
+++ b/utils/tests/027_send_zcp_mon_pipe_test
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+################################################################################
+# Test objective: check if a zero-copy monitor is correctly blocked until the
+#                 non-monitored netmap pipe reads the frame (and the slot is
+#                 given back to the sending pipe).
+# Operations:
+# 1) create a pair of netmap pipes (pipe{1, pipe}1).
+# 2) open a zero-copy monitor pipe{1/z for pipe{1.
+# 3) send from pipe{1 without receiving from pipe}1, check that pipe{1/z doesn't
+#    receive the frame.
+# 4) receive from pipe}1, send again from pipe{1 (slots are returned during a
+#    txsync action) and check that pipe{1/z receives the frame.
+################################################################################
+source test_lib
+
+parse_send_recv_arguments "$@"
+verbosity="${verbosity:-}"
+fill="${fill:-d}"
+len="${len:-150}"
+num="${num:-1}"
+seq="${seq:-}"
+
+# Pre-opening interface that will be needed. This is needed to avoid a race
+# condition between the sending and receiving ports.
+$FUNCTIONAL $verbosity -i "netmap:pipe{1"
+check_success $? "pre-open netmap:pipe{1"
+$FUNCTIONAL $verbosity -i "netmap:pipe{1/z"
+check_success $? "pre-open netmap:pipe{1/z"
+$FUNCTIONAL $verbosity -i "netmap:pipe}1"
+check_success $? "pre-open netmap:pipe}1"
+
+# Initially we don't receive with the non-monitored pipe end, therefore the
+# monitor should not receive the frame.
+$FUNCTIONAL $verbosity -i "netmap:pipe{1/z" -r "${len}:${fill}:${num}" $seq -n &
+p1=$!
+$FUNCTIONAL $verbosity -i "netmap:pipe{1"   -t "${len}:${fill}:${num}" $seq
+e2=$?
+wait $p1
+e1=$?
+check_success $e1 "no-receive-${num} netmap:pipe{1/z"
+check_success $e2 "send-${num} netmap:pipe{1"
+
+# Now we receive with the non-monitored pipe end. We need to send again with the
+# monitored pipe otherwise the zero-copy monitor won't be able to see the
+# packet, as the slot is returned to the monitored pipe only during a txsync
+# action.
+$FUNCTIONAL $verbosity -i "netmap:pipe{1/z" -r "${len}:${fill}:${num}" $seq &
+p3=$!
+$FUNCTIONAL $verbosity -i "netmap:pipe}1"   -r "${len}:${fill}:${num}" $seq
+e4=$?
+$FUNCTIONAL $verbosity -i "netmap:pipe{1"   -t "${len}:${fill}:${num}" $seq
+e5=$?
+wait $p3
+e3=$?
+check_success $e4 "receive-${num} netmap:pipe}1"
+check_success $e5 "send-${num} netmap:pipe{1"
+check_success $e3 "receive-${num} netmap:pipe{1/z"