summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFerruh Yigit <ferruh.yigit@intel.com>2016-05-31 18:38:35 +0100
committerFerruh Yigit <ferruh.yigit@intel.com>2016-07-27 13:48:21 +0100
commit508fa1eedeb5a8abcf53f98153f1a58790e7fa63 (patch)
tree29db724fbe87e5eae8cb31d7186d98e60327786a
downloadspp-508fa1eedeb5a8abcf53f98153f1a58790e7fa63.zip
spp-508fa1eedeb5a8abcf53f98153f1a58790e7fa63.tar.gz
spp-508fa1eedeb5a8abcf53f98153f1a58790e7fa63.tar.xz
Initial SPP
Initial Soft Patch Panel code SPP is a framework for managing DPDK resources. The goal of SPP is to easily interconnect DPDK applications together, and assign resources dynamically to these applications to build a pipeline. The first version of SPP provides for the management of DPDK ports, and assigning ports to different DPDK applications. The framework is composed of a primary DPDK application that is responsible for resource management. This primary application doesn't interact with any traffic, and is used to manage creation and freeing of resources only. A Python based management interface is provided to control the primary DPDK application to create resources, which are then to be used by secondary applications. This management application provides a socket based interface for the primary and secondary DPDK applications to interface to the manager. The management application will use OVSDB to maintain all created and assigned ports. Signed-off-by: Ferruh Yigit <ferruh.yigit@intel.com> Signed-off-by: Gerald Rogers <gerald.rogers@intel.com> Signed-off-by: Sy Jong Choi <sy.jong.choi@intel.com>
-rw-r--r--.gitignore0
-rw-r--r--examples/multi_process/patch_panel/Makefile45
-rwxr-xr-xexamples/multi_process/patch_panel/docs/qemu-ifup16
-rw-r--r--examples/multi_process/patch_panel/docs/sample_configs/1nfv_1vm_bi.config19
-rw-r--r--examples/multi_process/patch_panel/docs/sample_configs/1nfv_1vm_uni.config12
-rw-r--r--examples/multi_process/patch_panel/docs/sample_configs/1nfv_bi.config5
-rw-r--r--examples/multi_process/patch_panel/docs/sample_configs/2nfv_1ring_uni.config9
-rw-r--r--examples/multi_process/patch_panel/docs/sample_configs/2nfv_1vm_bi.config21
-rw-r--r--examples/multi_process/patch_panel/docs/sample_configs/2nfv_bi.config7
-rw-r--r--examples/multi_process/patch_panel/docs/setup_guide.md346
-rw-r--r--examples/multi_process/patch_panel/docs/spp_commands.md90
-rw-r--r--examples/multi_process/patch_panel/nfv/Makefile53
-rw-r--r--examples/multi_process/patch_panel/nfv/nfv.c791
-rw-r--r--examples/multi_process/patch_panel/primary/Makefile65
-rw-r--r--examples/multi_process/patch_panel/primary/args.c113
-rw-r--r--examples/multi_process/patch_panel/primary/args.h45
-rw-r--r--examples/multi_process/patch_panel/primary/init.c268
-rw-r--r--examples/multi_process/patch_panel/primary/init.h65
-rw-r--r--examples/multi_process/patch_panel/primary/main.c347
-rw-r--r--examples/multi_process/patch_panel/shared/common.c251
-rw-r--r--examples/multi_process/patch_panel/shared/common.h202
-rwxr-xr-xexamples/multi_process/patch_panel/spp.py431
-rw-r--r--examples/multi_process/patch_panel/vm/Makefile65
-rw-r--r--examples/multi_process/patch_panel/vm/args.c108
-rw-r--r--examples/multi_process/patch_panel/vm/args.h45
-rw-r--r--examples/multi_process/patch_panel/vm/init.c154
-rw-r--r--examples/multi_process/patch_panel/vm/init.h63
-rw-r--r--examples/multi_process/patch_panel/vm/main.c636
28 files changed, 4272 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.gitignore
diff --git a/examples/multi_process/patch_panel/Makefile b/examples/multi_process/patch_panel/Makefile
new file mode 100644
index 0000000..798ed2b
--- /dev/null
+++ b/examples/multi_process/patch_panel/Makefile
@@ -0,0 +1,45 @@
+# BSD LICENSE
+#
+# Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += nfv
+DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += primary
+DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += vm
+
+include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/multi_process/patch_panel/docs/qemu-ifup b/examples/multi_process/patch_panel/docs/qemu-ifup
new file mode 100755
index 0000000..4538b65
--- /dev/null
+++ b/examples/multi_process/patch_panel/docs/qemu-ifup
@@ -0,0 +1,16 @@
+vi /etc/qemu-ifup
+#!/bin/sh
+set -x
+
+switch=virbr0
+
+if [ -n "$1" ];then
+ /usr/bin/sudo /usr/sbin/tunctl -u `whoami` -t $1
+ /usr/bin/sudo /sbin/ip link set $1 up
+ sleep 0.5s
+ /usr/bin/sudo /usr/sbin/brctl addif $switch $1
+ exit 0
+else
+ echo "Error: no interface specified"
+ exit 1
+fi
diff --git a/examples/multi_process/patch_panel/docs/sample_configs/1nfv_1vm_bi.config b/examples/multi_process/patch_panel/docs/sample_configs/1nfv_1vm_bi.config
new file mode 100644
index 0000000..c4e8baa
--- /dev/null
+++ b/examples/multi_process/patch_panel/docs/sample_configs/1nfv_1vm_bi.config
@@ -0,0 +1,19 @@
+status
+sec 0;status
+sec 1;status
+sec 0;add ring 0
+sec 0;add ring 1
+sec 0;add ring 2
+sec 0;add ring 3
+sec 1;add ring 0
+sec 1;add ring 1
+sec 1;add ring 2
+sec 1;add ring 3
+sec 0;patch 0 2
+sec 0;patch 3 0
+sec 0;patch 1 4
+sec 0;patch 5 1
+sec 1;patch 0 3
+sec 1;patch 2 1
+sec 0;forward
+sec 1;forward
diff --git a/examples/multi_process/patch_panel/docs/sample_configs/1nfv_1vm_uni.config b/examples/multi_process/patch_panel/docs/sample_configs/1nfv_1vm_uni.config
new file mode 100644
index 0000000..202f216
--- /dev/null
+++ b/examples/multi_process/patch_panel/docs/sample_configs/1nfv_1vm_uni.config
@@ -0,0 +1,12 @@
+status
+sec 0;status
+sec 1;status
+sec 0;add ring 0
+sec 0;add ring 1
+sec 1;add ring 0
+sec 1;add ring 1
+sec 0;patch 0 2
+sec 0;patch 3 1
+sec 1;patch 0 1
+sec 0;forward
+sec 1;forward
diff --git a/examples/multi_process/patch_panel/docs/sample_configs/1nfv_bi.config b/examples/multi_process/patch_panel/docs/sample_configs/1nfv_bi.config
new file mode 100644
index 0000000..3e333db
--- /dev/null
+++ b/examples/multi_process/patch_panel/docs/sample_configs/1nfv_bi.config
@@ -0,0 +1,5 @@
+status
+sec 0;status
+sec 0;patch 0 1
+sec 0;patch 1 0
+sec 0;forward
diff --git a/examples/multi_process/patch_panel/docs/sample_configs/2nfv_1ring_uni.config b/examples/multi_process/patch_panel/docs/sample_configs/2nfv_1ring_uni.config
new file mode 100644
index 0000000..be8f51c
--- /dev/null
+++ b/examples/multi_process/patch_panel/docs/sample_configs/2nfv_1ring_uni.config
@@ -0,0 +1,9 @@
+status
+sec 0;status
+sec 1;status
+sec 0;add ring 0
+sec 1;add ring 0
+sec 0;patch 0 2
+sec 1;patch 2 1
+sec 0;forward
+sec 1;forward
diff --git a/examples/multi_process/patch_panel/docs/sample_configs/2nfv_1vm_bi.config b/examples/multi_process/patch_panel/docs/sample_configs/2nfv_1vm_bi.config
new file mode 100644
index 0000000..2ba3175
--- /dev/null
+++ b/examples/multi_process/patch_panel/docs/sample_configs/2nfv_1vm_bi.config
@@ -0,0 +1,21 @@
+status
+sec 0;status
+sec 1;status
+sec 2;status
+sec 0;add ring 0
+sec 0;add ring 1
+sec 1;add ring 2
+sec 1;add ring 3
+sec 2;add ring 0
+sec 2;add ring 1
+sec 2;add ring 2
+sec 2;add ring 3
+sec 0;patch 0 2
+sec 0;patch 3 0
+sec 1;patch 3 1
+sec 1;patch 1 2
+sec 2;patch 0 3
+sec 2;patch 2 1
+sec 0;forward
+sec 1;forward
+sec 2;forward
diff --git a/examples/multi_process/patch_panel/docs/sample_configs/2nfv_bi.config b/examples/multi_process/patch_panel/docs/sample_configs/2nfv_bi.config
new file mode 100644
index 0000000..cad6bc7
--- /dev/null
+++ b/examples/multi_process/patch_panel/docs/sample_configs/2nfv_bi.config
@@ -0,0 +1,7 @@
+status
+sec 0;status
+sec 1;status
+sec 0;patch 0 1
+sec 1;patch 1 0
+sec 0;forward
+sec 1;forward
diff --git a/examples/multi_process/patch_panel/docs/setup_guide.md b/examples/multi_process/patch_panel/docs/setup_guide.md
new file mode 100644
index 0000000..0dcbcc3
--- /dev/null
+++ b/examples/multi_process/patch_panel/docs/setup_guide.md
@@ -0,0 +1,346 @@
+Sample usage of the application :
+
+Start Controller
+================
+python spp.py -p 5555 -s 6666
+
+Start spp_primary
+=================
+sudo ./primary/primary/x86_64-ivshmem-linuxapp-gcc/spp_primary -c 0x02 -n 4 --socket-mem 512,512 --huge-dir=/dev/hugepages --proc-type=primary -- -p 0x03 -n 4 -s 192.168.122.1:5555
+
+Start spp_nfv
+=============
+sudo ./nfv/nfv/x86_64-ivshmem-linuxapp-gcc/app/spp_nfv -c 0x06 -n 4 --proc-type=secondary -- -n 1 -s 192.168.122.1:6666
+sudo ./nfv/nfv/x86_64-ivshmem-linuxapp-gcc/app/spp_nfv -c 0x0A -n 4 --proc-type=secondary -- -n 1 -s 192.168.122.1:6666
+
+Start VM (QEMU)
+===============
+Comman qemu command line:
+sudo ./x86_64-softmmu/qemu-system-x86_64 -cpu host -enable-kvm -object memory-backend-file,id=mem,size=2048M,mem-path=/dev/hugepages,share=on -numa node,memdev=mem -mem-prealloc -hda /home/dpdk/debian_wheezy_amd64_standard.qcow2 -m 2048 -smp cores=4,threads=1,sockets=1 -device e1000,netdev=net0,mac=DE:AD:BE:EF:00:01 -netdev tap,id=net0 -nographic -vnc :2
+
+To start spp_vm "qemu-ifup" script required, please copy docs/qemu-ifup to host /etc/qemu-ifup
+
+Two types of VM interfaces supported:
+* ring based (ivshmem)
+* vhost interface
+
+* ring based (ivshmem)
+----------------------
+ - This requires custom qemu
+ - Needs ivshmem argument for qemu, refer to spp_primary logs to get the ivshmem metadata command line:
+ APP: QEMU command line for config 'pp_ivshmem':
+ -device ivshmem,size=2048M,shm=fd:/dev/hugepages/rtemap_0:0x0:0x40000000:/dev/zero:0x0:0x3fffc000:/var/run/.dpdk_ivshmem_metadata_pp_ivshmem:0x0:0x4000
+
+ Insert into qemu command line:-
+ sudo ./x86_64-softmmu/qemu-system-x86_64 -cpu host -enable-kvm -object memory-backend-file,id=mem,size=2048M,mem-path=/dev/hugepages,share=on -numa node,memdev=mem -mem-prealloc -hda /home/dpdk/debian_wheezy_amd64_standard.qcow2 -m 2048 -smp cores=4,threads=1,sockets=1 -device e1000,netdev=net0,mac=DE:AD:BE:EF:00:01 -netdev tap,id=net0 -device ivshmem,size=2048M,shm=fd:/dev/hugepages/rtemap_0:0x0:0x40000000:/dev/zero:0x0:0x3fffc000:/var/run/.dpdk_ivshmem_metadata_pp_ivshmem:0x0:0x4000 -nographic
+
+* vhost interface
+-----------------
+ - spp should do a "sec x:add vhost y" before starting the VM. x: vnf number, y: vhost port id.
+ - Needs vhost argument for qemu:
+ sudo ./x86_64-softmmu/qemu-system-x86_64 -cpu host -enable-kvm -object memory-backend-file,id=mem,size=2048M,mem-path=/dev/hugepages,share=on -numa node,memdev=mem -mem-prealloc -hda /home/dpdk/debian_wheezy_amd64_standard.qcow2 -m 2048 -smp cores=4,threads=1,sockets=1 -device e1000,netdev=net0,mac=DE:AD:BE:EF:00:01 -netdev tap,id=net0 -chardev socket,id=chr0,path=/tmp/sock0 -netdev vhost-user,id=net0,chardev=chr0,vhostforce -device virtio-net-pci,netdev=net0 -nographic -vnc :2
+
+
+Start spp_vm (Inside the VM)
+============================
+sudo ./vm/vm/x86_64-ivshmem-linuxapp-gcc/spp_vm -c 0x03 -n 4 --proc-type=primary -- -p 0x01 -n 1 -s 192.168.122.1:6666
+
+
+
+Test Setup 1: Single NFV
+
+ __
+ +--------------+ |
+ | spp_nfv | |
+ | | |
+ +--------------+ |
+ ^ : |
+ | | |
+ : v |
+ +----+----------+-------------------------------------------------+ |
+ | | primary | ^ : | |
+ | +----------+ : : | |
+ | : : | |
+ | +----------+ +---------+ | | host
+ | : v | |
+ | +--------------+ +--------------+ | |
+ | | phy port 0 | ovs-dpdk | phy port 1 | | |
+ +------------------+--------------+------------+--------------+---+ __|
+ ^ :
+ | |
+ : v
+
+Configuration for L2fwd:-
+spp > sec 0;patch 0 1
+spp > sec 0;patch 1 0
+spp > sec 0;forward
+
+Configuration for loopback:-
+spp > sec 0;patch 0 0
+spp > sec 0;patch 1 1
+spp > sec 0;forward
+
+
+Test Setup 2: Dual NFV
+
+ __
+ +--------------+ +--------------+ |
+ | spp_nfv | | spp_nfv | |
+ | | | | |
+ +--------------+ +--------------+ |
+ ^ : : : |
+ | | +--------+ | |
+ : v | v |
+ +----+----------+-----------------------+-------------------------+ |
+ | | primary | ^ : | : | |
+ | +----------+ | +------+--------+ : | |
+ | : | : : | |
+ | : +------+ : | | | host
+ | : v v v | |
+ | +--------------+ +--------------+ | |
+ | | phy port 0 | | phy port 1 | | |
+ +------------------+--------------+------------+--------------+---+ __|
+ ^ :
+ | |
+ : v
+
+Configuration for L2fwd:-
+spp > sec 0;patch 0 1
+spp > sec 1;patch 1 0
+spp > sec 0;forward
+spp > sec 1;forward
+
+
+ __
+ +--------------+ +--------------+ |
+ | spp_nfv | | spp_nfv | |
+ | | | | |
+ +--------------+ +--------------+ |
+ ^ : ^ : |
+ | | | | |
+ : v : v |
+ +----+----------+-------------------------------------------------+ |
+ | | primary | ^ : ^ : | |
+ | +----------+ | : | : | |
+ | : : : : | |
+ | : | : | | | host
+ | : v : v | |
+ | +--------------+ +--------------+ | |
+ | | phy port 0 | | phy port 1 | | |
+ +------------------+--------------+------------+--------------+---+ __|
+ ^ ^
+ | |
+ v v
+
+Configuration for loopback:-
+spp > sec 0;patch 0 0
+spp > sec 1;patch 1 1
+spp > sec 0;forward
+spp > sec 1;forward
+
+
+Test Setup 3: Dual NFV with ring pmd
+
+ __
+ +----------+ ring +----------+ |
+ | spp_nfv | +--------+ | spp_nfv | |
+ | 2 | -> | | | |- > | 2 | |
+ +----------+ +--------+ +----------+ |
+ ^ : |
+ | | |
+ : v |
+ +----+----------+-------------------------------------------------+ |
+ | | primary | ^ : | |
+ | +----------+ | : | |
+ | : : | |
+ | : | | | host
+ | : v | |
+ | +--------------+ +--------------+ | |
+ | | phy port 0 | | phy port 1| | |
+ +------------------+--------------+------------+--------------+---+ __|
+ ^ :
+ | |
+ : v
+
+Configuration for Uni directional L2fwd:-
+spp > sec 0;add ring 0
+spp > sec 1;add ring 0
+spp > sec 0;patch 0 2
+spp > sec 1;patch 2 1
+spp > sec 0;forward
+spp > sec 1;forward
+
+
+ __
+ ring |
+ +--------+ |
+ +----------+ <--| | | |<-- +----------+ |
+ | 3 | +--------+ | 3 | |
+ | spp_nfv | | spp_nfv | |
+ | 2 |--> +--------+ -->| 2 | |
+ +----------+ | | | | +----------+ |
+ ^ +--------+ ^ |
+ | ring | |
+ v v |
+ +----+----------+-------------------------------------------------+ |
+ | | primary | ^ ^ | |
+ | +----------+ | : | |
+ | : : | |
+ | : | | | host
+ | v v | |
+ | +--------------+ +--------------+ | |
+ | | phy port 0 | | phy port 1| | |
+ +------------------+--------------+------------+--------------+---+ __|
+ ^ ^
+ | |
+ v v
+
+Configuration for L2fwd:-
+spp > sec 0;add ring 0
+spp > sec 0;add ring 1
+spp > sec 1;add ring 0
+spp > sec 1;add ring 1
+spp > sec 0;patch 0 2
+spp > sec 0;patch 3 0
+spp > sec 1;patch 1 3
+spp > sec 1;patch 2 1
+spp > sec 0;forward
+spp > sec 1;forward
+
+
+Test Setup 3: Dual NFV with VM through ring pmd
+ __
+ +----------------------+ |
+ | guest | |
+ | | |
+ | +--------------+ | | guest
+ | | spp_vm | | |
+ | | 0 1 | | |
+ +---+--------------+---+ __|
+ ^ :
+ | |
+ : v
+ +-+ +-+
+ +-+ +-+
+ ring 0 +-+ +-+ ring 1
+ +-+ +-+
+ ^ |
+ | V __
+ +----------+ +----------+ |
+ | spp_nfv1 | | spp_nfv2 | |
+ | 2 | | 3 | |
+ +----------+ +----------+ |
+ ^ : |
+ | | |
+ : v |
+ +----+----------+-------------------------------------------------+ |
+ | | primary | ^ : | |
+ | +----------+ | : | |
+ | : : | |
+ | : | | | host
+ | : v | |
+ | +--------------+ +--------------+ | |
+ | | phy port 0 | | phy port 1 | | |
+ +------------------+--------------+------------+--------------+---+ __|
+ ^ :
+ | |
+ : v
+
+Legend:-
+sec 0 = spp_nfv1
+sec 1 = spp_nfv2
+sec 2 = spp_vm
+
+
+Configuration for Uni directional L2fwd:-
+spp > sec 0;add ring 0
+spp > sec 0;add ring 1
+spp > sec 1;add ring 0
+spp > sec 1;add ring 1
+spp > sec 2;add ring 0
+spp > sec 2;add ring 1
+spp > sec 2;patch 0 1
+spp > sec 0;patch 0 2
+spp > sec 1;patch 3 1
+spp > sec 2;forward
+spp > sec 1;forward
+spp > sec 0;forward
+
+
+Test Setup 4: Single NFV with VM through vhost pmd
+
+ __
+ +----------------------+ |
+ | guest | |
+ | | |
+ | +-------------+ | | guest 192.168.122.51
+ | | spp_vm | | |
+ | | 0 | | |
+ +---+--------------+---+ __|
+ ^ :
+ | |
+ | virtio |
+ | |
+ | V __
+ +--------------------+ |
+ | spp_nfv | |
+ | 2 | |
+ +--------------------+ |
+ ^ : |
+ | +---------- + |
+ : v |
+ +----+----------+--------------------------------------------+ |
+ | | primary | ^ : | |
+ | +----------+ | : | |
+ | : : | |
+ | : | | | host 192.168.122.1
+ | : v | |
+ | +--------------+ +--------------+ | |
+ | | phy port 0 | | phy port 1| | |
+ +------------------+--------------+-------+--------------+---+ __|
+ ^ :
+ | |
+ : v
+
+Legend:-
+sec 0 = spp_nfv
+sec 1 = spp_vm
+
+
+spp > sec 0;add vhost 0
+[start VM]
+spp > sec 0;patch 0 2
+spp > sec 0;patch 2 1
+spp > sec 1;patch 0 0
+spp > sec 1;forward
+spp > sec 0;forward
+
+
+
+Optimizing qemu performance:-
+First find out the PID for qemu-system-x86 process
+ps ea
+ PID TTY STAT TIME COMMAND
+192606 pts/11 Sl+ 4:42 ./x86_64-softmmu/qemu-system-x86_64 -cpu host -enable-kvm -object memory-backend-file,id=mem,siz
+
+Using pstree to list out qemu-system-x86_64 threads:-
+pstree -p 192606
+qemu-system-x86(192606)--+--{qemu-system-x8}(192607)
+ |--{qemu-system-x8}(192623)
+ |--{qemu-system-x8}(192624)
+ |--{qemu-system-x8}(192625)
+ |--{qemu-system-x8}(192626)
+
+To Optimize, use taskset to pin each thread:-
+$ sudo taskset -pc 4 192623
+pid 192623's current affinity list: 0-31
+pid 192623's new affinity list: 4
+$ sudo taskset -pc 5 192624
+pid 192624's current affinity list: 0-31
+pid 192624's new affinity list: 5
+$ sudo taskset -pc 6 192625
+pid 192625's current affinity list: 0-31
+pid 192625's new affinity list: 6
+$ sudo taskset -pc 7 192626
+pid 192626's current affinity list: 0-31
+pid 192626's new affinity list: 7
diff --git a/examples/multi_process/patch_panel/docs/spp_commands.md b/examples/multi_process/patch_panel/docs/spp_commands.md
new file mode 100644
index 0000000..1cd86fc
--- /dev/null
+++ b/examples/multi_process/patch_panel/docs/spp_commands.md
@@ -0,0 +1,90 @@
+spp commands:
+
+* help [> help]
+
+ Displays brief help
+
+
+* status [> status]
+
+ Display number of connected primary and secondary application count
+ Also display connected secondary application client_id
+
+
+* sec <id>
+
+ Send commands to secondary applications with client id <id>:
+
+ * status [> sec 0;status]
+
+ * add
+
+ * ring <id> [> sec 0;add ring 0]
+
+ * vhost <id> [> sec 0;add vhost 0]
+
+ * patch <id> <id> [> sec 0;patch 0 2]
+
+ * reset [> sec 0; patch reset]
+
+ * forward [> sec 0;forward]
+
+ * del
+
+ * ring <id> [> sec 0; del ring 0]
+
+ * exit [> sec 0;exit]
+
+ * stop [>sec 0;stop]
+
+
+* record, playback [> record a.txt] [> playback a.txt]
+
+ To record commands in SPP:-
+ spp > record <filename>
+
+ To stop recording
+ spp > bye
+
+ To playback commands
+ spp > playback <filename>
+
+ '#' character at the beginning of the line used for comments
+
+
+* bye [> bye]
+
+ Close the spp
+
+ * sec [> bye sec]
+
+ Close all secondary app and spp
+
+ * all [> bye all]
+
+ Close all secondary app, primary app and spp
+
+
+* pri
+
+ Send commands to primary applications:
+
+ * status [> pri status]
+
+ * add
+
+ * ring <id> [> pri add ring 0]
+
+ * del
+
+ * ring <id> [> pri del ring 0]
+
+ * exit [> pri exit]
+
+ * stop [> pri stop]
+
+ * start [> pri start]
+
+ * clear [> pri clear]
+
+ Clear statistics
diff --git a/examples/multi_process/patch_panel/nfv/Makefile b/examples/multi_process/patch_panel/nfv/Makefile
new file mode 100644
index 0000000..aab58a7
--- /dev/null
+++ b/examples/multi_process/patch_panel/nfv/Makefile
@@ -0,0 +1,53 @@
+# BSD LICENSE
+#
+# Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriden by command line or environment
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = spp_nfv
+
+# all source are stored in SRCS-y
+SRCS-y := nfv.c ../shared/common.c
+
+CFLAGS += $(WERROR_FLAGS) -O3
+CFLAGS += -I$(SRCDIR)/../shared
+
+ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
+LDLIBS += -lrte_pmd_ring
+LDLIBS += -lrte_pmd_vhost
+endif
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/multi_process/patch_panel/nfv/nfv.c b/examples/multi_process/patch_panel/nfv/nfv.c
new file mode 100644
index 0000000..fc9ad1e
--- /dev/null
+++ b/examples/multi_process/patch_panel/nfv/nfv.c
@@ -0,0 +1,791 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arpa/inet.h>
+#include <getopt.h>
+
+#include <rte_eth_ring.h>
+#include <rte_eth_vhost.h>
+#include <rte_memzone.h>
+
+#include "common.h"
+
+static sig_atomic_t on = 1;
+
+/*
+ * our client id number - tells us which rx queue to read, and NIC TX
+ * queue to write to.
+ */
+static uint8_t client_id;
+static char *server_ip;
+static int server_port;
+
+static enum cmd_type cmd = STOP;
+
+static struct port_map port_map[RTE_MAX_ETHPORTS];
+
+/* the port details */
+struct port_info *ports;
+
+static struct port ports_fwd_array[RTE_MAX_ETHPORTS];
+
+/*
+ * print a usage message
+ */
+static void
+usage(const char *progname)
+{
+ RTE_LOG(INFO, APP,
+ "Usage: %s [EAL args] -- -n <client_id>\n\n", progname);
+}
+
+/*
+ * Parse the application arguments to the client app.
+ */
+static int
+parse_app_args(int argc, char *argv[])
+{
+ int option_index, opt;
+ char **argvopt = argv;
+ const char *progname = argv[0];
+ static struct option lgopts[] = { {0} };
+ int ret;
+
+ while ((opt = getopt_long(argc, argvopt, "n:s:", lgopts,
+ &option_index)) != EOF) {
+ switch (opt) {
+ case 'n':
+ if (parse_num_clients(&client_id, optarg) != 0) {
+ usage(progname);
+ return -1;
+ }
+ break;
+ case 's':
+ ret = parse_server(&server_ip, &server_port, optarg);
+ if (ret != 0) {
+ usage(progname);
+ return -1;
+ }
+ break;
+ default:
+ usage(progname);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+forward(void)
+{
+ uint16_t nb_rx;
+ uint16_t nb_tx;
+ int in_port;
+ int out_port;
+ uint16_t buf;
+ int i;
+
+ /* Go through every possible port numbers*/
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+ struct rte_mbuf *bufs[MAX_PKT_BURST];
+
+ if (ports_fwd_array[i].in_port_id < 0)
+ continue;
+
+ if (ports_fwd_array[i].out_port_id < 0)
+ continue;
+
+ /* if status active, i count is in port*/
+ in_port = i;
+ out_port = ports_fwd_array[i].out_port_id;
+
+ /* Get burst of RX packets, from first port of pair. */
+ /*first port rx, second port tx*/
+ nb_rx = ports_fwd_array[in_port].rx_func(in_port, 0, bufs,
+ MAX_PKT_BURST);
+ if (unlikely(nb_rx == 0))
+ continue;
+
+ port_map[in_port].stats->rx += nb_rx;
+
+ /* Send burst of TX packets, to second port of pair. */
+ nb_tx = ports_fwd_array[out_port].tx_func(out_port, 0, bufs,
+ nb_rx);
+
+ port_map[out_port].stats->tx += nb_tx;
+
+ /* Free any unsent packets. */
+ if (unlikely(nb_tx < nb_rx)) {
+ port_map[out_port].stats->tx_drop += nb_rx - nb_tx;
+ for (buf = nb_tx; buf < nb_rx; buf++)
+ rte_pktmbuf_free(bufs[buf]);
+ }
+ }
+}
+
+/* main processing loop */
+static void
+nfv_loop(void)
+{
+ unsigned int lcore_id = rte_lcore_id();
+
+ RTE_LOG(INFO, APP, "entering main loop on lcore %u\n", lcore_id);
+
+ while (1) {
+ if (unlikely(cmd == STOP)) {
+ sleep(1);
+ /*RTE_LOG(INFO, APP, "Idling\n");*/
+ continue;
+ } else if (cmd == FORWARD) {
+ forward();
+ }
+ }
+}
+
+/* leading to nfv processing loop */
+static int
+main_loop(void *dummy __rte_unused)
+{
+ nfv_loop();
+ return 0;
+}
+
+static void
+port_map_init_one(unsigned int i)
+{
+ port_map[i].id = PORT_RESET;
+ port_map[i].port_type = UNDEF;
+ port_map[i].stats = &port_map[i].default_stats;
+}
+
+static void
+port_map_init(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++)
+ port_map_init_one(i);
+}
+
+static void
+forward_array_init_one(unsigned int i)
+{
+ ports_fwd_array[i].in_port_id = PORT_RESET;
+ ports_fwd_array[i].out_port_id = PORT_RESET;
+}
+
+/* initialize forward array with default value*/
+static void
+forward_array_init(void)
+{
+ unsigned int i;
+
+ /* initialize port forward array*/
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++)
+ forward_array_init_one(i);
+}
+
+static void
+forward_array_reset(void)
+{
+ unsigned int i;
+
+ /* initialize port forward array*/
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+ if (ports_fwd_array[i].in_port_id > -1) {
+ ports_fwd_array[i].out_port_id = PORT_RESET;
+ RTE_LOG(INFO, APP, "Port ID %d\n", i);
+ RTE_LOG(INFO, APP, "out_port_id %d\n",
+ ports_fwd_array[i].out_port_id);
+ }
+ }
+}
+
+/* print forward array active port*/
+static void
+print_active_ports(char *str)
+{
+ unsigned int i;
+
+ sprintf(str, "%d\n", client_id);
+
+ /* every elements value*/
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+ if (ports_fwd_array[i].in_port_id < 0)
+ continue;
+
+ RTE_LOG(INFO, APP, "Port ID %d\n", i);
+ RTE_LOG(INFO, APP, "Status %d\n",
+ ports_fwd_array[i].in_port_id);
+
+ sprintf(str + strlen(str), "port id: %d,", i);
+ if (ports_fwd_array[i].in_port_id >= 0)
+ sprintf(str + strlen(str), "on,");
+ else
+ sprintf(str + strlen(str), "off,");
+
+ switch (port_map[i].port_type) {
+ case PHY:
+ RTE_LOG(INFO, APP, "Type: PHY\n");
+ sprintf(str + strlen(str), "PHY,");
+ break;
+ case RING:
+ RTE_LOG(INFO, APP, "Type: RING\n");
+ sprintf(str + strlen(str), "RING(%u),",
+ port_map[i].id);
+ break;
+ case VHOST:
+ RTE_LOG(INFO, APP, "Type: VHOST\n");
+ sprintf(str + strlen(str), "VHOST(%u),",
+ port_map[i].id);
+ break;
+ case UNDEF:
+ RTE_LOG(INFO, APP, "Type: UDF\n");
+ sprintf(str + strlen(str), "UDF,");
+ break;
+ }
+
+ RTE_LOG(INFO, APP, "Out Port ID %d\n",
+ ports_fwd_array[i].out_port_id);
+ sprintf(str + strlen(str), "outport: %d\n",
+ ports_fwd_array[i].out_port_id);
+ }
+}
+
+static int
+do_send(int *connected, int *sock, char *str)
+{
+ int ret;
+
+ ret = send(*sock, str, MSG_SIZE, 0);
+ if (ret == -1) {
+ RTE_LOG(ERR, APP, "send failed");
+ *connected = 0;
+ return -1;
+ }
+
+ RTE_LOG(INFO, APP, "To Server: %s\n", str);
+
+ return 0;
+}
+
+static void
+forward_array_remove(int port_id)
+{
+ unsigned int i;
+
+ /* Update ports_fwd_array */
+ forward_array_init_one(port_id);
+
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+ if (ports_fwd_array[i].in_port_id < 0)
+ continue;
+
+ if (ports_fwd_array[i].out_port_id == port_id) {
+ ports_fwd_array[i].out_port_id = PORT_RESET;
+ break;
+ }
+ }
+}
+
+static int
+find_port_id(int id, enum port_type type)
+{
+ int port_id = PORT_RESET;
+ unsigned int i;
+
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+ if (port_map[i].port_type != type)
+ continue;
+
+ if (port_map[i].id == id) {
+ port_id = i;
+ break;
+ }
+ }
+
+ return port_id;
+}
+
+static int
+do_del(char *token_list[], int max_token)
+{
+ int port_id = PORT_RESET;
+ int id;
+
+ if (max_token <= 2)
+ return -1;
+
+ if (!strcmp(token_list[1], "vhost")) {
+ if (spp_atoi(token_list[2], &id) < 0)
+ return 0;
+
+ port_id = find_port_id(id, VHOST);
+ if (port_id < 0)
+ return -1;
+
+
+ } else if (!strcmp(token_list[1], "ring")) {
+ char name[RTE_ETH_NAME_MAX_LEN];
+
+ if (spp_atoi(token_list[2], &id) < 0)
+ return 0;
+
+ port_id = find_port_id(id, RING);
+ if (port_id < 0)
+ return -1;
+
+ rte_eth_dev_detach(port_id, name);
+ }
+
+ forward_array_remove(port_id);
+ port_map_init_one(port_id);
+
+ return 0;
+}
+
+static int
+is_valid_port(int port_id)
+{
+ if (port_id < 0 || port_id > RTE_MAX_ETHPORTS)
+ return 0;
+
+ return port_map[port_id].id != PORT_RESET;
+}
+
+static int
+add_patch(int in_port, int out_port)
+{
+ if (!is_valid_port(in_port) || !is_valid_port(out_port))
+ return -1;
+
+ /* Populate in port data */
+ ports_fwd_array[in_port].in_port_id = in_port;
+ ports_fwd_array[in_port].rx_func = &rte_eth_rx_burst;
+ ports_fwd_array[in_port].tx_func = &rte_eth_tx_burst;
+ ports_fwd_array[in_port].out_port_id = out_port;
+
+ /* Populate out port data */
+ ports_fwd_array[out_port].in_port_id = out_port;
+ ports_fwd_array[out_port].rx_func = &rte_eth_rx_burst;
+ ports_fwd_array[out_port].tx_func = &rte_eth_tx_burst;
+
+ RTE_LOG(DEBUG, APP, "STATUS: in port %d in_port_id %d\n", in_port,
+ ports_fwd_array[in_port].in_port_id);
+ RTE_LOG(DEBUG, APP, "STATUS: in port %d patch out port id %d\n",
+ in_port, ports_fwd_array[in_port].out_port_id);
+ RTE_LOG(DEBUG, APP, "STATUS: outport %d in_port_id %d\n", out_port,
+ ports_fwd_array[out_port].in_port_id);
+
+ return 0;
+}
+
+static int
+add_ring_pmd(int ring_id)
+{
+ struct rte_ring *ring;
+ int ring_port_id;
+
+ /* look up ring, based on user's provided id*/
+ ring = rte_ring_lookup(get_rx_queue_name(ring_id));
+ if (ring == NULL) {
+ RTE_LOG(ERR, APP,
+ "Cannot get RX ring - is server process running?\n");
+ return -1;
+ }
+
+ /* create ring pmd*/
+ ring_port_id = rte_eth_from_ring(ring);
+ RTE_LOG(DEBUG, APP, "ring port id %d\n", ring_port_id);
+
+ return ring_port_id;
+}
+
+static int
+add_vhost_pmd(int index)
+{
+ struct rte_eth_conf port_conf = {
+ .rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
+ };
+ struct rte_mempool *mp;
+ uint8_t vhost_port_id;
+ int nr_queues = 1;
+ const char *name;
+ char devargs[64];
+ char *iface;
+ uint16_t q;
+ int ret;
+#define NR_DESCS 128
+
+ mp = rte_mempool_lookup(PKTMBUF_POOL_NAME);
+ if (mp == NULL)
+ rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n");
+
+ /* eth_vhost0 index 0 iface /tmp/sock0 on numa 0 */
+ name = get_vhost_backend_name(index);
+ iface = get_vhost_iface_name(index);
+
+ sprintf(devargs, "%s,iface=%s,queues=%d", name, iface, nr_queues);
+ ret = rte_eth_dev_attach(devargs, &vhost_port_id);
+ if (ret < 0)
+ return ret;
+
+ ret = rte_eth_dev_configure(vhost_port_id, nr_queues, nr_queues,
+ &port_conf);
+ if (ret < 0)
+ return ret;
+
+ /* Allocate and set up 1 RX queue per Ethernet port. */
+ for (q = 0; q < nr_queues; q++) {
+ ret = rte_eth_rx_queue_setup(vhost_port_id, q, NR_DESCS,
+ rte_eth_dev_socket_id(vhost_port_id), NULL, mp);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Allocate and set up 1 TX queue per Ethernet port. */
+ for (q = 0; q < nr_queues; q++) {
+ ret = rte_eth_tx_queue_setup(vhost_port_id, q, NR_DESCS,
+ rte_eth_dev_socket_id(vhost_port_id), NULL);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Start the Ethernet port. */
+ ret = rte_eth_dev_start(vhost_port_id);
+ if (ret < 0)
+ return ret;
+
+ RTE_LOG(DEBUG, APP, "vhost port id %d\n", vhost_port_id);
+
+ return vhost_port_id;
+}
+
+static int
+do_add(char *token_list[], int max_token)
+{
+ enum port_type type = UNDEF;
+ int port_id = PORT_RESET;
+ int id;
+
+ if (max_token <= 2)
+ return -1;
+
+ if (!strcmp(token_list[1], "vhost")) {
+ if (spp_atoi(token_list[2], &id) < 0)
+ return 0;
+
+ type = VHOST;
+ port_id = add_vhost_pmd(id);
+
+ } else if (!strcmp(token_list[1], "ring")) {
+ if (spp_atoi(token_list[2], &id) < 0)
+ return 0;
+
+ type = RING;
+ port_id = add_ring_pmd(id);
+ }
+
+ if (port_id < 0)
+ return -1;
+
+ port_map[port_id].id = id;
+ port_map[port_id].port_type = type;
+ port_map[port_id].stats = &ports->client_stats[id];
+
+ /* Update ports_fwd_array with port id */
+ ports_fwd_array[port_id].in_port_id = port_id;
+
+ return 0;
+}
+
+static int
+parse_command(char *str)
+{
+ char *token_list[MAX_PARAMETER] = {NULL};
+ int max_token = 0;
+ int ret = 0;
+ int i;
+
+ if (!str)
+ return 0;
+
+ /* tokenize user command from controller */
+ token_list[max_token] = strtok(str, " ");
+ while (token_list[max_token] != NULL) {
+ RTE_LOG(DEBUG, APP, "token %d = %s\n", max_token,
+ token_list[max_token]);
+ max_token++;
+ token_list[max_token] = strtok(NULL, " ");
+ }
+
+ if (max_token == 0)
+ return 0;
+
+ if (!strcmp(token_list[0], "status")) {
+ RTE_LOG(DEBUG, APP, "status\n");
+ memset(str, '\0', MSG_SIZE);
+ if (cmd == FORWARD)
+ i = sprintf(str, "Client ID %d Running\n", client_id);
+ else
+ i = sprintf(str, "Client ID %d Idling\n", client_id);
+ print_active_ports(str + i);
+
+ } else if (!strcmp(token_list[0], "_get_client_id")) {
+ memset(str, '\0', MSG_SIZE);
+ i = sprintf(str, "%d", client_id);
+
+ } else if (!strcmp(token_list[0], "_set_client_id")) {
+ int id;
+
+ if (spp_atoi(token_list[1], &id) >= 0)
+ client_id = id;
+
+ } else if (!strcmp(token_list[0], "exit")) {
+ RTE_LOG(DEBUG, APP, "exit\n");
+ RTE_LOG(DEBUG, APP, "stop\n");
+ cmd = STOP;
+ ret = -1;
+
+ } else if (!strcmp(token_list[0], "stop")) {
+ RTE_LOG(DEBUG, APP, "stop\n");
+ cmd = STOP;
+
+ } else if (!strcmp(token_list[0], "forward")) {
+ RTE_LOG(DEBUG, APP, "forward\n");
+ cmd = FORWARD;
+
+ } else if (!strcmp(token_list[0], "add")) {
+ RTE_LOG(DEBUG, APP, "add\n");
+ do_add(token_list, max_token);
+
+ } else if (!strcmp(token_list[0], "patch")) {
+ RTE_LOG(DEBUG, APP, "patch\n");
+
+ if (max_token <= 1)
+ return 0;
+
+ if (strncmp(token_list[1], "reset", 5) == 0) {
+ /* reset forward array*/
+ forward_array_reset();
+ } else {
+ int in_port;
+ int out_port;
+
+ if (max_token <= 2)
+ return 0;
+
+ if (spp_atoi(token_list[1], &in_port) < 0)
+ return 0;
+
+ if (spp_atoi(token_list[2], &out_port) < 0)
+ return 0;
+
+ add_patch(in_port, out_port);
+ }
+
+ } else if (!strcmp(token_list[0], "del")) {
+ RTE_LOG(DEBUG, APP, "del\n");
+
+ cmd = STOP;
+ do_del(token_list, max_token);
+ }
+
+ return ret;
+}
+
+static int
+do_receive(int *connected, int *sock, char *str)
+{
+ int ret;
+
+ memset(str, '\0', MSG_SIZE);
+
+ ret = recv(*sock, str, MSG_SIZE, 0);
+ if (ret <= 0) {
+ RTE_LOG(DEBUG, APP, "Receive count: %d\n", ret);
+ if (ret < 0)
+ RTE_LOG(ERR, APP, "Receive Fail");
+ else
+ RTE_LOG(INFO, APP, "Receive 0\n");
+
+ RTE_LOG(INFO, APP, "Assume Server closed connection\n");
+ close(*sock);
+ *sock = SOCK_RESET;
+ *connected = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+do_connection(int *connected, int *sock)
+{
+ static struct sockaddr_in servaddr;
+ int ret = 0;
+
+ if (*connected == 0) {
+ if (*sock < 0) {
+ RTE_LOG(INFO, APP, "Creating socket...\n");
+ *sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (*sock < 0)
+ rte_exit(EXIT_FAILURE, "socket error\n");
+
+ /*Create of the tcp socket*/
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr.s_addr = inet_addr(server_ip);
+ servaddr.sin_port = htons(server_port);
+ }
+
+ RTE_LOG(INFO, APP, "Trying to connect ... socket %d\n", *sock);
+ ret = connect(*sock, (struct sockaddr *) &servaddr,
+ sizeof(servaddr));
+ if (ret < 0) {
+ RTE_LOG(ERR, APP, "Connection Error");
+ return ret;
+ }
+
+ RTE_LOG(INFO, APP, "Connected\n");
+ *connected = 1;
+ }
+
+ return ret;
+}
+
+/*
+ * Application main function - loops through
+ * receiving and processing packets. Never returns
+ */
+int
+main(int argc, char *argv[])
+{
+ const struct rte_memzone *mz;
+ int sock = SOCK_RESET;
+ unsigned int lcore_id;
+ unsigned int nb_ports;
+ int connected = 0;
+ char str[MSG_SIZE];
+ unsigned int i;
+ int ret;
+
+ ret = rte_eal_init(argc, argv);
+ if (ret < 0)
+ return -1;
+
+ argc -= ret;
+ argv += ret;
+
+ if (parse_app_args(argc, argv) < 0)
+ rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n");
+
+ /* initialize port forward array*/
+ forward_array_init();
+ port_map_init();
+
+ /* Check that there is an even number of ports to send/receive on. */
+ nb_ports = rte_eth_dev_count();
+ if (nb_ports > RTE_MAX_ETHPORTS)
+ nb_ports = RTE_MAX_ETHPORTS;
+
+ /* set up array for port data */
+ if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
+ mz = rte_memzone_lookup(MZ_PORT_INFO);
+ if (mz == NULL)
+ rte_exit(EXIT_FAILURE,
+ "Cannot get port info structure\n");
+ ports = mz->addr;
+ } else { /* RTE_PROC_PRIMARY */
+ mz = rte_memzone_reserve(MZ_PORT_INFO, sizeof(*ports),
+ rte_socket_id(), NO_FLAGS);
+ if (mz == NULL)
+ rte_exit(EXIT_FAILURE,
+ "Cannot reserve memory zone for port information\n");
+ memset(mz->addr, 0, sizeof(*ports));
+ ports = mz->addr;
+ }
+
+ RTE_LOG(INFO, APP, "Number of Ports: %d\n", nb_ports);
+
+ /* update port_forward_array with active port */
+ for (i = 0; i < nb_ports; i++) {
+ if (!rte_eth_dev_is_valid_port(i))
+ continue;
+
+ /* Update ports_fwd_array with phy port*/
+ ports_fwd_array[i].in_port_id = i;
+ port_map[i].port_type = PHY;
+ port_map[i].id = i;
+ port_map[i].stats = &ports->port_stats[i];
+ }
+
+ lcore_id = 0;
+ RTE_LCORE_FOREACH_SLAVE(lcore_id) {
+ rte_eal_remote_launch(main_loop, NULL, lcore_id);
+ }
+
+ RTE_LOG(INFO, APP, "My ID %d start handling message\n", client_id);
+ RTE_LOG(INFO, APP, "[Press Ctrl-C to quit ...]\n");
+
+ /* send and receive msg loop */
+ while (on) {
+ ret = do_connection(&connected, &sock);
+ if (ret < 0) {
+ sleep(1);
+ continue;
+ }
+
+ ret = do_receive(&connected, &sock, str);
+ if (ret < 0)
+ continue;
+
+ RTE_LOG(DEBUG, APP, "Received string: %s\n", str);
+
+ ret = parse_command(str);
+ if (ret < 0)
+ break;
+
+ /*Send the message back to client*/
+ ret = do_send(&connected, &sock, str);
+ if (ret < 0)
+ continue;
+ }
+
+ /* exit */
+ close(sock);
+ sock = SOCK_RESET;
+ RTE_LOG(INFO, APP, "spp_nfv exit.\n");
+ return 0;
+}
diff --git a/examples/multi_process/patch_panel/primary/Makefile b/examples/multi_process/patch_panel/primary/Makefile
new file mode 100644
index 0000000..ec4d98b
--- /dev/null
+++ b/examples/multi_process/patch_panel/primary/Makefile
@@ -0,0 +1,65 @@
+# BSD LICENSE
+#
+# Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# binary name
+APP = spp_primary
+
+# all source are stored in SRCS-y
+SRCS-y := main.c init.c args.c ../shared/common.c
+
+INC := $(wildcard *.h)
+
+CFLAGS += $(WERROR_FLAGS) -O3
+CFLAGS += -I$(SRCDIR)/../shared
+
+ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
+LDLIBS += -lrte_pmd_ring
+endif
+
+# for newer gcc, e.g. 4.4, no-strict-aliasing may not be necessary
+# and so the next line can be removed in those cases.
+EXTRA_CFLAGS += -fno-strict-aliasing
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/multi_process/patch_panel/primary/args.c b/examples/multi_process/patch_panel/primary/args.c
new file mode 100644
index 0000000..55433a1
--- /dev/null
+++ b/examples/multi_process/patch_panel/primary/args.c
@@ -0,0 +1,113 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <getopt.h>
+
+#include <rte_memory.h>
+
+#include "args.h"
+#include "common.h"
+#include "init.h"
+
+/* global var for number of clients - extern in header */
+uint8_t num_clients;
+char *server_ip;
+int server_port;
+
+static const char *progname;
+
+/**
+ * Prints out usage information to stdout
+ */
+static void
+usage(void)
+{
+ RTE_LOG(INFO, APP,
+ "%s [EAL options] -- -p PORTMASK -n NUM_CLIENTS [-s NUM_SOCKETS]\n"
+ " -p PORTMASK: hexadecimal bitmask of ports to use\n"
+ " -n NUM_CLIENTS: number of client processes to use\n"
+ , progname);
+}
+
+/**
+ * The application specific arguments follow the DPDK-specific
+ * arguments which are stripped by the DPDK init. This function
+ * processes these application arguments, printing usage info
+ * on error.
+ */
+int
+parse_app_args(uint8_t max_ports, int argc, char *argv[])
+{
+ int option_index, opt;
+ char **argvopt = argv;
+ struct option lgopts[] = { {0} };
+ int ret;
+
+ progname = argv[0];
+
+ while ((opt = getopt_long(argc, argvopt, "n:p:s:", lgopts,
+ &option_index)) != EOF) {
+ switch (opt) {
+ case 'p':
+ if (parse_portmask(ports, max_ports, optarg) != 0) {
+ usage();
+ return -1;
+ }
+ break;
+ case 'n':
+ if (parse_num_clients(&num_clients, optarg) != 0) {
+ usage();
+ return -1;
+ }
+ break;
+ case 's':
+ ret = parse_server(&server_ip, &server_port, optarg);
+ if (ret != 0) {
+ usage();
+ return -1;
+ }
+ break;
+ default:
+ RTE_LOG(ERR, APP, "ERROR: Unknown option '%c'\n", opt);
+ usage();
+ return -1;
+ }
+ }
+
+ if (ports->num_ports == 0 || num_clients == 0) {
+ usage();
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/examples/multi_process/patch_panel/primary/args.h b/examples/multi_process/patch_panel/primary/args.h
new file mode 100644
index 0000000..73e1f82
--- /dev/null
+++ b/examples/multi_process/patch_panel/primary/args.h
@@ -0,0 +1,45 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _ARGS_H_
+#define _ARGS_H_
+
+#include <stdint.h>
+
+extern uint8_t num_clients;
+extern char *server_ip;
+extern int server_port;
+
+int parse_app_args(uint8_t max_ports, int argc, char *argv[]);
+
+#endif /* ifndef _ARGS_H_ */
diff --git a/examples/multi_process/patch_panel/primary/init.c b/examples/multi_process/patch_panel/primary/init.c
new file mode 100644
index 0000000..d4675c6
--- /dev/null
+++ b/examples/multi_process/patch_panel/primary/init.c
@@ -0,0 +1,268 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <limits.h>
+
+#include <rte_cycles.h>
+#include <rte_ivshmem.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+
+#include "args.h"
+#include "common.h"
+#include "init.h"
+
+#define CLIENT_QUEUE_RINGSIZE 128
+
+#define IVSHMEN_METADATA_NAME "pp_ivshmem"
+#define QEMU_CMD_FMT "/tmp/ivshmem_qemu_cmdline_%s"
+
+/* array of info/queues for clients */
+struct client *clients;
+
+/* The mbuf pool for packet rx */
+static struct rte_mempool *pktmbuf_pool;
+
+/* the port details */
+struct port_info *ports;
+
+/**
+ * Initialise the mbuf pool for packet reception for the NIC, and any other
+ * buffer pools needed by the app - currently none.
+ */
+static int
+init_mbuf_pools(void)
+{
+ const unsigned int num_mbufs = (num_clients * MBUFS_PER_CLIENT)
+ + (ports->num_ports * MBUFS_PER_PORT);
+
+ /*
+ * don't pass single-producer/single-consumer flags to mbuf create as
+ * it seems faster to use a cache instead
+ */
+ RTE_LOG(DEBUG, APP, "Creating mbuf pool '%s' [%u mbufs] ...\n",
+ PKTMBUF_POOL_NAME, num_mbufs);
+
+ if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
+ pktmbuf_pool = rte_mempool_lookup(PKTMBUF_POOL_NAME);
+ if (pktmbuf_pool == NULL)
+ rte_exit(EXIT_FAILURE,
+ "Cannot get mempool for mbufs\n");
+ } else {
+ pktmbuf_pool = rte_mempool_create(PKTMBUF_POOL_NAME, num_mbufs,
+ MBUF_SIZE, MBUF_CACHE_SIZE,
+ sizeof(struct rte_pktmbuf_pool_private),
+ rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL,
+ rte_socket_id(), NO_FLAGS);
+ }
+
+ return (pktmbuf_pool == NULL); /* 0 on success */
+}
+
+/**
+ * Set up the DPDK rings which will be used to pass packets, via
+ * pointers, between the multi-process server and client processes.
+ * Each client needs one RX queue.
+ */
+static int
+init_shm_rings(void)
+{
+ const unsigned int ringsize = CLIENT_QUEUE_RINGSIZE;
+ unsigned int socket_id;
+ const char *q_name;
+ unsigned int i;
+
+ clients = rte_malloc("client details",
+ sizeof(*clients) * num_clients, 0);
+ if (clients == NULL)
+ rte_exit(EXIT_FAILURE,
+ "Cannot allocate memory for client program details\n");
+
+ for (i = 0; i < num_clients; i++) {
+ /* Create an RX queue for each client */
+ socket_id = rte_socket_id();
+ q_name = get_rx_queue_name(i);
+ if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
+ clients[i].rx_q = rte_ring_lookup(q_name);
+ } else {
+ clients[i].rx_q = rte_ring_create(q_name,
+ ringsize, socket_id,
+ /* single prod, single cons */
+ RING_F_SP_ENQ | RING_F_SC_DEQ);
+ }
+ if (clients[i].rx_q == NULL)
+ rte_exit(EXIT_FAILURE,
+ "Cannot create rx ring queue for client %u\n",
+ i);
+ }
+
+ return 0;
+}
+
+static int
+print_to_file(const char *cmdline, const char *config_name)
+{
+ FILE *file;
+ char path[PATH_MAX];
+
+ snprintf(path, sizeof(path), QEMU_CMD_FMT, config_name);
+ file = fopen(path, "w");
+ if (file == NULL) {
+ RTE_LOG(ERR, APP, "Could not open '%s'\n", path);
+ return -1;
+ }
+
+ RTE_LOG(INFO, APP, "QEMU command line for config '%s': %s\n",
+ config_name, cmdline);
+
+ fprintf(file, "%s\n", cmdline);
+ fclose(file);
+
+ return 0;
+}
+
+static int
+generate_ivshmem_cmdline(const char *config_name)
+{
+ char cmdline[PATH_MAX];
+
+ if (rte_ivshmem_metadata_cmdline_generate(cmdline, sizeof(cmdline),
+ config_name) < 0)
+ return -1;
+
+ if (print_to_file(cmdline, config_name) < 0)
+ return -1;
+
+ rte_ivshmem_metadata_dump(stdout, config_name);
+
+ return 0;
+}
+
+/**
+ * Main init function for the multi-process server app,
+ * calls subfunctions to do each stage of the initialisation.
+ */
+int
+init(int argc, char *argv[])
+{
+ int retval;
+ const struct rte_memzone *mz;
+ uint8_t i, total_ports;
+
+ /* init EAL, parsing EAL args */
+ retval = rte_eal_init(argc, argv);
+ if (retval < 0)
+ return -1;
+
+ argc -= retval;
+ argv += retval;
+
+ /* get total number of ports */
+ total_ports = rte_eth_dev_count();
+
+ /* set up array for port data */
+ if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
+ mz = rte_memzone_lookup(MZ_PORT_INFO);
+ if (mz == NULL)
+ rte_exit(EXIT_FAILURE,
+ "Cannot get port info structure\n");
+ ports = mz->addr;
+ } else { /* RTE_PROC_PRIMARY */
+ mz = rte_memzone_reserve(MZ_PORT_INFO, sizeof(*ports),
+ rte_socket_id(), NO_FLAGS);
+ if (mz == NULL)
+ rte_exit(EXIT_FAILURE,
+ "Cannot reserve memory zone for port information\n");
+ memset(mz->addr, 0, sizeof(*ports));
+ ports = mz->addr;
+ }
+
+ /* parse additional, application arguments */
+ retval = parse_app_args(total_ports, argc, argv);
+ if (retval != 0)
+ return -1;
+
+ /* initialise mbuf pools */
+ retval = init_mbuf_pools();
+ if (retval != 0)
+ rte_exit(EXIT_FAILURE, "Cannot create needed mbuf pools\n");
+
+ /* now initialise the ports we will use */
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+ for (i = 0; i < ports->num_ports; i++) {
+ retval = init_port(ports->id[i], pktmbuf_pool);
+ if (retval != 0)
+ rte_exit(EXIT_FAILURE,
+ "Cannot initialise port %d\n", i);
+ }
+ }
+ check_all_ports_link_status(ports, ports->num_ports, (~0x0));
+
+ /* initialise the client queues/rings for inter-eu comms */
+ init_shm_rings();
+
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+ int ret;
+
+ /* create metadata, output cmdline */
+ ret = rte_ivshmem_metadata_create(IVSHMEN_METADATA_NAME);
+ if (ret < 0)
+ rte_exit(EXIT_FAILURE,
+ "Cannot create IVSHMEM metadata\n");
+
+ ret = rte_ivshmem_metadata_add_memzone(mz,
+ IVSHMEN_METADATA_NAME);
+ if (ret)
+ rte_exit(EXIT_FAILURE,
+ "Cannot add memzone to IVSHMEM metadata\n");
+
+ ret = rte_ivshmem_metadata_add_mempool(pktmbuf_pool,
+ IVSHMEN_METADATA_NAME);
+ if (ret)
+ rte_exit(EXIT_FAILURE,
+ "Cannot add mbuf mempool to IVSHMEM metadata\n");
+
+ for (i = 0; i < num_clients; i++) {
+ ret = rte_ivshmem_metadata_add_ring(clients[i].rx_q,
+ IVSHMEN_METADATA_NAME);
+ if (ret < 0)
+ rte_exit(EXIT_FAILURE,
+ "Cannot add ring client %d to IVSHMEM metadata\n",
+ i);
+ }
+
+ generate_ivshmem_cmdline(IVSHMEN_METADATA_NAME);
+ }
+
+ return 0;
+}
diff --git a/examples/multi_process/patch_panel/primary/init.h b/examples/multi_process/patch_panel/primary/init.h
new file mode 100644
index 0000000..915552c
--- /dev/null
+++ b/examples/multi_process/patch_panel/primary/init.h
@@ -0,0 +1,65 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _INIT_H_
+#define _INIT_H_
+
+#include <stdint.h>
+
+/*
+ * Define a client structure with all needed info, including
+ * stats from the clients.
+ */
+struct client {
+ struct rte_ring *rx_q;
+ unsigned int client_id;
+ /*
+ * These stats hold how many packets the client will actually receive,
+ * and how many packets were dropped because the client's queue was full
+ * The port-info stats, in contrast, record how many packets were
+ * received or transmitted on an actual NIC port.
+ */
+ struct {
+ uint64_t rx;
+ uint64_t rx_drop;
+ } stats;
+};
+
+extern struct client *clients;
+
+/* the shared port information: port numbers, rx and tx stats etc. */
+extern struct port_info *ports;
+
+int init(int argc, char *argv[]);
+
+#endif /* ifndef _INIT_H_ */
diff --git a/examples/multi_process/patch_panel/primary/main.c b/examples/multi_process/patch_panel/primary/main.c
new file mode 100644
index 0000000..15fa53a
--- /dev/null
+++ b/examples/multi_process/patch_panel/primary/main.c
@@ -0,0 +1,347 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <poll.h>
+
+#include <rte_atomic.h>
+#include <rte_eth_ring.h>
+
+#include "args.h"
+#include "common.h"
+#include "init.h"
+
+static sig_atomic_t on = 1;
+
+static enum cmd_type cmd = STOP;
+
+static struct pollfd pfd;
+
+static void
+turn_off(int sig)
+{
+ on = 0;
+ RTE_LOG(INFO, APP, "terminated %d\n", sig);
+}
+
+static const char *
+get_printable_mac_addr(uint8_t port)
+{
+ static const char err_address[] = "00:00:00:00:00:00";
+ static char addresses[RTE_MAX_ETHPORTS][sizeof(err_address)];
+
+ if (unlikely(port >= RTE_MAX_ETHPORTS))
+ return err_address;
+
+ if (unlikely(addresses[port][0] == '\0')) {
+ struct ether_addr mac;
+
+ rte_eth_macaddr_get(port, &mac);
+ snprintf(addresses[port], sizeof(addresses[port]),
+ "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac.addr_bytes[0], mac.addr_bytes[1],
+ mac.addr_bytes[2], mac.addr_bytes[3],
+ mac.addr_bytes[4], mac.addr_bytes[5]);
+ }
+
+ return addresses[port];
+}
+
+/*
+ * This function displays the recorded statistics for each port
+ * and for each client. It uses ANSI terminal codes to clear
+ * screen when called. It is called from a single non-master
+ * thread in the server process, when the process is run with more
+ * than one lcore enabled.
+ */
+static void
+do_stats_display(void)
+{
+ const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' };
+ const char clr[] = { 27, '[', '2', 'J', '\0' };
+ unsigned int i;
+
+ /* Clear screen and move to top left */
+ printf("%s%s", clr, topLeft);
+
+ printf("PORTS\n");
+ printf("-----\n");
+ for (i = 0; i < ports->num_ports; i++)
+ printf("Port %u: '%s'\t", ports->id[i],
+ get_printable_mac_addr(ports->id[i]));
+ printf("\n\n");
+ for (i = 0; i < ports->num_ports; i++) {
+ printf("Port %u - rx: %9"PRIu64"\t tx: %9"PRIu64"\t"
+ " tx_drop: %9"PRIu64"\n",
+ ports->id[i], ports->port_stats[i].rx,
+ ports->port_stats[i].tx,
+ ports->client_stats[i].tx_drop);
+ }
+
+ printf("\nCLIENTS\n");
+ printf("-------\n");
+ for (i = 0; i < num_clients; i++) {
+ printf("Client %2u - rx: %9"PRIu64", rx_drop: %9"PRIu64"\n"
+ " tx: %9"PRIu64", tx_drop: %9"PRIu64"\n",
+ i, ports->client_stats[i].rx,
+ ports->client_stats[i].rx_drop,
+ ports->client_stats[i].tx,
+ ports->client_stats[i].tx_drop);
+ }
+
+ printf("\n");
+}
+
+/*
+ * The function called from each non-master lcore used by the process.
+ * The test_and_set function is used to randomly pick a single lcore on which
+ * the code to display the statistics will run. Otherwise, the code just
+ * repeatedly sleeps.
+ */
+static int
+sleep_lcore(void *dummy __rte_unused)
+{
+ /* Used to pick a display thread - static, so zero-initialised */
+ static rte_atomic32_t display_stats;
+
+ /* Only one core should display stats */
+ if (rte_atomic32_test_and_set(&display_stats)) {
+ const unsigned int sleeptime = 1;
+
+ RTE_LOG(INFO, APP, "Core %u displaying statistics\n",
+ rte_lcore_id());
+
+ /* Longer initial pause so above log is seen */
+ sleep(sleeptime * 3);
+
+ /* Loop forever: sleep always returns 0 or <= param */
+ while (sleep(sleeptime) <= sleeptime && on)
+ do_stats_display();
+ }
+
+ return 0;
+}
+
+/*
+ * Function to set all the client statistic values to zero.
+ * Called at program startup.
+ */
+static void
+clear_stats(void)
+{
+ memset(ports->port_stats, 0, sizeof(struct stats) * RTE_MAX_ETHPORTS);
+ memset(ports->client_stats, 0, sizeof(struct stats) * MAX_CLIENT);
+}
+
+static int
+do_send(int *connected, int *sock, char *str)
+{
+ int ret;
+
+ ret = send(*sock, str, MSG_SIZE, 0);
+ if (ret == -1) {
+ RTE_LOG(ERR, APP, "send failed");
+ *connected = 0;
+ return -1;
+ }
+
+ RTE_LOG(INFO, APP, "To Server: %s\n", str);
+
+ return 0;
+}
+
+static int
+parse_command(char *str)
+{
+ char *token_list[MAX_PARAMETER] = {NULL};
+ int ret = 0;
+ int i = 0;
+
+ /* tokenize the user commands from controller */
+ token_list[i] = strtok(str, " ");
+ while (token_list[i] != NULL) {
+ RTE_LOG(DEBUG, APP, "token %d = %s\n", i, token_list[i]);
+ i++;
+ token_list[i] = strtok(NULL, " ");
+ }
+
+ if (!strcmp(token_list[0], "status")) {
+ RTE_LOG(DEBUG, APP, "status\n");
+
+ memset(str, '\0', MSG_SIZE);
+ if (cmd == START)
+ sprintf(str, "Server Running\n");
+ else
+ sprintf(str, "Server Idling\n");
+
+ } else if (!strcmp(token_list[0], "exit")) {
+ RTE_LOG(DEBUG, APP, "exit\n");
+ RTE_LOG(DEBUG, APP, "stop\n");
+ cmd = STOP;
+ ret = -1;
+
+ } else if (!strcmp(token_list[0], "clear")) {
+ clear_stats();
+ }
+
+ return ret;
+}
+
+static int
+do_receive(int *connected, int *sock, char *str)
+{
+ int ret;
+
+ memset(str, '\0', MSG_SIZE);
+
+#define POLL_TIMEOUT_MS 100
+ ret = poll(&pfd, 1, POLL_TIMEOUT_MS);
+ if (ret <= 0) {
+ if (ret < 0) {
+ close(*sock);
+ *sock = SOCK_RESET;
+ *connected = 0;
+ }
+ return -1;
+ }
+
+ ret = recv(*sock, str, MSG_SIZE, 0);
+ if (ret <= 0) {
+ RTE_LOG(DEBUG, APP, "Receive count: %d\n", ret);
+
+ if (ret < 0)
+ RTE_LOG(ERR, APP, "Receive Fail");
+ else
+ RTE_LOG(INFO, APP, "Receive 0\n");
+
+ RTE_LOG(INFO, APP, "Assume Server closed connection\n");
+ close(*sock);
+ *sock = SOCK_RESET;
+ *connected = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+do_connection(int *connected, int *sock)
+{
+ static struct sockaddr_in servaddr;
+ int ret = 0;
+
+ if (*connected == 0) {
+ if (*sock < 0) {
+ RTE_LOG(INFO, APP, "Creating socket...\n");
+ *sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (*sock < 0)
+ rte_exit(EXIT_FAILURE, "socket error\n");
+
+ /* Creation of the socket */
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr.s_addr = inet_addr(server_ip);
+ servaddr.sin_port = htons(server_port);
+
+ pfd.fd = *sock;
+ pfd.events = POLLIN;
+ }
+
+ RTE_LOG(INFO, APP, "Trying to connect ... socket %d\n", *sock);
+ ret = connect(*sock, (struct sockaddr *) &servaddr,
+ sizeof(servaddr));
+ if (ret < 0) {
+ RTE_LOG(ERR, APP, "Connection Error");
+ return ret;
+ }
+
+ RTE_LOG(INFO, APP, "Connected\n");
+ *connected = 1;
+ }
+
+ return ret;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int sock = SOCK_RESET;
+ int connected = 0;
+ char str[MSG_SIZE];
+ int ret;
+
+ /* Register signals */
+ signal(SIGINT, turn_off);
+
+ /* initialise the system */
+ if (init(argc, argv) < 0)
+ return -1;
+
+ RTE_LOG(INFO, APP, "Finished Process Init.\n");
+
+ /* clear statistics */
+ clear_stats();
+
+ /* put all other cores to sleep bar master */
+ rte_eal_mp_remote_launch(sleep_lcore, NULL, SKIP_MASTER);
+
+ while (on) {
+ ret = do_connection(&connected, &sock);
+ if (ret < 0) {
+ sleep(1);
+ continue;
+ }
+
+ ret = do_receive(&connected, &sock, str);
+ if (ret < 0)
+ continue;
+
+ RTE_LOG(DEBUG, APP, "Received string: %s\n", str);
+
+ ret = parse_command(str);
+ if (ret < 0)
+ break;
+
+ /* Send the message back to client */
+ ret = do_send(&connected, &sock, str);
+ if (ret < 0)
+ continue;
+ }
+
+ /* exit */
+ close(sock);
+ sock = SOCK_RESET;
+ RTE_LOG(INFO, APP, "spp_primary exit.\n");
+ return 0;
+}
diff --git a/examples/multi_process/patch_panel/shared/common.c b/examples/multi_process/patch_panel/shared/common.c
new file mode 100644
index 0000000..da73cfe
--- /dev/null
+++ b/examples/multi_process/patch_panel/shared/common.c
@@ -0,0 +1,251 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <rte_cycles.h>
+
+#include "common.h"
+
+/* Check the link status of all ports in up to 9s, and print them finally */
+void
+check_all_ports_link_status(struct port_info *ports, uint8_t port_num,
+ uint32_t port_mask)
+{
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
+ uint8_t portid, count, all_ports_up;
+ struct rte_eth_link link;
+
+ RTE_LOG(INFO, APP, "\nChecking link status");
+ fflush(stdout);
+ for (count = 0; count <= MAX_CHECK_TIME; count++) {
+ all_ports_up = 1;
+ for (portid = 0; portid < port_num; portid++) {
+ if ((port_mask & (1 << ports->id[portid])) == 0)
+ continue;
+
+ memset(&link, 0, sizeof(link));
+ rte_eth_link_get_nowait(ports->id[portid], &link);
+
+ /* clear all_ports_up flag if any link down */
+ if (link.link_status == 0) {
+ all_ports_up = 0;
+ break;
+ }
+ }
+
+ if (all_ports_up == 0) {
+ printf(".");
+ fflush(stdout);
+ rte_delay_ms(CHECK_INTERVAL);
+ } else {
+ printf("done\n");
+ break;
+ }
+ }
+
+ /* all ports up or timed out */
+ for (portid = 0; portid < port_num; portid++) {
+ if ((port_mask & (1 << ports->id[portid])) == 0)
+ continue;
+
+ memset(&link, 0, sizeof(link));
+ rte_eth_link_get_nowait(ports->id[portid], &link);
+
+ /* print link status */
+ if (link.link_status)
+ RTE_LOG(INFO, APP,
+ "Port %d Link Up - speed %u Mbps - %s\n",
+ ports->id[portid], link.link_speed,
+ (link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
+ "full-duplex\n" : "half-duplex\n");
+ else
+ RTE_LOG(INFO, APP,
+ "Port %d Link Down\n", ports->id[portid]);
+ }
+}
+
+/**
+ * Initialise an individual port:
+ * - configure number of rx and tx rings
+ * - set up each rx ring, to pull from the main mbuf pool
+ * - set up each tx ring
+ * - start the port and report its status to stdout
+ */
+int
+init_port(uint8_t port_num, struct rte_mempool *pktmbuf_pool)
+{
+ /* for port configuration all features are off by default */
+ const struct rte_eth_conf port_conf = {
+ .rxmode = {
+ .mq_mode = ETH_MQ_RX_RSS,
+ },
+ };
+ const uint16_t rx_rings = 1, tx_rings = 1;
+ const uint16_t rx_ring_size = RTE_MP_RX_DESC_DEFAULT;
+ const uint16_t tx_ring_size = RTE_MP_TX_DESC_DEFAULT;
+ uint16_t q;
+ int retval;
+
+ RTE_LOG(INFO, APP, "Port %u init ... ", port_num);
+ fflush(stdout);
+
+ /*
+ * Standard DPDK port initialisation - config port, then set up
+ * rx and tx rings
+ */
+ retval = rte_eth_dev_configure(port_num, rx_rings, tx_rings,
+ &port_conf);
+ if (retval != 0)
+ return retval;
+
+ for (q = 0; q < rx_rings; q++) {
+ retval = rte_eth_rx_queue_setup(port_num, q, rx_ring_size,
+ rte_eth_dev_socket_id(port_num), NULL, pktmbuf_pool);
+ if (retval < 0)
+ return retval;
+ }
+
+ for (q = 0; q < tx_rings; q++) {
+ retval = rte_eth_tx_queue_setup(port_num, q, tx_ring_size,
+ rte_eth_dev_socket_id(port_num), NULL);
+ if (retval < 0)
+ return retval;
+ }
+
+ rte_eth_promiscuous_enable(port_num);
+
+ retval = rte_eth_dev_start(port_num);
+ if (retval < 0)
+ return retval;
+
+ RTE_LOG(INFO, APP, "Port %d Init done\n", port_num);
+
+ return 0;
+}
+
+/**
+ * The ports to be used by the application are passed in
+ * the form of a bitmask. This function parses the bitmask
+ * and places the port numbers to be used into the port[]
+ * array variable
+ */
+int
+parse_portmask(struct port_info *ports, uint8_t max_ports,
+ const char *portmask)
+{
+ char *end = NULL;
+ unsigned long pm;
+ uint8_t count = 0;
+
+ if (portmask == NULL || *portmask == '\0')
+ return -1;
+
+ /* convert parameter to a number and verify */
+ pm = strtoul(portmask, &end, 16);
+ if (end == NULL || *end != '\0' || pm == 0)
+ return -1;
+
+ /* loop through bits of the mask and mark ports */
+ while (pm != 0) {
+ if (pm & 0x01) { /* bit is set in mask, use port */
+ if (count >= max_ports)
+ RTE_LOG(WARNING, APP,
+ "requested port %u not present - ignoring\n",
+ count);
+ else
+ ports->id[ports->num_ports++] = count;
+ }
+ pm = (pm >> 1);
+ count++;
+ }
+
+ return 0;
+}
+
+/**
+ * Take the number of clients parameter passed to the app
+ * and convert to a number to store in the num_clients variable
+ */
+int
+parse_num_clients(uint8_t *num_clients, const char *clients)
+{
+ char *end = NULL;
+ unsigned long temp;
+
+ if (clients == NULL || *clients == '\0')
+ return -1;
+
+ temp = strtoul(clients, &end, 10);
+ if (end == NULL || *end != '\0' || temp == 0)
+ return -1;
+
+ *num_clients = (uint8_t)temp;
+ return 0;
+}
+
+int
+parse_server(char **server_ip, int *server_port, char *server_addr)
+{
+ const char delim[2] = ":";
+ char *token;
+
+ if (server_addr == NULL || *server_addr == '\0')
+ return -1;
+
+ *server_ip = strtok(server_addr, delim);
+ RTE_LOG(DEBUG, APP, "server ip %s\n", *server_ip);
+
+ token = strtok(NULL, delim);
+ RTE_LOG(DEBUG, APP, "token %s\n", token);
+ if (token == NULL || *token == '\0')
+ return -1;
+
+ RTE_LOG(DEBUG, APP, "token %s\n", token);
+ *server_port = atoi(token);
+ return 0;
+}
+
+int
+spp_atoi(const char *str, int *val)
+{
+ char *end;
+
+ *val = strtol(str, &end, 10);
+
+ if (*end) {
+ RTE_LOG(ERR, APP, "Bad integer value: %s\n", str);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/examples/multi_process/patch_panel/shared/common.h b/examples/multi_process/patch_panel/shared/common.h
new file mode 100644
index 0000000..6ae4ac9
--- /dev/null
+++ b/examples/multi_process/patch_panel/shared/common.h
@@ -0,0 +1,202 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <rte_branch_prediction.h>
+#include <rte_common.h>
+#include <rte_config.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_log.h>
+#include <rte_mbuf.h>
+#include <rte_mempool.h>
+#include <rte_ring.h>
+
+#define MAX_CLIENT 99
+#define MSG_SIZE 1000
+#define SOCK_RESET -1
+#define PORT_RESET -99
+
+/*
+ * When doing reads from the NIC or the client queues,
+ * use this batch size
+ */
+#define PACKET_READ_SIZE 32
+#define MAX_PKT_BURST 32
+
+#define MAX_PARAMETER 10
+
+#define MBUFS_PER_CLIENT 1536
+#define MBUFS_PER_PORT 1536
+#define MBUF_CACHE_SIZE 512
+#define MBUF_OVERHEAD (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
+#define RX_MBUF_DATA_SIZE 2048
+#define MBUF_SIZE (RX_MBUF_DATA_SIZE + MBUF_OVERHEAD)
+
+#define RTE_MP_RX_DESC_DEFAULT 512
+#define RTE_MP_TX_DESC_DEFAULT 512
+
+#define NO_FLAGS 0
+
+/* Command. */
+enum cmd_type {
+ STOP,
+ START,
+ FORWARD,
+};
+
+/*
+ * Shared port info, including statistics information for display by server.
+ * Structure will be put in a memzone.
+ * - All port id values share one cache line as this data will be read-only
+ * during operation.
+ * - All rx statistic values share cache lines, as this data is written only
+ * by the server process. (rare reads by stats display)
+ * - The tx statistics have values for all ports per cache line, but the stats
+ * themselves are written by the clients, so we have a distinct set, on
+ * different cache lines for each client to use.
+ */
+
+struct stats {
+ uint64_t rx;
+ uint64_t rx_drop;
+ uint64_t tx;
+ uint64_t tx_drop;
+} __rte_cache_aligned;
+
+struct port_info {
+ uint8_t num_ports;
+ uint8_t id[RTE_MAX_ETHPORTS];
+ struct stats port_stats[RTE_MAX_ETHPORTS];
+ struct stats client_stats[MAX_CLIENT];
+};
+
+enum port_type {
+ PHY,
+ RING,
+ VHOST,
+ UNDEF,
+};
+
+struct port_map {
+ int id;
+ enum port_type port_type;
+ struct stats *stats;
+ struct stats default_stats;
+};
+
+struct port {
+ int in_port_id;
+ int out_port_id;
+ uint16_t (*rx_func)(uint8_t, uint16_t, struct rte_mbuf **, uint16_t);
+ uint16_t (*tx_func)(uint8_t, uint16_t, struct rte_mbuf **, uint16_t);
+};
+
+/* define common names for structures shared between server and client */
+#define MP_CLIENT_RXQ_NAME "eth_ring%u"
+#define PKTMBUF_POOL_NAME "MProc_pktmbuf_pool"
+#define VM_PKTMBUF_POOL_NAME "VM_Proc_pktmbuf_pool"
+#define VM_MZ_PORT_INFO "VM_Proc_port_info"
+#define MZ_PORT_INFO "MProc_port_info"
+#define VHOST_BACKEND_NAME "eth_vhost%u"
+#define VHOST_IFACE_NAME "/tmp/sock%u"
+
+/*
+ * Given the rx queue name template above, get the queue name
+ */
+static inline const char *
+get_rx_queue_name(unsigned int id)
+{
+ /*
+ * buffer for return value. Size calculated by %u being replaced
+ * by maximum 3 digits (plus an extra byte for safety)
+ */
+ static char buffer[sizeof(MP_CLIENT_RXQ_NAME) + 2];
+
+ snprintf(buffer, sizeof(buffer) - 1, MP_CLIENT_RXQ_NAME, id);
+ return buffer;
+}
+
+static inline const char *
+get_vhost_backend_name(unsigned int id)
+{
+ /*
+ * buffer for return value. Size calculated by %u being replaced
+ * by maximum 3 digits (plus an extra byte for safety)
+ */
+ static char buffer[sizeof(VHOST_BACKEND_NAME) + 2];
+
+ snprintf(buffer, sizeof(buffer) - 1, VHOST_BACKEND_NAME, id);
+ return buffer;
+}
+
+static inline char *
+get_vhost_iface_name(unsigned int id)
+{
+ /*
+ * buffer for return value. Size calculated by %u being replaced
+ * by maximum 3 digits (plus an extra byte for safety)
+ */
+ static char buffer[sizeof(VHOST_IFACE_NAME) + 2];
+
+ snprintf(buffer, sizeof(buffer) - 1, VHOST_IFACE_NAME, id);
+ return buffer;
+}
+
+void check_all_ports_link_status(struct port_info *ports, uint8_t port_num,
+ uint32_t port_mask);
+
+int init_port(uint8_t port_num, struct rte_mempool *pktmbuf_pool);
+
+int parse_portmask(struct port_info *ports, uint8_t max_ports,
+ const char *portmask);
+int parse_num_clients(uint8_t *num_clients, const char *clients);
+int parse_server(char **server_ip, int *server_port, char *server_addr);
+
+int spp_atoi(const char *str, int *val);
+
+#define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1
+
+#endif
diff --git a/examples/multi_process/patch_panel/spp.py b/examples/multi_process/patch_panel/spp.py
new file mode 100755
index 0000000..687742c
--- /dev/null
+++ b/examples/multi_process/patch_panel/spp.py
@@ -0,0 +1,431 @@
+#!/usr/bin/python
+"""Soft Patch Panel"""
+
+from __future__ import print_function
+from Queue import Queue
+from thread import start_new_thread
+from threading import Thread
+import cmd
+import getopt
+import select
+import socket
+import sys
+
+class GrowingList(list):
+ """GrowingList"""
+
+ def __setitem__(self, index, value):
+ if index >= len(self):
+ self.extend([None]*(index + 1 - len(self)))
+ list.__setitem__(self, index, value)
+
+MAX_SECONDARY = 16
+
+# init
+PRIMARY = ''
+SECONDARY_LIST = []
+SECONDARY_COUNT = 0
+
+#init primary comm channel
+MAIN2PRIMARY = Queue()
+PRIMARY2MAIN = Queue()
+
+#init secondary comm channel list
+MAIN2SEC = GrowingList()
+SEC2MAIN = GrowingList()
+
+def connectionthread(name, client_id, conn, m2s, s2m):
+ """Manage secondary process connections"""
+
+ cmd_str = 'hello'
+
+ #infinite loop so that function do not terminate and thread do not end.
+ while True:
+ try:
+ _, _, _ = select.select([conn,], [conn,], [], 5)
+ except select.error:
+ break
+
+ #Sending message to connected secondary
+ try:
+ cmd_str = m2s.get(True)
+ conn.send(cmd_str) #send only takes string
+ except KeyError:
+ break
+ except Exception, excep:
+ print (str(excep))
+ break
+
+ #Receiving from secondary
+ try:
+ data = conn.recv(1024) # 1024 stands for bytes of data to be received
+ if data:
+ s2m.put("recv:" + str(conn.fileno()) + ":" + "{" + data + "}")
+ else:
+ s2m.put("closing:" + str(conn))
+ break
+ except Exception, excep:
+ print (str(excep))
+ break
+
+ SECONDARY_LIST.remove(client_id)
+ conn.close()
+
+def getclientid(conn):
+ """Get client_id from client"""
+
+ try:
+ conn.send("_get_client_id")
+ except KeyError:
+ return -1
+
+ data = conn.recv(1024)
+ if data == None:
+ return -1
+
+ client_id = int(data.strip('\0'))
+
+ if client_id < 0 or client_id > MAX_SECONDARY:
+ return -1
+
+ found = 0
+ for i in SECONDARY_LIST:
+ if client_id == i:
+ found = 1
+ break
+
+ if found == 0:
+ return client_id
+
+ # client_id in use, find a free one
+ free_client_id = -1
+ for i in range(MAX_SECONDARY):
+ found = -1
+ for j in SECONDARY_LIST:
+ if i == j:
+ found = i
+ break
+ if found == -1:
+ free_client_id = i
+ break
+
+ if free_client_id < 0:
+ return -1
+
+ conn.send("_set_client_id %u" % free_client_id)
+ data = conn.recv(1024)
+
+ return free_client_id
+
+def acceptthread(sock, main2sec, sec2main):
+ """Listen for secondary processes"""
+
+ global SECONDARY_COUNT
+
+ try:
+ while True:
+ #Accepting incoming connections
+ conn, _ = sock.accept()
+
+ client_id = getclientid(conn)
+ if client_id < 0:
+ break
+
+ #Creating new thread.
+ #Calling secondarythread function for this function and passing
+ #conn as argument.
+
+ SECONDARY_LIST.append(client_id)
+ main2sec[client_id] = Queue()
+ sec2main[client_id] = Queue()
+ start_new_thread(connectionthread,
+ ('secondary', client_id, conn,
+ main2sec[client_id],
+ sec2main[client_id], ))
+ SECONDARY_COUNT += 1
+ except Exception, excep:
+ print (str(excep))
+ sock.close()
+
+def command_primary(command):
+ """Send command to primary process"""
+
+ if PRIMARY:
+ MAIN2PRIMARY.put(command)
+ print (PRIMARY2MAIN.get(True))
+ else:
+ print ("primary not started")
+
+def command_secondary(sec_id, command):
+ """Send command to secondary process with sec_id"""
+
+ if sec_id in SECONDARY_LIST:
+ MAIN2SEC[sec_id].put(command)
+ print (SEC2MAIN[sec_id].get(True))
+ else:
+ print ("secondary id %d not exist" % sec_id)
+
+def print_status():
+ """Display information about connected clients"""
+
+ print ("Soft Patch Panel Status :")
+ print ("primary: %d" % PRIMARY)
+ print ("secondary count: %d" % len(SECONDARY_LIST))
+ for i in SECONDARY_LIST:
+ print ("Connected secondary id: %d" % i)
+
+def primarythread(sock, main2primary, primary2main):
+ """Manage primary process connection"""
+
+ global PRIMARY
+ cmd_str = ''
+
+ while True:
+ #waiting for connection
+ PRIMARY = False
+ conn, addr = sock.accept()
+ PRIMARY = True
+
+ while conn:
+ try:
+ _, _, _ = select.select([conn,], [conn,], [], 5)
+ except select.error:
+ break
+
+ #Sending message to connected primary
+ try:
+ cmd_str = main2primary.get(True)
+ conn.send(cmd_str) #send only takes string
+ except KeyError:
+ break
+ except Exception, excep:
+ print (str(excep))
+ break
+
+ #Receiving from primary
+ try:
+ data = conn.recv(1024) # 1024 stands for bytes of data to be received
+ if data:
+ primary2main.put("recv:" + str(addr) + ":" + "{" + data + "}")
+ else:
+ primary2main.put("closing:" + str(addr))
+ conn.close()
+ break
+ except Exception, excep:
+ print (str(excep))
+ break
+
+ print ("primary communication thread end")
+
+def close_all_secondary():
+ """Exit all secondary processes"""
+
+ global SECONDARY_COUNT
+
+ tmp_list = []
+ for i in SECONDARY_LIST:
+ tmp_list.append(i)
+ for i in tmp_list:
+ command_secondary(i, 'exit')
+ SECONDARY_COUNT = 0
+
+def check_sec_cmds(cmds):
+ """Validate secondary commands before sending"""
+
+ level1 = ['status', 'exit', 'forward', 'stop']
+ level2 = ['add', 'patch', 'del']
+ patch_args = ['reset']
+ add_del_args = ['ring', 'vhost']
+ cmdlist = cmds.split(' ')
+ valid = 0
+
+ length = len(cmdlist)
+ if length == 1:
+ if cmdlist[0] in level1:
+ valid = 1
+ elif length == 2:
+ if cmdlist[0] == 'patch':
+ if cmdlist[1] in patch_args:
+ valid = 1
+ elif length == 3:
+ if cmdlist[0] in level2:
+ if cmdlist[0] == 'add' or cmdlist[0] == 'del':
+ if cmdlist[1] in add_del_args:
+ if str.isdigit(cmdlist[2]):
+ valid = 1
+ elif cmdlist[0] == 'patch':
+ if str.isdigit(cmdlist[1]) and str.isdigit(cmdlist[2]):
+ valid = 1
+
+ return valid
+
+class Shell(cmd.Cmd):
+ """SPP command prompt"""
+
+ intro = 'Welcome to the spp. Type help or ? to list commands.\n'
+ prompt = 'spp > '
+ recorded_file = None
+
+ COMMANDS = ['status', 'add', 'patch', 'ring', 'vhost',
+ 'reset', 'exit', 'forward', 'stop', 'clear']
+
+ def complete_pri(self, text, line, begidx, endidx):
+ """Completion for primary process commands"""
+
+ if not text:
+ completions = self.COMMANDS[:]
+ else:
+ completions = [p
+ for p in self.COMMANDS
+ if p.startswith(text)
+ ]
+ return completions
+
+ def complete_sec(self, text, line, begidx, endidx):
+ """Completion for secondary process commands"""
+
+ if not text:
+ completions = self.COMMANDS[:]
+ else:
+ completions = [p
+ for p in self.COMMANDS
+ if p.startswith(text)
+ ]
+ return completions
+
+ def do_status(self, _):
+ """Display Soft Patch Panel Status"""
+
+ print_status()
+
+ def do_pri(self, command):
+ """Send command to primary process"""
+
+ if command and command in self.COMMANDS:
+ command_primary(command)
+ else:
+ print ("primary invalid command")
+
+ def do_sec(self, arg):
+ """Send command to secondary process"""
+
+ cmds = arg.split(';')
+ if len(cmds) < 2:
+ print ("error")
+ elif str.isdigit(cmds[0]):
+ sec_id = int(cmds[0])
+ if check_sec_cmds(cmds[1]):
+ command_secondary(sec_id, cmds[1])
+ else:
+ print ("invalid cmd")
+ else:
+ print (cmds[0])
+ print ("first %s" % cmds[1])
+
+ def do_record(self, arg):
+ """Save future commands to filename: RECORD filename.cmd"""
+
+ self.recorded_file = open(arg, 'w')
+
+ def do_playback(self, arg):
+ """Playback commands from a file: PLAYBACK filename.cmd"""
+
+ self.close()
+ try:
+ with open(arg) as recorded_file:
+ lines = []
+ for line in recorded_file:
+ if line.strip().startswith("#"):
+ continue
+ lines.append(line)
+ self.cmdqueue.extend(lines)
+ except IOError:
+ print ("Error: File does not exist.")
+
+ def precmd(self, line):
+ line = line.lower()
+ if self.recorded_file and 'playback' not in line:
+ print(line, file=self.recorded_file)
+ return line
+
+ def close(self):
+ """Close record file"""
+
+ if self.recorded_file:
+ print("closing file")
+ self.recorded_file.close()
+ self.recorded_file = None
+
+ def do_bye(self, arg):
+ """Stop recording, close SPP, and exit: BYE"""
+
+ cmds = arg.split(' ')
+ if cmds[0] == 'sec':
+ close_all_secondary()
+ elif cmds[0] == 'all':
+ close_all_secondary()
+ command_primary('exit')
+
+ print('Thank you for using Soft Patch Panel')
+ self.close()
+ return True
+
+def main(argv):
+ """main"""
+
+ # Defining server address and port
+ host = '' #'localhost' or '127.0.0.1' or '' are all same
+
+ try:
+ opts, _ = getopt.getopt(argv, "p:s:h", ["help", "primary = ", "secondary"])
+ except getopt.GetoptError:
+ print ('spp.py -p <primary__port_number> -s <secondary_port_number>')
+ sys.exit(2)
+ for opt, arg in opts:
+ if opt in ("-h", "--help"):
+ print ('spp.py -p <primary__port_number> -s <secondary_port_number>')
+ sys.exit()
+ elif opt in ("-p", "--primary"):
+ primary_port = int(arg)
+ print ("primary port : %d" % primary_port)
+ elif opt in ("-s", "--secondary"):
+ secondary_port = int(arg)
+ print ('secondary port : %d' % secondary_port)
+
+ #Creating primary socket object
+ primary_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+ #Binding primary socket to a address. bind() takes tuple of host and port.
+ primary_sock.bind((host, primary_port))
+
+ #Listening primary at the address
+ primary_sock.listen(1) #5 denotes the number of clients can queue
+
+ primary_thread = Thread(target=primarythread,
+ args=(primary_sock, MAIN2PRIMARY, PRIMARY2MAIN,))
+ primary_thread.daemon = True
+ primary_thread.start()
+
+ #Creating secondary socket object
+ secondary_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+ #Binding secondary socket to a address. bind() takes tuple of host and port.
+ secondary_sock.bind((host, secondary_port))
+
+ #Listening secondary at the address
+ secondary_sock.listen(MAX_SECONDARY)
+
+ # secondary process handling thread
+ start_new_thread(acceptthread, (secondary_sock, MAIN2SEC, SEC2MAIN))
+
+ shell = Shell()
+ shell.cmdloop()
+ shell = None
+
+ primary_sock.shutdown(socket.SHUT_RDWR)
+ primary_sock.close()
+ secondary_sock.shutdown(socket.SHUT_RDWR)
+ secondary_sock.close()
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/examples/multi_process/patch_panel/vm/Makefile b/examples/multi_process/patch_panel/vm/Makefile
new file mode 100644
index 0000000..8996d84
--- /dev/null
+++ b/examples/multi_process/patch_panel/vm/Makefile
@@ -0,0 +1,65 @@
+# BSD LICENSE
+#
+# Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overriden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# binary name
+APP = spp_vm
+
+# all source are stored in SRCS-y
+SRCS-y := main.c init.c args.c ../shared/common.c
+
+INC := $(wildcard *.h)
+
+CFLAGS += $(WERROR_FLAGS) -O3
+CFLAGS += -I$(SRCDIR)/../shared
+
+ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),y)
+LDLIBS += -lrte_pmd_ring
+endif
+
+# for newer gcc, e.g. 4.4, no-strict-aliasing may not be necessary
+# and so the next line can be removed in those cases.
+EXTRA_CFLAGS += -fno-strict-aliasing
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/multi_process/patch_panel/vm/args.c b/examples/multi_process/patch_panel/vm/args.c
new file mode 100644
index 0000000..d67af9f
--- /dev/null
+++ b/examples/multi_process/patch_panel/vm/args.c
@@ -0,0 +1,108 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <getopt.h>
+
+#include <rte_memory.h>
+
+#include "args.h"
+#include "common.h"
+#include "init.h"
+
+/* global var for number of clients - extern in header */
+uint8_t num_clients;
+char *server_ip;
+int server_port;
+
+static const char *progname;
+
+/**
+ * Prints out usage information to stdout
+ */
+static void
+usage(void)
+{
+ RTE_LOG(INFO, APP,
+ "%s [EAL options] -- -p PORTMASK -n NUM_CLIENTS [-s NUM_SOCKETS]\n"
+ " -p PORTMASK: hexadecimal bitmask of ports to use\n"
+ " -n NUM_CLIENTS: number of client processes to use\n"
+ , progname);
+}
+
+/**
+ * The application specific arguments follow the DPDK-specific
+ * arguments which are stripped by the DPDK init. This function
+ * processes these application arguments, printing usage info
+ * on error.
+ */
+int
+parse_app_args(uint8_t max_ports, int argc, char *argv[])
+{
+ int option_index, opt;
+ char **argvopt = argv;
+ static struct option lgopts[] = { {0} };
+ int ret;
+
+ progname = argv[0];
+
+ while ((opt = getopt_long(argc, argvopt, "n:p:s:", lgopts,
+ &option_index)) != EOF) {
+ switch (opt) {
+ case 'p':
+ if (parse_portmask(ports, max_ports, optarg) != 0) {
+ usage();
+ return -1;
+ }
+ break;
+ case 'n':
+ if (parse_num_clients(&num_clients, optarg) != 0) {
+ usage();
+ return -1;
+ }
+ break;
+ case 's':
+ ret = parse_server(&server_ip, &server_port, optarg);
+ if (ret != 0) {
+ usage();
+ return -1;
+ }
+ break;
+ default:
+ RTE_LOG(ERR, APP, "Unknown option '%c'\n", opt);
+ usage();
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/examples/multi_process/patch_panel/vm/args.h b/examples/multi_process/patch_panel/vm/args.h
new file mode 100644
index 0000000..73e1f82
--- /dev/null
+++ b/examples/multi_process/patch_panel/vm/args.h
@@ -0,0 +1,45 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _ARGS_H_
+#define _ARGS_H_
+
+#include <stdint.h>
+
+extern uint8_t num_clients;
+extern char *server_ip;
+extern int server_port;
+
+int parse_app_args(uint8_t max_ports, int argc, char *argv[]);
+
+#endif /* ifndef _ARGS_H_ */
diff --git a/examples/multi_process/patch_panel/vm/init.c b/examples/multi_process/patch_panel/vm/init.c
new file mode 100644
index 0000000..3180a07
--- /dev/null
+++ b/examples/multi_process/patch_panel/vm/init.c
@@ -0,0 +1,154 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <limits.h>
+
+#include <rte_cycles.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+
+#include "args.h"
+#include "common.h"
+#include "init.h"
+
+/* The mbuf pool for packet rx */
+static struct rte_mempool *pktmbuf_pool;
+
+/* the port details */
+struct port_info *ports;
+
+/**
+ * Initialise the mbuf pool for packet reception for the NIC, and any other
+ * buffer pools needed by the app - currently none.
+ */
+static int
+init_mbuf_pools(void)
+{
+ const unsigned int num_mbufs = (num_clients * MBUFS_PER_CLIENT)
+ + (ports->num_ports * MBUFS_PER_PORT);
+
+ /*
+ * don't pass single-producer/single-consumer flags to mbuf create as
+ * it seems faster to use a cache instead
+ */
+ RTE_LOG(INFO, APP,
+ "Lookup mbuf pool '%s' [%u mbufs] ...\n", VM_PKTMBUF_POOL_NAME,
+ num_mbufs);
+
+ pktmbuf_pool = rte_mempool_lookup(VM_PKTMBUF_POOL_NAME);
+ if (pktmbuf_pool == NULL) {
+ RTE_LOG(INFO, APP, "Creating mbuf pool '%s' [%u mbufs] ...\n",
+ VM_PKTMBUF_POOL_NAME, num_mbufs);
+
+ pktmbuf_pool = rte_mempool_create(VM_PKTMBUF_POOL_NAME,
+ num_mbufs, MBUF_SIZE, MBUF_CACHE_SIZE,
+ sizeof(struct rte_pktmbuf_pool_private),
+ rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL,
+ rte_socket_id(), NO_FLAGS);
+ }
+ if (pktmbuf_pool == NULL) {
+ RTE_LOG(INFO, APP, "Creating mbuf pool '%s' [%u mbufs] ...\n",
+ VM_PKTMBUF_POOL_NAME, num_mbufs);
+
+ pktmbuf_pool = rte_mempool_create(VM_PKTMBUF_POOL_NAME,
+ num_mbufs, MBUF_SIZE, MBUF_CACHE_SIZE,
+ sizeof(struct rte_pktmbuf_pool_private),
+ rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL,
+ rte_socket_id(), NO_FLAGS);
+ }
+
+ return (pktmbuf_pool == NULL); /* 0 on success */
+}
+
+/**
+ * Main init function for the multi-process server app,
+ * calls subfunctions to do each stage of the initialisation.
+ */
+int
+init(int argc, char *argv[])
+{
+ int retval;
+ const struct rte_memzone *mz;
+ uint8_t i, total_ports;
+
+ /* init EAL, parsing EAL args */
+ retval = rte_eal_init(argc, argv);
+ if (retval < 0)
+ return -1;
+
+ argc -= retval;
+ argv += retval;
+
+ /* get total number of ports */
+ total_ports = rte_eth_dev_count();
+
+ /* set up array for port data */
+ if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
+ mz = rte_memzone_lookup(VM_MZ_PORT_INFO);
+ if (mz == NULL)
+ rte_exit(EXIT_FAILURE,
+ "Cannot get port info structure\n");
+ ports = mz->addr;
+ } else {
+ mz = rte_memzone_reserve(VM_MZ_PORT_INFO, sizeof(*ports),
+ rte_socket_id(), NO_FLAGS);
+ if (mz == NULL)
+ rte_exit(EXIT_FAILURE,
+ "Cannot reserve memory zone for port information\n");
+ memset(mz->addr, 0, sizeof(*ports));
+ ports = mz->addr;
+ }
+
+ /* parse additional, application arguments */
+ retval = parse_app_args(total_ports, argc, argv);
+ if (retval != 0)
+ return -1;
+
+ /* initialise mbuf pools */
+ retval = init_mbuf_pools();
+ if (retval != 0)
+ rte_exit(EXIT_FAILURE, "Cannot create needed mbuf pools\n");
+
+ /* now initialise the ports we will use */
+ if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+ for (i = 0; i < ports->num_ports; i++) {
+ retval = init_port(ports->id[i], pktmbuf_pool);
+ if (retval != 0)
+ rte_exit(EXIT_FAILURE,
+ "Cannot initialise port %d\n", i);
+ }
+ }
+ check_all_ports_link_status(ports, ports->num_ports, (~0x0));
+
+ return 0;
+}
diff --git a/examples/multi_process/patch_panel/vm/init.h b/examples/multi_process/patch_panel/vm/init.h
new file mode 100644
index 0000000..2b65e17
--- /dev/null
+++ b/examples/multi_process/patch_panel/vm/init.h
@@ -0,0 +1,63 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _INIT_H_
+#define _INIT_H_
+
+#include <stdint.h>
+
+/*
+ * Define a client structure with all needed info, including
+ * stats from the clients.
+ */
+struct client {
+ struct rte_ring *rx_q;
+ unsigned int client_id;
+ /*
+ * these stats hold how many packets the client will actually receive,
+ * and how many packets were dropped because the client's queue was full
+ * The port-info stats, in contrast, record how many packets were
+ * received or transmitted on an actual NIC port.
+ */
+ struct {
+ uint64_t rx;
+ uint64_t rx_drop;
+ } stats;
+};
+
+/* the shared port information: port numbers, rx and tx stats etc. */
+extern struct port_info *ports;
+
+int init(int argc, char *argv[]);
+
+#endif /* ifndef _INIT_H_ */
diff --git a/examples/multi_process/patch_panel/vm/main.c b/examples/multi_process/patch_panel/vm/main.c
new file mode 100644
index 0000000..eaf53df
--- /dev/null
+++ b/examples/multi_process/patch_panel/vm/main.c
@@ -0,0 +1,636 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arpa/inet.h>
+
+#include <rte_eth_ring.h>
+
+#include "args.h"
+#include "common.h"
+#include "init.h"
+
+static sig_atomic_t on = 1;
+
+static enum cmd_type cmd = STOP;
+
+static struct port_map port_map[RTE_MAX_ETHPORTS];
+
+static struct port ports_fwd_array[RTE_MAX_ETHPORTS];
+
+static struct rte_mempool *host_mp;
+static uint8_t client_id;
+
+static void
+forward(void)
+{
+ uint16_t nb_rx;
+ uint16_t nb_tx;
+ uint16_t buf;
+ int in_port;
+ int out_port;
+ int i;
+
+ /* Go through every possible port numbers */
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+ struct rte_mbuf *bufs[MAX_PKT_BURST];
+
+ if (ports_fwd_array[i].in_port_id < 0)
+ continue;
+
+ if (ports_fwd_array[i].out_port_id < 0)
+ continue;
+
+ /* if status active, i count is in port */
+ in_port = i;
+ out_port = ports_fwd_array[i].out_port_id;
+
+ /* Get burst of RX packets, from first port of pair. */
+ /* first port rx, second port tx */
+ nb_rx = ports_fwd_array[in_port].rx_func(in_port, 0, bufs,
+ MAX_PKT_BURST);
+ if (unlikely(nb_rx == 0))
+ continue;
+
+ port_map[in_port].stats->rx += nb_rx;
+
+ /* Send burst of TX packets, to second port of pair. */
+ nb_tx = ports_fwd_array[out_port].tx_func(out_port, 0, bufs,
+ nb_rx);
+
+ port_map[out_port].stats->tx += nb_tx;
+
+ /* Free any unsent packets. */
+ if (unlikely(nb_tx < nb_rx)) {
+ port_map[out_port].stats->tx_drop += nb_rx - nb_tx;
+ for (buf = nb_tx; buf < nb_rx; buf++)
+ rte_pktmbuf_free(bufs[buf]);
+ }
+ }
+}
+
+/* main processing loop */
+static void
+nfv_loop(void)
+{
+ unsigned int lcore_id = rte_lcore_id();
+
+ RTE_LOG(INFO, APP, "entering main loop on lcore %u\n", lcore_id);
+
+ while (1) {
+ if (unlikely(cmd == STOP)) {
+ sleep(1);
+ continue;
+ } else if (cmd == FORWARD) {
+ forward();
+ }
+ }
+}
+
+/* leading to nfv processing loop */
+static int
+main_loop(void *dummy __rte_unused)
+{
+ nfv_loop();
+ return 0;
+}
+
+static void
+port_map_init_one(unsigned int i)
+{
+ port_map[i].id = PORT_RESET;
+ port_map[i].port_type = UNDEF;
+ port_map[i].stats = &port_map[i].default_stats;
+}
+
+static void
+port_map_init(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++)
+ port_map_init_one(i);
+}
+
+static void
+forward_array_init_one(int i)
+{
+ ports_fwd_array[i].in_port_id = PORT_RESET;
+ ports_fwd_array[i].out_port_id = PORT_RESET;
+}
+
+/* initialize forward array with default value */
+static void
+forward_array_init(void)
+{
+ unsigned int i;
+
+ /* initialize port forward array */
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++)
+ forward_array_init_one(i);
+}
+
+static int
+do_send(int *connected, int *sock, char *str)
+{
+ int ret;
+
+ ret = send(*sock, str, MSG_SIZE, 0);
+ if (ret == -1) {
+ RTE_LOG(ERR, APP, "send failed");
+ *connected = 0;
+ return -1;
+ }
+
+ RTE_LOG(INFO, APP, "To Server: %s\n", str);
+
+ return 0;
+}
+
+static void
+forward_array_reset(void)
+{
+ unsigned int i;
+
+ /* initialize port forward array */
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+ if (ports_fwd_array[i].in_port_id > -1) {
+ ports_fwd_array[i].out_port_id = PORT_RESET;
+ RTE_LOG(INFO, APP, "Port ID %d\n", i);
+ RTE_LOG(INFO, APP, "out_port_id %d\n",
+ ports_fwd_array[i].out_port_id);
+ }
+ }
+}
+
+/* print forward array active port */
+static void
+print_active_ports(char *str)
+{
+ unsigned int i;
+
+ sprintf(str, "%d\n", client_id);
+ /* every elements value */
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+ if (ports_fwd_array[i].in_port_id < 0)
+ continue;
+
+ RTE_LOG(INFO, APP, "Port ID %d\n", i);
+ RTE_LOG(INFO, APP, "Status %d\n",
+ ports_fwd_array[i].in_port_id);
+
+ sprintf(str + strlen(str), "port id: %d,", i);
+ if (ports_fwd_array[i].in_port_id >= 0)
+ sprintf(str + strlen(str), "on,");
+ else
+ sprintf(str + strlen(str), "off,");
+
+ switch (port_map[i].port_type) {
+ case PHY:
+ RTE_LOG(INFO, APP, "Type: PHY\n");
+ sprintf(str + strlen(str), "PHY,");
+ break;
+ case RING:
+ RTE_LOG(INFO, APP, "Type: RING\n");
+ sprintf(str + strlen(str), "RING(%u),",
+ port_map[i].id);
+ break;
+ case VHOST:
+ RTE_LOG(INFO, APP, "Type: VHOST\n");
+ sprintf(str + strlen(str), "VHOST(%u),",
+ port_map[i].id);
+ break;
+ case UNDEF:
+ RTE_LOG(INFO, APP, "Type: UDF\n");
+ sprintf(str + strlen(str), "UDF,");
+ break;
+ }
+
+ RTE_LOG(INFO, APP, "Out Port ID %d\n",
+ ports_fwd_array[i].out_port_id);
+ sprintf(str + strlen(str), "outport: %d\n",
+ ports_fwd_array[i].out_port_id);
+ }
+}
+
+static void
+forward_array_remove(int port_id)
+{
+ unsigned int i;
+
+ /* Update ports_fwd_array */
+ forward_array_init_one(port_id);
+
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+ if (ports_fwd_array[i].in_port_id < 0)
+ continue;
+
+ if (ports_fwd_array[i].out_port_id == port_id) {
+ ports_fwd_array[i].out_port_id = PORT_RESET;
+ break;
+ }
+ }
+}
+
+static int
+find_port_id(int id, enum port_type type)
+{
+ int port_id = PORT_RESET;
+ unsigned int i;
+
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+ if (port_map[i].port_type != type)
+ continue;
+
+ if (port_map[i].id == id) {
+ port_id = i;
+ break;
+ }
+ }
+
+ return port_id;
+}
+
+static int
+do_del(char *token_list[], int max_token)
+{
+ int port_id = PORT_RESET;
+ int id;
+
+ if (max_token <= 2)
+ return -1;
+
+ if (!strcmp(token_list[1], "ring")) {
+ char name[RTE_ETH_NAME_MAX_LEN];
+
+ if (spp_atoi(token_list[2], &id) < 0)
+ return 0;
+
+ RTE_LOG(DEBUG, APP, "Del ring id %d\n", id);
+ port_id = find_port_id(id, RING);
+ if (port_id < 0)
+ return -1;
+
+ rte_eth_dev_detach(port_id, name);
+ }
+
+ forward_array_remove(port_id);
+ port_map_init_one(port_id);
+
+ return 0;
+}
+
+static int
+is_valid_port(int port_id)
+{
+ if (port_id < 0 || port_id > RTE_MAX_ETHPORTS)
+ return 0;
+
+ return port_map[port_id].id != PORT_RESET;
+}
+
+static int
+add_patch(int in_port, int out_port)
+{
+ if (!is_valid_port(in_port) || !is_valid_port(out_port))
+ return -1;
+
+ /* Populate in port data */
+ ports_fwd_array[in_port].in_port_id = in_port;
+ ports_fwd_array[in_port].rx_func = &rte_eth_rx_burst;
+ ports_fwd_array[in_port].tx_func = &rte_eth_tx_burst;
+ ports_fwd_array[in_port].out_port_id = out_port;
+
+ /* Populate out port data */
+ ports_fwd_array[out_port].in_port_id = out_port;
+ ports_fwd_array[out_port].rx_func = &rte_eth_rx_burst;
+ ports_fwd_array[out_port].tx_func = &rte_eth_tx_burst;
+
+ RTE_LOG(DEBUG, APP, "STATUS: in port %d in_port_id %d\n", in_port,
+ ports_fwd_array[in_port].in_port_id);
+ RTE_LOG(DEBUG, APP, "STATUS: in port %d patch out port id %d\n",
+ in_port, ports_fwd_array[in_port].out_port_id);
+ RTE_LOG(DEBUG, APP, "STATUS: outport %d in_port_id %d\n", out_port,
+ ports_fwd_array[out_port].in_port_id);
+
+ return 0;
+}
+
+static int
+add_ring_pmd(int ring_id)
+{
+ const struct rte_memzone *memzone;
+ struct rte_ring *ring;
+ int ring_port_id;
+
+ /* look up ring, based on user's provided id*/
+ ring = rte_ring_lookup(get_rx_queue_name(ring_id));
+ if (ring == NULL) {
+ RTE_LOG(ERR, APP,
+ "Cannot get RX ring - is server process running?\n");
+ return -1;
+ }
+
+ /* create ring pmd*/
+ /*
+ * TODO: ring->memzone is not accessable by vm
+ * this is workaround to prevent a crash in vm,
+ * this should be fixed properly
+ */
+ memzone = ring->memzone;
+ ring->memzone = NULL;
+ ring_port_id = rte_eth_from_ring(ring);
+ ring->memzone = memzone;
+
+ RTE_LOG(DEBUG, APP, "ring port id %d\n", ring_port_id);
+
+ return ring_port_id;
+}
+
+static int
+do_add(char *token_list[], int max_token)
+{
+ enum port_type type = UNDEF;
+ int port_id = PORT_RESET;
+ int id;
+
+ if (max_token <= 2)
+ return -1;
+
+ if (!strcmp(token_list[1], "ring")) {
+ if (spp_atoi(token_list[2], &id) < 0)
+ return 0;
+
+ type = RING;
+ port_id = add_ring_pmd(id);
+
+ } else if (strncmp(token_list[1], "pool", 4) == 0) {
+ host_mp = rte_mempool_lookup(PKTMBUF_POOL_NAME);
+ if (host_mp == NULL)
+ rte_exit(EXIT_FAILURE,
+ "Cannot get mempool for mbufs\n");
+ return 0;
+ }
+
+ if (port_id < 0)
+ return -1;
+
+ port_map[port_id].id = id;
+ port_map[port_id].port_type = type;
+ port_map[port_id].stats = &ports->client_stats[id];
+
+ /* Update ports_fwd_array with port id */
+ ports_fwd_array[port_id].in_port_id = port_id;
+
+ return 0;
+}
+
+static int
+parse_command(char *str)
+{
+ char *token_list[MAX_PARAMETER] = {NULL};
+ int max_token = 0;
+ int ret = 0;
+ int i;
+
+ if (!str)
+ return 0;
+
+ /* tokenize the user commands from controller */
+ token_list[max_token] = strtok(str, " ");
+ while (token_list[max_token] != NULL) {
+ RTE_LOG(DEBUG, APP, "token %d = %s\n", max_token,
+ token_list[max_token]);
+ max_token++;
+ token_list[max_token] = strtok(NULL, " ");
+ }
+
+ if (max_token == 0)
+ return 0;
+
+ if (!strcmp(token_list[0], "status")) {
+ RTE_LOG(DEBUG, APP, "status\n");
+ memset(str, '\0', MSG_SIZE);
+ if (cmd == FORWARD)
+ i = sprintf(str, "Client ID %d Running\n", client_id);
+ else
+ i = sprintf(str, "Client ID %d Idling\n", client_id);
+ print_active_ports(str + i);
+
+ } else if (!strcmp(token_list[0], "_get_client_id")) {
+ memset(str, '\0', MSG_SIZE);
+ i = sprintf(str, "%d", client_id);
+
+ } else if (!strcmp(token_list[0], "_set_client_id")) {
+ int id;
+
+ if (spp_atoi(token_list[1], &id) >= 0)
+ client_id = id;
+
+ } else if (!strcmp(token_list[0], "exit")) {
+ RTE_LOG(DEBUG, APP, "exit\n");
+ RTE_LOG(DEBUG, APP, "stop\n");
+ cmd = STOP;
+ ret = -1;
+
+ } else if (!strcmp(token_list[0], "stop")) {
+ RTE_LOG(DEBUG, APP, "stop\n");
+ cmd = STOP;
+
+ } else if (!strcmp(token_list[0], "forward")) {
+ RTE_LOG(DEBUG, APP, "forward\n");
+ cmd = FORWARD;
+
+ } else if (strncmp(token_list[0], "add", 3) == 0) {
+ RTE_LOG(DEBUG, APP, "add\n");
+ do_add(token_list, max_token);
+
+ } else if (!strcmp(token_list[0], "patch")) {
+ RTE_LOG(DEBUG, APP, "patch\n");
+
+ if (max_token <= 1)
+ return 0;
+
+ if (strncmp(token_list[1], "reset", 5) == 0) {
+ /* reset forward array*/
+ forward_array_reset();
+ } else {
+ int in_port;
+ int out_port;
+
+ if (max_token <= 2)
+ return 0;
+
+ if (spp_atoi(token_list[1], &in_port) < 0)
+ return 0;
+
+ if (spp_atoi(token_list[2], &out_port) < 0)
+ return 0;
+
+ add_patch(in_port, out_port);
+ }
+ } else if (strncmp(str, "del", 3) == 0) {
+ RTE_LOG(DEBUG, APP, "del\n");
+
+ cmd = STOP;
+ do_del(token_list, max_token);
+ }
+
+ return ret;
+}
+
+static int
+do_receive(int *connected, int *sock, char *str)
+{
+ int ret;
+
+ memset(str, '\0', MSG_SIZE);
+
+ ret = recv(*sock, str, MSG_SIZE, 0);
+ if (ret <= 0) {
+ RTE_LOG(DEBUG, APP, "Receive count: %d\n", ret);
+ if (ret < 0)
+ RTE_LOG(ERR, APP, "Receive Fail");
+ else
+ RTE_LOG(INFO, APP, "Receive 0\n");
+
+ RTE_LOG(INFO, APP, "Assume Server closed connection\n");
+ close(*sock);
+ *sock = SOCK_RESET;
+ *connected = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+do_connection(int *connected, int *sock)
+{
+ static struct sockaddr_in servaddr;
+ int ret = 0;
+
+ if (*connected == 0) {
+ if (*sock < 0) {
+ RTE_LOG(INFO, APP, "Creating socket...\n");
+ *sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (*sock < 0)
+ rte_exit(EXIT_FAILURE, "socket error\n");
+
+ /* Creation of the socket */
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr.s_addr = inet_addr(server_ip);
+ servaddr.sin_port = htons(server_port);
+ }
+
+ RTE_LOG(INFO, APP, "Trying to connect ... socket %d\n", *sock);
+ ret = connect(*sock, (struct sockaddr *) &servaddr,
+ sizeof(servaddr));
+ if (ret < 0) {
+ RTE_LOG(ERR, APP, "Connection Error");
+ return ret;
+ }
+
+ RTE_LOG(INFO, APP, "Connected\n");
+ *connected = 1;
+ }
+
+ return ret;
+}
+
+int
+main(int argc, char *argv[])
+{
+ unsigned int lcore_id;
+ int sock = SOCK_RESET;
+ int connected = 0;
+ char str[MSG_SIZE];
+ int ret;
+ int i;
+
+ /* initialise the system */
+ if (init(argc, argv) < 0)
+ return -1;
+
+ /* initialize port forward array*/
+ forward_array_init();
+ port_map_init();
+
+ /* update port_forward_array with active port */
+ for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+ if (!rte_eth_dev_is_valid_port(i))
+ continue;
+
+ /* Update ports_fwd_array with phy port*/
+ ports_fwd_array[i].in_port_id = i;
+ port_map[i].port_type = PHY;
+ port_map[i].id = i;
+ port_map[i].stats = &ports->port_stats[i];
+ }
+
+ RTE_LOG(INFO, APP, "Finished Process Init.\n");
+
+ lcore_id = 0;
+ RTE_LCORE_FOREACH_SLAVE(lcore_id) {
+ rte_eal_remote_launch(main_loop, NULL, lcore_id);
+ }
+
+ while (on) {
+ ret = do_connection(&connected, &sock);
+ if (ret < 0) {
+ sleep(1);
+ continue;
+ }
+
+ ret = do_receive(&connected, &sock, str);
+ if (ret < 0)
+ continue;
+
+ RTE_LOG(DEBUG, APP, "Received string: %s\n", str);
+
+ ret = parse_command(str);
+ if (ret < 0)
+ break;
+
+ /*Send the message back to client*/
+ ret = do_send(&connected, &sock, str);
+ if (ret < 0)
+ continue;
+ }
+
+ /* exit */
+ close(sock);
+ sock = SOCK_RESET;
+ RTE_LOG(INFO, APP, "spp_vm exit.\n");
+ return 0;
+}