[spp] [PATCH 01/57] spp_vf: add vf functions

Yasufumi Ogawa ogawa.yasufumi at lab.ntt.co.jp
Thu Dec 28 09:49:59 CET 2017


Hi Hiroyuki,

Thank you for your contribution! I would like to review your patches for 
merging.

Thanks,
Yasufumi

On 2017/12/28 13:55, x-fn-spp at sl.ntt-tx.co.jp wrote:
> From: Hiroyuki Nakamura <nakamura.hioryuki at po.ntt-tx.co.jp>
> 
> Hi everyone,
> 
> This the first patch of series for spp_vf. Some of them might not comply
> with coding style for whitespace or indenting and I would like to send
> patches to fix it later.
> 
> I also would like to send another series of patches for documentation after
> you accept first series.
> 
> Thanks,
> 
> * Classifier by MAC address
> * Forwarder
> * Merger
> 
> Signed-off-by: Tomoyuki Mizuguchi <mizuguchi.tomoyuki at po.ntt-tx.co.jp>
> Signed-off-by: Yasufum Ogawa <ogawa.yasufumi at lab.ntt.co.jp>
> ---
>   src/Makefile              |   1 +
>   src/vf/Makefile           |  56 ++++
>   src/vf/classifier_mac.c   | 233 ++++++++++++++
>   src/vf/classifier_mac.h   |  12 +
>   src/vf/ringlatencystats.c | 147 +++++++++
>   src/vf/ringlatencystats.h |  69 ++++
>   src/vf/spp_config.c       | 669 ++++++++++++++++++++++++++++++++++++++
>   src/vf/spp_config.h       |  90 ++++++
>   src/vf/spp_forward.c      | 106 +++++++
>   src/vf/spp_forward.h      |   9 +
>   src/vf/spp_vf.c           | 794 ++++++++++++++++++++++++++++++++++++++++++++++
>   src/vf/spp_vf.h           |  40 +++
>   12 files changed, 2226 insertions(+)
>   create mode 100644 src/vf/Makefile
>   create mode 100644 src/vf/classifier_mac.c
>   create mode 100644 src/vf/classifier_mac.h
>   create mode 100644 src/vf/ringlatencystats.c
>   create mode 100644 src/vf/ringlatencystats.h
>   create mode 100644 src/vf/spp_config.c
>   create mode 100644 src/vf/spp_config.h
>   create mode 100644 src/vf/spp_forward.c
>   create mode 100644 src/vf/spp_forward.h
>   create mode 100644 src/vf/spp_vf.c
>   create mode 100644 src/vf/spp_vf.h
> 
> diff --git a/src/Makefile b/src/Makefile
> index 6fd24b8..d2eb9b6 100644
> --- a/src/Makefile
> +++ b/src/Makefile
> @@ -41,5 +41,6 @@ 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
> +DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += vf
>   
>   include $(RTE_SDK)/mk/rte.extsubdir.mk
> diff --git a/src/vf/Makefile b/src/vf/Makefile
> new file mode 100644
> index 0000000..4961d2e
> --- /dev/null
> +++ b/src/vf/Makefile
> @@ -0,0 +1,56 @@
> +#   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_vf
> +
> +# all source are stored in SRCS-y
> +SRCS-y := spp_vf.c spp_config.c classifier_mac.c spp_forward.c ringlatencystats.c ../shared/common.c
> +
> +CFLAGS += $(WERROR_FLAGS) -O3
> +CFLAGS += -I$(SRCDIR)/../shared
> +#CFLAGS += -DSPP_DEMONIZE
> +#CFLAGS += -DSPP_RINGLATENCYSTATS_ENABLE
> +
> +LDLIBS += -ljansson
> +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/src/vf/classifier_mac.c b/src/vf/classifier_mac.c
> new file mode 100644
> index 0000000..da03905
> --- /dev/null
> +++ b/src/vf/classifier_mac.c
> @@ -0,0 +1,233 @@
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stddef.h>
> +#include <math.h>
> +
> +#include <rte_mbuf.h>
> +#include <rte_log.h>
> +#include <rte_cycles.h>
> +#include <rte_malloc.h>
> +#include <rte_memcpy.h>
> +#include <rte_random.h>
> +#include <rte_byteorder.h>
> +#include <rte_per_lcore.h>
> +#include <rte_eal.h>
> +#include <rte_launch.h>
> +#include <rte_hash.h>
> +
> +#include "spp_vf.h"
> +#include "ringlatencystats.h"
> +#include "classifier_mac.h"
> +
> +#define RTE_LOGTYPE_SPP_CLASSIFIER_MAC RTE_LOGTYPE_USER1
> +
> +#ifdef RTE_MACHINE_CPUFLAG_SSE4_2
> +#include <rte_hash_crc.h>
> +#define DEFAULT_HASH_FUNC rte_hash_crc
> +#else
> +#include <rte_jhash.h>
> +#define DEFAULT_HASH_FUNC rte_jhash
> +#endif
> +
> +/* number of classifier mac table entry */
> +#define NUM_CLASSIFIER_MAC_TABLE_ENTRY 128
> +
> +/* interval that transmit burst packet, if buffer is not filled.
> +		nano second */
> +#define DRAIN_TX_PACKET_INTERVAL 100
> +
> +/* mac address string(xx:xx:xx:xx:xx:xx) buffer size */
> +static const size_t ETHER_ADDR_STR_BUF_SZ =
> +		ETHER_ADDR_LEN * 2 + (ETHER_ADDR_LEN - 1) + 1;
> +
> +/* classified data (destination port, target packets, etc) */
> +struct classified_data {
> +	int      if_no;
> +	uint8_t  tx_port;
> +	uint16_t num_pkt;
> +	struct rte_mbuf *pkts[MAX_PKT_BURST];
> +};
> +
> +/* initialize classifier. */
> +static int
> +init_classifier(const struct spp_core_info *core_info,
> +		struct rte_hash **classifier_mac_table, struct classified_data *classified_data)
> +{
> +	int ret = -1;
> +	int i;
> +	struct ether_addr eth_addr;
> +	char mac_addr_str[ETHER_ADDR_STR_BUF_SZ];
> +
> +	/* set hash creating parameters */
> +	struct rte_hash_parameters hash_params = {
> +			.name      = "classifier_mac_table",
> +			.entries   = NUM_CLASSIFIER_MAC_TABLE_ENTRY,
> +			.key_len   = sizeof(struct ether_addr),
> +			.hash_func = DEFAULT_HASH_FUNC,
> +			.hash_func_init_val = 0,
> +			.socket_id = rte_socket_id(),
> +	};
> +
> +#ifdef RTE_MACHINE_CPUFLAG_SSE4_2
> +	RTE_LOG(DEBUG, SPP_CLASSIFIER_MAC, "Enabled SSE4.2. use crc hash.\n");
> +#else
> +	RTE_LOG(DEBUG, SPP_CLASSIFIER_MAC, "Disabled SSE4.2. use jenkins hash.\n");
> +#endif
> +
> +	/* create classifier mac table (hash table) */
> +	*classifier_mac_table = rte_hash_create(&hash_params);
> +	if (unlikely(*classifier_mac_table == NULL)) {
> +		RTE_LOG(ERR, SPP_CLASSIFIER_MAC, "Cannot create classifier mac table\n");
> +		return -1;
> +	}
> +
> +	/* populate the hash */
> +	for (i = 0; i < core_info->num_tx_port; i++) {
> +		rte_memcpy(&eth_addr, &core_info->tx_ports[i].mac_addr, ETHER_ADDR_LEN);
> +
> +		/* add entry to classifier mac table */
> +		ret = rte_hash_add_key_data(*classifier_mac_table, (void*)&eth_addr, (void*)(long)i);
> +		if (unlikely(ret < 0)) {
> +			ether_format_addr(mac_addr_str, sizeof(mac_addr_str), &eth_addr);
> +			RTE_LOG(ERR, SPP_CLASSIFIER_MAC,
> +					"Cannot add entry to classifier mac table. "
> +					"ret=%d, mac_addr=%s\n", ret, mac_addr_str);
> +			rte_hash_free(*classifier_mac_table);
> +			*classifier_mac_table = NULL;
> +			return -1;
> +		}
> +
> +		/* set value */
> +		classified_data[i].if_no   = i;
> +		classified_data[i].tx_port = core_info->tx_ports[i].dpdk_port;
> +		classified_data[i].num_pkt = 0;
> +	}
> +
> +	return 0;
> +}
> +
> +/* transimit packet to one destination. */
> +static inline void
> +transimit_packet(struct classified_data *classified_data)
> +{
> +	int i;
> +	uint16_t n_tx;
> +
> +	/* set ringlatencystats */
> +	spp_ringlatencystats_add_time_stamp(classified_data->if_no,
> +			classified_data->pkts, classified_data->num_pkt);
> +
> +	/* transimit packets */
> +	n_tx = rte_eth_tx_burst(classified_data->tx_port, 0,
> +			classified_data->pkts, classified_data->num_pkt);
> +
> +	/* free cannnot transiit packets */
> +	if (unlikely(n_tx != classified_data->num_pkt)) {
> +		for (i = n_tx; i < classified_data->num_pkt; i++)
> +			rte_pktmbuf_free(classified_data->pkts[i]);
> +		RTE_LOG(DEBUG, SPP_CLASSIFIER_MAC,
> +				"drop packets(tx). num=%hu, dpdk_port=%hhu\n",
> +				classified_data->num_pkt - n_tx, classified_data->tx_port);
> +	}
> +
> +	classified_data->num_pkt = 0;
> +}
> +
> +/* classify packet by destination mac address,
> +		and transimit packet (conditional). */
> +static inline void
> +classify_packet(struct rte_mbuf **rx_pkts, uint16_t n_rx,
> +		struct rte_hash *classifier_mac_table, struct classified_data *classified_data)
> +{
> +	int ret;
> +	int i;
> +	struct ether_hdr *eth;
> +	struct classified_data *cd;
> +	void *lookup_data;
> +	char mac_addr_str[ETHER_ADDR_STR_BUF_SZ];
> +
> +	for (i = 0; i < n_rx; i++) {
> +		eth = rte_pktmbuf_mtod(rx_pkts[i], struct ether_hdr *);
> +
> +		/* find in table (by destination mac address)*/
> +		ret = rte_hash_lookup_data(classifier_mac_table,
> +				(const void*)&eth->d_addr, &lookup_data);
> +		if (unlikely(ret < 0)) {
> +			ether_format_addr(mac_addr_str, sizeof(mac_addr_str), &eth->d_addr);
> +			RTE_LOG(ERR, SPP_CLASSIFIER_MAC,
> +					"unknown mac address. ret=%d, mac_addr=%s\n", ret, mac_addr_str);
> +			rte_pktmbuf_free(rx_pkts[i]);
> +			continue;
> +		}
> +
> +		/* set mbuf pointer to tx buffer */
> +		cd = classified_data + (long)lookup_data;
> +		cd->pkts[cd->num_pkt++] = rx_pkts[i];
> +
> +		/* transimit packet, if buffer is filled */
> +		if (unlikely(cd->num_pkt == MAX_PKT_BURST))
> +			transimit_packet(cd);
> +	}
> +}
> +
> +/* classifier(mac address) thread function. */
> +int
> +spp_classifier_mac_do(void *arg)
> +{
> +	int ret = -1;
> +	int i;
> +	int n_rx;
> +	struct spp_core_info *core_info = (struct spp_core_info *)arg;
> +	struct rte_mbuf *rx_pkts[MAX_PKT_BURST];
> +
> +	struct rte_hash *classifier_mac_table = NULL;
> +	const int n_classified_data = core_info->num_tx_port;
> +	struct classified_data classified_data[n_classified_data];
> +
> +	uint64_t cur_tsc, prev_tsc = 0;
> +	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) /
> +			US_PER_S * DRAIN_TX_PACKET_INTERVAL;
> +
> +	/* initialize */
> +	ret = init_classifier(core_info, &classifier_mac_table, classified_data);
> +	if (unlikely(ret != 0))
> +		return ret;
> +
> +	/* to idle  */
> +	core_info->status = SPP_CORE_IDLE;
> +	while(likely(core_info->status == SPP_CORE_IDLE) ||
> +			likely(core_info->status == SPP_CORE_FORWARD)) {
> +
> +		while(likely(core_info->status == SPP_CORE_FORWARD)) {
> +			/* drain tx packets, if buffer is not filled for interval */
> +			cur_tsc = rte_rdtsc();
> +			if (unlikely(cur_tsc - prev_tsc > drain_tsc)) {
> +				for (i = 0; i < n_classified_data; i++) {
> +					if (unlikely(classified_data[i].num_pkt != 0))
> +						transimit_packet(&classified_data[i]);
> +				}
> +				prev_tsc = cur_tsc;
> +			}
> +
> +			/* retrieve packets */
> +			n_rx = rte_eth_rx_burst(core_info->rx_ports[0].dpdk_port, 0,
> +					rx_pkts, MAX_PKT_BURST);
> +			if (unlikely(n_rx == 0))
> +				continue;
> +
> +			/* classify and transimit (filled) */
> +			classify_packet(rx_pkts, n_rx, classifier_mac_table, classified_data);
> +		}
> +	}
> +
> +	/* uninitialize */
> +	if (classifier_mac_table != NULL) {
> +		rte_hash_free(classifier_mac_table);
> +		classifier_mac_table = NULL;
> +	}
> +	core_info->status = SPP_CORE_STOP;
> +
> +	return 0;
> +}
> diff --git a/src/vf/classifier_mac.h b/src/vf/classifier_mac.h
> new file mode 100644
> index 0000000..2661206
> --- /dev/null
> +++ b/src/vf/classifier_mac.h
> @@ -0,0 +1,12 @@
> +#ifndef _CLASSIFIER_MAC_H_
> +#define _CLASSIFIER_MAC_H_
> +
> +/**
> + * classifier(mac address) thread function.
> + *
> + * @param arg
> + *  pointer to struct spp_core_info.
> + */
> +int spp_classifier_mac_do(void *arg);
> +
> +#endif /* _CLASSIFIER_MAC_H_ */
> diff --git a/src/vf/ringlatencystats.c b/src/vf/ringlatencystats.c
> new file mode 100644
> index 0000000..8f44020
> --- /dev/null
> +++ b/src/vf/ringlatencystats.c
> @@ -0,0 +1,147 @@
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <stdio.h>
> +#include <stddef.h>
> +#include <math.h>
> +
> +#include <rte_mbuf.h>
> +#include <rte_log.h>
> +#include <rte_cycles.h>
> +#include <rte_malloc.h>
> +#include <rte_memcpy.h>
> +
> +#include "ringlatencystats.h"
> +
> +#define NS_PER_SEC 1E9
> +
> +#define RTE_LOGTYPE_SPP_RING_LATENCY_STATS RTE_LOGTYPE_USER1
> +
> +#ifdef SPP_RINGLATENCYSTATS_ENABLE
> +
> +/** ring latency statistics information */
> +struct ring_latency_stats_info {
> +	uint64_t timer_tsc;   /**< sampling interval counter */
> +	uint64_t prev_tsc;    /**< previous time */
> +	struct spp_ringlatencystats_ring_latency_stats stats;  /**< ring latency statistics list */
> +};
> +
> +/** sampling interval */
> +static uint64_t g_samp_intvl = 0;
> +
> +/** ring latency statistics information instance */
> +static struct ring_latency_stats_info *g_stats_info = NULL;
> +
> +/** number of ring latency statisics */
> +static uint16_t g_stats_count = 0;
> +
> +/* clock cycles per nano second */
> +static inline uint64_t
> +cycles_per_ns(void)
> +{
> +	return rte_get_timer_hz() / NS_PER_SEC;
> +}
> +
> +int
> +spp_ringlatencystats_init(uint64_t samp_intvl, uint16_t stats_count)
> +{
> +	/* allocate memory for ring latency statisics infromation */
> +	g_stats_info = rte_zmalloc(
> +			"global ring_latency_stats_info",
> +			sizeof(struct ring_latency_stats_info) * stats_count, 0);
> +	if (unlikely(g_stats_info == NULL)) {
> +		RTE_LOG(ERR, SPP_RING_LATENCY_STATS,
> +				"Cannot allocate memory for ring latency stats info\n");
> +		return -1;
> +	}
> +
> +	/* store global information for ring latency statistics */
> +	g_samp_intvl = samp_intvl * cycles_per_ns();
> +	g_stats_count = stats_count;
> +
> +	RTE_LOG(DEBUG, SPP_RING_LATENCY_STATS,
> +			"g_samp_intvl=%lu, g_stats_count=%hu, cpns=%lu\n",
> +			g_samp_intvl, g_stats_count, cycles_per_ns());
> +
> +	return 0;
> +}
> +
> +void
> +spp_ringlatencystats_uninit(void)
> +{
> +	/* free memory for ring latency statisics infromation */
> +	if (likely(g_stats_info != NULL)) {
> +		rte_free(g_stats_info);
> +		g_stats_count = 0;
> +	}
> +}
> +
> +void
> +spp_ringlatencystats_add_time_stamp(int ring_id,
> +			struct rte_mbuf **pkts, uint16_t nb_pkts)
> +{
> +	unsigned int i;
> +	uint64_t diff_tsc, now;
> +	struct ring_latency_stats_info *stats_info = &g_stats_info[ring_id];
> +
> +	for (i = 0; i < nb_pkts; i++) {
> +
> +		/* get tsc now */
> +		now = rte_rdtsc();
> +
> +		/* calculate difference from the previous processing time */
> +		diff_tsc = now - stats_info->prev_tsc;
> +		stats_info->timer_tsc += diff_tsc;
> +
> +		/* when it is over sampling interval */
> +		/* set tsc to mbuf::timestamp */
> +		if (unlikely(stats_info->timer_tsc >= g_samp_intvl)) {
> +			pkts[i]->timestamp = now;
> +			stats_info->timer_tsc = 0;
> +		}
> +
> +		/* update previus tsc */
> +		stats_info->prev_tsc = now;
> +	}
> +}
> +
> +void
> +spp_ringlatencystats_calculate_latency(int ring_id,
> +			struct rte_mbuf **pkts, uint16_t nb_pkts)
> +{
> +	unsigned int i;
> +	uint64_t now;
> +	int64_t latency;
> +	struct ring_latency_stats_info *stats_info = &g_stats_info[ring_id];
> +
> +	now = rte_rdtsc();
> +	for (i = 0; i < nb_pkts; i++) {
> +		if (likely(pkts[i]->timestamp == 0))
> +			continue;
> +
> +		/* when mbuf::timestamp is not zero */
> +		/* calculate latency */
> +		latency = (uint64_t)floor((now - pkts[i]->timestamp) / cycles_per_ns());
> +		if (likely(latency < SPP_RINGLATENCYSTATS_STATS_SLOT_COUNT-1))
> +			stats_info->stats.slot[latency]++;
> +		else
> +			stats_info->stats.slot[SPP_RINGLATENCYSTATS_STATS_SLOT_COUNT-1]++;
> +	}
> +}
> +
> +int
> +spp_ringlatencystats_get_count(void)
> +{
> +	return g_stats_count;
> +}
> +
> +void
> +spp_ringlatencystats_get_stats(int ring_id,
> +		struct spp_ringlatencystats_ring_latency_stats *stats)
> +{
> +	struct ring_latency_stats_info *stats_info = &g_stats_info[ring_id];
> +
> +	rte_memcpy(stats, &stats_info->stats,
> +			sizeof(struct spp_ringlatencystats_ring_latency_stats));
> +}
> +
> +#endif /* SPP_RINGLATENCYSTATS_ENABLE */
> diff --git a/src/vf/ringlatencystats.h b/src/vf/ringlatencystats.h
> new file mode 100644
> index 0000000..bc47699
> --- /dev/null
> +++ b/src/vf/ringlatencystats.h
> @@ -0,0 +1,69 @@
> +#ifndef _RINGLATENCYSTATS_H_
> +#define _RINGLATENCYSTATS_H_
> +
> +#include <rte_mbuf.h>
> +
> +/** number of slots to save latency. 0ns~99ns and 100ns over */
> +#define SPP_RINGLATENCYSTATS_STATS_SLOT_COUNT 101
> +
> +/** ring latency statistics */
> +struct spp_ringlatencystats_ring_latency_stats {
> +	uint64_t slot[SPP_RINGLATENCYSTATS_STATS_SLOT_COUNT]; /**< slots to save latency */
> +};
> +
> +
> +#ifdef SPP_RINGLATENCYSTATS_ENABLE
> +/**
> + * initialize ring latency statisics.
> + *
> + * @retval 0: succeeded.
> + * @retval -1: failed.
> + */
> +int spp_ringlatencystats_init(uint64_t samp_intvl, uint16_t stats_count);
> +
> +/**
> + *uninitialize ring latency statisics.
> + */
> +void spp_ringlatencystats_uninit(void);
> +
> +/**
> + * add time-stamp to mbuf's member.
> + *
> + * call at enqueue.
> + */
> +void spp_ringlatencystats_add_time_stamp(int ring_id,
> +			struct rte_mbuf **pkts, uint16_t nb_pkts);
> +
> +/**
> + * calculate latency.
> + *
> + * call at dequeue.
> + */
> +void spp_ringlatencystats_calculate_latency(int ring_id,
> +			struct rte_mbuf **pkts, uint16_t nb_pkts);
> +
> +/**
> + * get number of ring latency statisics.
> + *
> + * @return spp_ringlatencystats_init's parameter "stats_count"
> + */
> +int spp_ringlatencystats_get_count(void);
> +
> +/**
> + *get specific ring latency statisics.
> + */
> +void spp_ringlatencystats_get_stats(int ring_id,
> +		struct spp_ringlatencystats_ring_latency_stats *stats);
> +
> +#else
> +
> +#define spp_ringlatencystats_init(arg1, arg2) 0
> +#define spp_ringlatencystats_uninit()
> +#define spp_ringlatencystats_add_time_stamp(arg1, arg2, arg3)
> +#define spp_ringlatencystats_calculate_latency(arg1, arg2, arg3)
> +#define spp_ringlatencystats_get_count() 0
> +#define spp_ringlatencystats_get_stats(arg1, arg2)
> +
> +#endif /* SPP_RINGLATENCYSTATS_ENABLE */
> +
> +#endif /* _RINGLATENCYSTATS_H_ */
> diff --git a/src/vf/spp_config.c b/src/vf/spp_config.c
> new file mode 100644
> index 0000000..63b85f2
> --- /dev/null
> +++ b/src/vf/spp_config.c
> @@ -0,0 +1,669 @@
> +#include <sys/types.h>
> +#include <jansson.h>
> +
> +#include <rte_log.h>
> +
> +#include "spp_config.h"
> +
> +#define CONFIG_CORE_TYPE_CLASSIFIER_MAC "classifier_mac"
> +#define CONFIG_CORE_TYPE_MERGE          "merge"
> +#define CONFIG_CORE_TYPE_FORWARD        "forward"
> +
> +#define JSONPATH_CLASSIFIER_TABLE "$.classifier_table"
> +#define JSONPATH_PROC_TABLE "$.vfs"
> +#define JSONPATH_NAME       "$.name"
> +#define JSONPATH_TABLE      "$.table"
> +#define JSONPATH_MAC        "$.mac"
> +#define JSONPATH_PORT       "$.port"
> +#define JSONPATH_NUM_VHOST  "$.num_vhost"
> +#define JSONPATH_NUM_RING   "$.num_ring"
> +#define JSONPATH_FUNCTIONS  "$.functions"
> +#define JSONPATH_CORE_NO    "$.core"
> +#define JSONPATH_CORE_TYPE  "$.type"
> +#define JSONPATH_RX_PORT    "$.rx_port"
> +#define JSONPATH_TX_PORT    "$.tx_port"
> +#define JSONPATH_TX_TABLE   "$.tx_port_table"
> +
> +/*
> + * Get integer data from config
> + */
> +static int
> +config_get_int_value(const json_t *obj, const char *path, int *value)
> +{
> +	/* 指定パラメータのJsonオブジェクト取得 */
> +	json_t *tmp_obj = json_path_get(obj, path);
> +	if (unlikely(tmp_obj == NULL)) {
> +		/* 必須でないデータを取得する場合を考慮し、DEBUGログとする。 */
> +		RTE_LOG(DEBUG, APP, "No parameter. (path = %s)\n", path);
> +		return -1;
> +	}
> +
> +	/* Integer type check */
> +	if (unlikely(!json_is_integer(tmp_obj))) {
> +		/* 必須でないデータを取得する場合を考慮し、DEBUGログとする。 */
> +		RTE_LOG(DEBUG, APP, "Not an integer. (path = %s)\n", path);
> +		return -1;
> +	}
> +
> +	/* Set to OUT parameter */
> +	*value = json_integer_value(tmp_obj);
> +	RTE_LOG(DEBUG, APP, "get value = %d\n", *value);
> +	return 0;
> +}
> +
> +/*
> + * Get String data from config
> + */
> +static int
> +config_get_str_value(const json_t *obj, const char *path, char *value)
> +{
> +	/* 指定パラメータのJsonオブジェクト取得 */
> +	json_t *tmp_obj = json_path_get(obj, path);
> +	if (unlikely(tmp_obj == NULL)) {
> +		RTE_LOG(DEBUG, APP, "No parameter. (path = %s)\n", path);
> +		return -1;
> +	}
> +
> +	/* String type check */
> +	if (unlikely(!json_is_string(tmp_obj))) {
> +		RTE_LOG(DEBUG, APP, "Not a string. (path = %s)\n", path);
> +		return -1;
> +	}
> +
> +	/* Set to OUT parameter */
> +	strcpy(value, json_string_value(tmp_obj));
> +	RTE_LOG(DEBUG, APP, "get value = %s\n", value);
> +	return 0;
> +}
> +
> +/*
> + * コンフィグ情報初期化
> + */
> +static void
> +config_init_data(struct spp_config_area *config)
> +{
> +	/* 0クリア */
> +	memset(config, 0x00, sizeof(struct spp_config_area));
> +	int core_cnt, port_cnt, table_cnt;
> +
> +	/* IF種別初期設定 */
> +	for (core_cnt = 0; core_cnt < SPP_CONFIG_CORE_MAX; core_cnt++) {
> +		for (port_cnt = 0; port_cnt < RTE_MAX_ETHPORTS; port_cnt++) {
> +			config->proc.functions[core_cnt].rx_ports[port_cnt].if_type = UNDEF;
> +			config->proc.functions[core_cnt].tx_ports[port_cnt].if_type = UNDEF;
> +		}
> +	}
> +	for (table_cnt = 0; table_cnt < SPP_CONFIG_MAC_TABLE_MAX; table_cnt++) {
> +		config->classifier_table.mac_tables[table_cnt].port.if_type = UNDEF;
> +	}
> +
> +	return;
> +}
> +
> +/*
> + * IFの情報からIF種別とIF番号を取得する
> + * ("ring0" -> 種別:"ring"、番号:0)
> + */
> +static int
> +config_get_if_info(const char *port, enum port_type *if_type, int *if_no)
> +{
> +	enum port_type type = UNDEF;
> +	const char *no_str = NULL;
> +	char *endptr = NULL;
> +
> +	/* IF type check */
> +	if (strncmp(port, SPP_CONFIG_IFTYPE_NIC, strlen(SPP_CONFIG_IFTYPE_NIC)) == 0) {
> +		/* NIC */
> +		type = PHY;
> +		no_str = &port[strlen(SPP_CONFIG_IFTYPE_NIC)];
> +	} else if (strncmp(port, SPP_CONFIG_IFTYPE_VHOST, strlen(SPP_CONFIG_IFTYPE_VHOST)) == 0) {
> +		/* VHOST */
> +		type = VHOST;
> +		no_str = &port[strlen(SPP_CONFIG_IFTYPE_VHOST)];
> +	} else if (strncmp(port, SPP_CONFIG_IFTYPE_RING, strlen(SPP_CONFIG_IFTYPE_RING)) == 0) {
> +		/* RING */
> +		type = RING;
> +		no_str = &port[strlen(SPP_CONFIG_IFTYPE_RING)];
> +	} else {
> +		/* OTHER */
> +		RTE_LOG(ERR, APP, "Unknown interface type. (port = %s)\n", port);
> +		return -1;
> +	}
> +
> +	/* IF番号を文字列から数値変換 */
> +	int ret_no = strtol(no_str, &endptr, 0);
> +	if (unlikely(no_str == endptr) || unlikely(*endptr != '\0')) {
> +		/* No IF number */
> +		RTE_LOG(ERR, APP, "No interface number. (port = %s)\n", port);
> +		return -1;
> +	}
> +
> +	/* Set OUT parameter */
> +	*if_type = type;
> +	*if_no = ret_no;
> +
> +	RTE_LOG(DEBUG, APP, "Port = %s => Type = %d No = %d\n",
> +			port, *if_type, *if_no);
> +	return 0;
> +}
> +
> +/*
> + * MAC addressを文字列から数値へ変換
> + */
> +int64_t
> +config_change_mac_str_to_int64(const char *mac)
> +{
> +	int64_t ret_mac = 0;
> +	int64_t token_val = 0;
> +	int token_cnt = 0;
> +	char tmp_mac[SPP_CONFIG_STR_LEN];
> +	char *str = tmp_mac;
> +	char *saveptr = NULL;
> +	char *endptr = NULL;
> +
> +	RTE_LOG(DEBUG, APP, "MAC address change. (mac = %s)\n", mac);
> +
> +	strcpy(tmp_mac, mac);
> +	while(1) {
> +		/* Split by clolon(':') */
> +		char *ret_tok = strtok_r(str, ":", &saveptr);
> +		if (unlikely(ret_tok == NULL)) {
> +			break;
> +		}
> +
> +		/* Convert string to hex value */
> +		int ret_tol = strtol(ret_tok, &endptr, 16);
> +		if (unlikely(ret_tok == endptr) || unlikely(*endptr != '\0')) {
> +			break;
> +		}
> +
> +		/* 各数値をまとめる */
> +		token_val = (int64_t)ret_tol;
> +		ret_mac |= token_val << (token_cnt * 8);
> +		token_cnt++;
> +		str = NULL;
> +	}
> +
> +	/* 区切り文字が5個以外 */
> +	if (unlikely(token_cnt != ETHER_ADDR_LEN)) {
> +		RTE_LOG(ERR, APP, "MAC address format error. (mac = %s)\n",
> +				 mac);
> +		return -1;
> +	}
> +
> +	RTE_LOG(DEBUG, APP, "MAC address change. (mac = %s => 0x%08lx)\n",
> +			 mac, ret_mac);
> +	return ret_mac;
> +}
> +
> +/*
> + * Classifier table読み込み
> + */
> +static int
> +config_load_classifier_table(const json_t *obj,
> +		struct spp_config_classifier_table *classifier_table)
> +{
> +	/* classifier_table用オブジェクト取得 */
> +	json_t *classifier_obj = json_path_get(obj, JSONPATH_CLASSIFIER_TABLE);
> +	if (unlikely(classifier_obj == NULL)) {
> +		RTE_LOG(ERR, APP, "Json object get failed. (path = %s)\n",
> +				JSONPATH_CLASSIFIER_TABLE);
> +		return -1;
> +	}
> +
> +	/* name取得 */
> +	int ret_name = config_get_str_value(classifier_obj, JSONPATH_NAME,
> +			classifier_table->name);
> +	if (unlikely(ret_name != 0)) {
> +		RTE_LOG(ERR, APP, "Classifier table name get failed.\n");
> +		return -1;
> +	}
> +
> +	/* table用オブジェクト取得 */
> +	json_t *array_obj = json_path_get(classifier_obj, JSONPATH_TABLE);
> +	if (unlikely(!array_obj)) {
> +		RTE_LOG(ERR, APP, "Json object get failed. (path = %s)\n",
> +				JSONPATH_TABLE);
> +		return -1;
> +	}
> +
> +	/* table用オブジェクトが配列かチェック */
> +	if (unlikely(!json_is_array(array_obj))) {
> +		RTE_LOG(ERR, APP, "Not an array. (path = %s)\n",
> +				JSONPATH_TABLE);
> +		return -1;
> +	}
> +
> +	/* table用オブジェクトの要素数取得 */
> +	int array_num = json_array_size(array_obj);
> +	if (unlikely(array_num <= 0) ||
> +			unlikely(array_num >= SPP_CONFIG_MAC_TABLE_MAX)) {
> +		RTE_LOG(ERR, APP, "Table size out of range. (path = %s, size = %d)\n",
> +				JSONPATH_TABLE, array_num);
> +		return -1;
> +	}
> +	classifier_table->num_table = array_num;
> +
> +	/* テーブルの各要素毎にデータ取得 */
> +	struct spp_config_mac_table_element *tmp_table = NULL;
> +	char if_str[SPP_CONFIG_STR_LEN];
> +	int table_cnt = 0;
> +	for (table_cnt = 0; table_cnt < array_num; table_cnt++) {
> +		tmp_table = &classifier_table->mac_tables[table_cnt];
> +
> +		/* 要素取得 */
> +		json_t *elements_obj = json_array_get(array_obj, table_cnt);
> +		if (unlikely(elements_obj == NULL)) {
> +			RTE_LOG(ERR, APP,
> +				"Element get failed. (No = %d, path = %s)\n",
> +				table_cnt, JSONPATH_TABLE);
> +			return -1;
> +		}
> +
> +		/* MACアドレス(文字列)取得 */
> +		int ret_mac = config_get_str_value(elements_obj, JSONPATH_MAC,
> +				tmp_table->mac_addr_str);
> +		if (unlikely(ret_mac != 0)) {
> +			RTE_LOG(ERR, APP,
> +				"MAC address get failed. (No = %d, path = %s)\n",
> +				table_cnt, JSONPATH_MAC);
> +			return -1;
> +		}
> +
> +		/* MACアドレス数値変換 */
> +		int64_t ret_mac64 = config_change_mac_str_to_int64(
> +				tmp_table->mac_addr_str);
> +		if (unlikely(ret_mac64 == -1)) {
> +			RTE_LOG(ERR, APP,
> +				"MAC address change failed. (No = %d, mac = %s)\n",
> +				table_cnt, tmp_table->mac_addr_str);
> +			return -1;
> +		}
> +		tmp_table->mac_addr = ret_mac64;
> +
> +		/* IF情報取得 */
> +		int ret_if_str = config_get_str_value(elements_obj,
> +				JSONPATH_PORT, if_str);
> +		if (unlikely(ret_if_str != 0)) {
> +			RTE_LOG(ERR, APP,
> +				"Interface get failed. (No = %d, path = %s)\n",
> +				table_cnt, JSONPATH_PORT);
> +			return -1;
> +		}
> +
> +		/* IF種別とIF番号に分割 */
> +		int ret_if = config_get_if_info(if_str, &tmp_table->port.if_type,
> +				&tmp_table->port.if_no);
> +		if (unlikely(ret_if != 0)) {
> +			RTE_LOG(ERR, APP,
> +				"Interface change failed. (No = %d, IF = %s)\n",
> +				table_cnt, if_str);
> +			return -1;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * 処理種別を文字列から数値変換
> + */
> +static enum spp_core_type
> +config_change_core_type(const char *core_type)
> +{
> +	if(strncmp(core_type, CONFIG_CORE_TYPE_CLASSIFIER_MAC,
> +			 strlen(CONFIG_CORE_TYPE_CLASSIFIER_MAC)+1) == 0) {
> +		/* Classifier */
> +		return SPP_CONFIG_CLASSIFIER_MAC;
> +	} else if (strncmp(core_type, CONFIG_CORE_TYPE_MERGE,
> +			 strlen(CONFIG_CORE_TYPE_MERGE)+1) == 0) {
> +		/* Merge */
> +		return SPP_CONFIG_MERGE;
> +	} else if (strncmp(core_type, CONFIG_CORE_TYPE_FORWARD,
> +			 strlen(CONFIG_CORE_TYPE_FORWARD)+1) == 0) {
> +		/* Forward */
> +		return SPP_CONFIG_FORWARD;
> +	}
> +	return SPP_CONFIG_UNUSE;
> +}
> +
> +/*
> + * 受信ポート取得
> + */
> +static int
> +config_set_rx_port(enum spp_core_type type, json_t *obj,
> +		struct spp_config_functions *functions)
> +{
> +	struct spp_config_port_info *tmp_rx_port = NULL;
> +	char if_str[SPP_CONFIG_STR_LEN];
> +	if (type == SPP_CONFIG_MERGE) {
> +		/* Merge */
> +		/* 受信ポート用オブジェクト取得 */
> +		json_t *array_obj = json_path_get(obj, JSONPATH_RX_PORT);
> +		if (unlikely(!array_obj)) {
> +			RTE_LOG(ERR, APP, "Json object get failed. (path = %s, route = merge)\n",
> +				JSONPATH_RX_PORT);
> +			return -1;
> +		}
> +
> +		/* 受信ポート用オブジェクトが配列かチェック */
> +		if (unlikely(!json_is_array(array_obj))) {
> +			RTE_LOG(ERR, APP, "Not an array. (path = %s, route = merge)\n",
> +				JSONPATH_TABLE);
> +			return -1;
> +		}
> +
> +		/* 受信ポート用オブジェクトの要素数取得 */
> +		int port_num = json_array_size(array_obj);
> +		if (unlikely(port_num <= 0) ||
> +				unlikely(port_num >= RTE_MAX_ETHPORTS)) {
> +			RTE_LOG(ERR, APP, "RX port out of range. (path = %s, port = %d, route = merge)\n",
> +					JSONPATH_RX_PORT, port_num);
> +			return -1;
> +		}
> +		functions->num_rx_port = port_num;
> +
> +		/* 要素毎にデータ取得 */
> +		int array_cnt = 0;
> +		for (array_cnt = 0; array_cnt < port_num; array_cnt++) {
> +			tmp_rx_port = &functions->rx_ports[array_cnt];
> +
> +			/* 要素取得 */
> +			json_t *elements_obj = json_array_get(array_obj, array_cnt);
> +			if (unlikely(elements_obj == NULL)) {
> +				RTE_LOG(ERR, APP,
> +					"Element get failed. (No = %d, path = %s, route = merge)\n",
> +					array_cnt, JSONPATH_RX_PORT);
> +				return -1;
> +			}
> +
> +			/* String type check */
> +			if (unlikely(!json_is_string(elements_obj))) {
> +				RTE_LOG(ERR, APP, "Not a string. (path = %s, No = %d, route = merge)\n",
> +						JSONPATH_RX_PORT, array_cnt);
> +				return -1;
> +			}
> +			strcpy(if_str, json_string_value(elements_obj));
> +
> +			/* IF種別とIF番号に分割 */
> +			int ret_if = config_get_if_info(if_str, &tmp_rx_port->if_type,
> +					&tmp_rx_port->if_no);
> +			if (unlikely(ret_if != 0)) {
> +				RTE_LOG(ERR, APP,
> +					"Interface change failed. (No = %d, port = %s, route = merge)\n",
> +					array_cnt, if_str);
> +				return -1;
> +			}
> +		}
> +	} else {
> +		/* Classifier/Forward */
> +		tmp_rx_port = &functions->rx_ports[0];
> +		functions->num_rx_port = 1;
> +
> +		/* 受信ポート取得 */
> +		int ret_rx_port = config_get_str_value(obj, JSONPATH_RX_PORT, if_str);
> +		if (unlikely(ret_rx_port != 0)) {
> +			RTE_LOG(ERR, APP, "RX port get failed.\n");
> +			return -1;
> +		}
> +
> +		/* IF種別とIF番号に分割 */
> +		int ret_if = config_get_if_info(if_str, &tmp_rx_port->if_type,
> +				&tmp_rx_port->if_no);
> +		if (unlikely(ret_if != 0)) {
> +			RTE_LOG(ERR, APP,
> +				"Interface change failed. (port = %s)\n", if_str);
> +			return -1;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * 送信先ポート情報取得
> + */
> +static int
> +config_set_tx_port(enum spp_core_type type, json_t *obj,
> +		struct spp_config_functions *functions,
> +		struct spp_config_classifier_table *classifier_table)
> +{
> +	struct spp_config_port_info *tmp_tx_port = NULL;
> +	char if_str[SPP_CONFIG_STR_LEN];
> +	if ((type == SPP_CONFIG_MERGE) || (type == SPP_CONFIG_FORWARD)) {
> +		/* Merge or Forward */
> +		tmp_tx_port = &functions->tx_ports[0];
> +		functions->num_tx_port = 1;
> +
> +		/* 送信ポート取得 */
> +		int ret_tx_port = config_get_str_value(obj,
> +				JSONPATH_TX_PORT, if_str);
> +		if (unlikely(ret_tx_port != 0)) {
> +			RTE_LOG(ERR, APP, "TX port get failed.\n");
> +			return -1;
> +		}
> +
> +		/* IF種別とIF番号に分割 */
> +		int ret_if = config_get_if_info(if_str, &tmp_tx_port->if_type,
> +				&tmp_tx_port->if_no);
> +		if (unlikely(ret_if != 0)) {
> +			RTE_LOG(ERR, APP,
> +				"Interface change failed. (port = %s)\n",
> +				if_str);
> +			return -1;
> +		}
> +	} else {
> +		/* Classifier */
> +		int cnt = 0;
> +		functions->num_tx_port = classifier_table->num_table;
> +		struct spp_config_mac_table_element *tmp_mac_table = NULL;
> +		for (cnt = 0; cnt < classifier_table->num_table; cnt++) {
> +			tmp_tx_port = &functions->tx_ports[cnt];
> +			tmp_mac_table = &classifier_table->mac_tables[cnt];
> +
> +			/* MAC振り分けテーブルより設定 */
> +			tmp_tx_port->if_type = tmp_mac_table->port.if_type;
> +			tmp_tx_port->if_no   = tmp_mac_table->port.if_no;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * プロセス情報取得
> + */
> +static int
> +config_load_proc_info(const json_t *obj, int node_id, struct spp_config_area *config)
> +{
> +	struct spp_config_proc_info *proc = &config->proc;
> +	struct spp_config_classifier_table *classifier_table = &config->classifier_table;
> +
> +	/* proc_table用オブジェクト取得 */
> +	json_t *proc_table_obj = json_path_get(obj, JSONPATH_PROC_TABLE);
> +	if (unlikely(proc_table_obj == NULL)) {
> +		RTE_LOG(ERR, APP, "Json object get failed. (path = %s)\n",
> +				JSONPATH_PROC_TABLE);
> +		return -1;
> +	}
> +
> +	/* table用オブジェクトが配列かチェック */
> +	if (unlikely(!json_is_array(proc_table_obj))) {
> +		RTE_LOG(ERR, APP, "Not an array. (path = %s)\n",
> +				JSONPATH_TABLE);
> +		return -1;
> +	}
> +
> +	/* table用オブジェクトの要素数取得 */
> +	int proc_table_num = json_array_size(proc_table_obj);
> +	if (unlikely(proc_table_num < node_id)) {
> +		RTE_LOG(ERR, APP, "No process data. (Size = %d, Node = %d)\n",
> +			proc_table_num, node_id);
> +		return -1;
> +	}
> +
> +	/* 要素取得 */
> +	json_t *proc_obj = json_array_get(proc_table_obj, node_id);
> +	if (unlikely(proc_obj == NULL)) {
> +		RTE_LOG(ERR, APP, "Process data get failed. (Node = %d)\n",
> +				node_id);
> +		return -1;
> +	}
> +
> +	/* name取得 */
> +	int ret_name = config_get_str_value(proc_obj, JSONPATH_NAME, proc->name);
> +	if (unlikely(ret_name != 0)) {
> +		RTE_LOG(ERR, APP, "Process name get failed.\n");
> +		return -1;
> +	}
> +
> +	/* VHOST数取得 */
> +	int ret_vhost = config_get_int_value(proc_obj, JSONPATH_NUM_VHOST,
> +			&proc->num_vhost);
> +	if (unlikely(ret_vhost != 0)) {
> +		RTE_LOG(ERR, APP, "VHOST number get failed.\n");
> +		return -1;
> +	}
> +
> +	/* RING数取得 */
> +	int ret_ring = config_get_int_value(proc_obj, JSONPATH_NUM_RING,
> +			&proc->num_ring);
> +	if (unlikely(ret_ring != 0)) {
> +		RTE_LOG(ERR, APP, "RING number get failed.\n");
> +		return -1;
> +	}
> +
> +	/* functions用オブジェクト取得 */
> +	json_t *array_obj = json_path_get(proc_obj, JSONPATH_FUNCTIONS);
> +	if (unlikely(!array_obj)) {
> +		RTE_LOG(ERR, APP, "Json object get failed. (path = %s)\n",
> +				JSONPATH_FUNCTIONS);
> +		return -1;
> +	}
> +
> +	/* functions用オブジェクトが配列かチェック */
> +	if (unlikely(!json_is_array(array_obj))) {
> +		RTE_LOG(ERR, APP, "Not an array. (path = %s)\n",
> +				JSONPATH_FUNCTIONS);
> +		return -1;
> +	}
> +
> +	/* functions用オブジェクトの要素数取得 */
> +	int array_num = json_array_size(array_obj);
> +	if (unlikely(array_num <= 0) ||
> +			unlikely(array_num >= SPP_CONFIG_CORE_MAX)) {
> +		RTE_LOG(ERR, APP, "Functions size out of range. (path = %s, size = %d)\n",
> +				JSONPATH_TABLE, array_num);
> +		return -1;
> +	}
> +	proc->num_func = array_num;
> +
> +	/* 要素毎にデータ取得 */
> +	struct spp_config_functions *tmp_functions = NULL;
> +	char core_type_str[SPP_CONFIG_STR_LEN];
> +	int array_cnt = 0;
> +	for (array_cnt = 0; array_cnt < array_num; array_cnt++) {
> +		tmp_functions = &proc->functions[array_cnt];
> +
> +		/* 要素取得 */
> +		json_t *elements_obj = json_array_get(array_obj, array_cnt);
> +		if (unlikely(elements_obj == NULL)) {
> +			RTE_LOG(ERR, APP,
> +				"Element get failed. (No = %d, path = %s)\n",
> +				array_cnt, JSONPATH_FUNCTIONS);
> +			return -1;
> +		}
> +
> +		/* CORE番号取得 */
> +		int ret_core = config_get_int_value(elements_obj, JSONPATH_CORE_NO,
> +				&tmp_functions->core_no);
> +		if (unlikely(ret_core != 0)) {
> +			RTE_LOG(ERR, APP, "Core number get failed. (No = %d)\n",
> +					array_cnt);
> +			return -1;
> +		}
> +
> +		/* 処理種別取得 */
> +		int ret_core_type = config_get_str_value(elements_obj,
> +				 JSONPATH_CORE_TYPE, core_type_str);
> +		if (unlikely(ret_core_type != 0)) {
> +			RTE_LOG(ERR, APP, "Core type get failed. (No = %d)\n",
> +					array_cnt);
> +			return -1;
> +		}
> +
> +		/* 処理種別を数値に変換 */
> +		enum spp_core_type core_type = config_change_core_type(core_type_str);
> +		if (unlikely(core_type == SPP_CONFIG_UNUSE)) {
> +			RTE_LOG(ERR, APP,
> +				"Unknown core type. (No = %d, type = %s)\n",
> +				array_cnt, core_type_str);
> +			return -1;
> +		}
> +		tmp_functions->type = core_type;
> +
> +		/* 受信ポート取得 */
> +		int ret_rx_port = config_set_rx_port(core_type, elements_obj,
> +				tmp_functions);
> +		if (unlikely(ret_rx_port != 0)) {
> +			RTE_LOG(ERR, APP, "RX port set failure. (No = %d)\n",
> +					array_cnt);
> +			return -1;
> +		}
> +
> +		/* 送信ポート取得 */
> +		int ret_tx_port = config_set_tx_port(core_type, elements_obj,
> +				tmp_functions, classifier_table);
> +		if (unlikely(ret_tx_port != 0)) {
> +			RTE_LOG(ERR, APP, "TX port set failure. (No = %d)\n",
> +					array_cnt);
> +			return -1;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Load config file
> + * OK : 0
> + * NG : -1
> + */
> +int
> +spp_config_load_file(int node_id, struct spp_config_area *config)
> +{
> +	/* Config initialize */
> +	config_init_data(config);
> +	
> +	/* Config load */
> +	json_error_t json_error;
> +	json_t *conf_obj = json_load_file(SPP_CONFIG_FILE_PATH, 0, &json_error);
> +	if (unlikely(conf_obj == NULL)) {
> +		/* Load error */
> +		RTE_LOG(ERR, APP, "Config load failed. (path = %s, text = %s)\n",
> +				 SPP_CONFIG_FILE_PATH, json_error.text);
> +		return -1;
> +	}
> +
> +	/* classifier table */
> +	int ret_classifier = config_load_classifier_table(conf_obj,
> +			&config->classifier_table);
> +	if (unlikely(ret_classifier != 0)) {
> +		RTE_LOG(ERR, APP, "Classifier table load failed.\n");
> +		json_decref(conf_obj);
> +		return -1;
> +	}
> +
> +	/* proc info */
> +	int ret_proc = config_load_proc_info(conf_obj, node_id, config);
> +	if (unlikely(ret_proc != 0)) {
> +		RTE_LOG(ERR, APP, "Process table load failed.\n");
> +		json_decref(conf_obj);
> +		return -1;
> +	}
> +
> +	/* Config object release */
> +	json_decref(conf_obj);
> +
> +	return 0;
> +}
> diff --git a/src/vf/spp_config.h b/src/vf/spp_config.h
> new file mode 100644
> index 0000000..d2a6a4b
> --- /dev/null
> +++ b/src/vf/spp_config.h
> @@ -0,0 +1,90 @@
> +#ifndef __SPP_CONFIG_H__
> +#define __SPP_CONFIG_H__
> +
> +#include "common.h"
> +
> +#define SPP_CONFIG_FILE_PATH "/usr/local/etc/spp/spp.json"
> +
> +#define SPP_CONFIG_IFTYPE_NIC   "nic"
> +#define SPP_CONFIG_IFTYPE_VHOST "vhost"
> +#define SPP_CONFIG_IFTYPE_RING  "ring"
> +
> +#define SPP_CONFIG_STR_LEN 32
> +#define SPP_CONFIG_MAC_TABLE_MAX 16
> +#define SPP_CONFIG_CORE_MAX 64
> +
> +/*
> + * Process type for each CORE
> + */
> +enum spp_core_type {
> +	SPP_CONFIG_UNUSE,
> +	SPP_CONFIG_CLASSIFIER_MAC,
> +	SPP_CONFIG_MERGE,
> +	SPP_CONFIG_FORWARD,
> +};
> +
> +/*
> + * Interface information structure
> + */
> +struct spp_config_port_info {
> +	enum port_type	if_type;
> +	int		if_no;
> +};
> +
> +/*
> + * MAC Table information structure
> + */
> +struct spp_config_mac_table_element {
> +	struct		spp_config_port_info port;
> +	char		mac_addr_str[SPP_CONFIG_STR_LEN];
> +	uint64_t	mac_addr;
> +};
> +
> +/*
> + * Classifier Table information structure
> + */
> +struct spp_config_classifier_table {
> +	char	name[SPP_CONFIG_STR_LEN];
> +	int	num_table;
> +	struct spp_config_mac_table_element mac_tables[SPP_CONFIG_MAC_TABLE_MAX];
> +};
> +
> +/*
> + * Functions information structure
> + */
> +struct spp_config_functions {
> +	int	core_no;
> +	enum	spp_core_type type;
> +	int	num_rx_port;
> +	int	num_tx_port;
> +	struct spp_config_port_info rx_ports[RTE_MAX_ETHPORTS];
> +	struct spp_config_port_info tx_ports[RTE_MAX_ETHPORTS];
> +};
> +
> +/*
> + * Process information structure
> + */
> +struct spp_config_proc_info {
> +	char	name[SPP_CONFIG_STR_LEN];
> +	int	num_vhost;
> +	int	num_ring;
> +	int	num_func;
> +	struct spp_config_functions functions[SPP_CONFIG_CORE_MAX];
> +};
> +
> +/*
> + * Config information structure
> + */
> +struct spp_config_area {
> +	struct spp_config_proc_info proc;
> +	struct spp_config_classifier_table classifier_table;
> +};
> +
> +/*
> + * Load config file
> + * OK : 0
> + * NG : -1
> + */
> +int spp_config_load_file(int node_id, struct spp_config_area *config);
> +
> +#endif /* __SPP_CONFIG_H__ */
> diff --git a/src/vf/spp_forward.c b/src/vf/spp_forward.c
> new file mode 100644
> index 0000000..4396929
> --- /dev/null
> +++ b/src/vf/spp_forward.c
> @@ -0,0 +1,106 @@
> +#include "spp_vf.h"
> +#include "ringlatencystats.h"
> +
> +#define RTE_LOGTYPE_SPP_FORWARD RTE_LOGTYPE_USER1
> +
> +/*
> + * 送受信ポートの経路情報
> + */
> +struct rxtx {
> +	struct spp_core_port_info rx;
> +	struct spp_core_port_info tx;
> +};
> +
> +/*
> + * 使用するIF情報を移し替える
> + */
> +void
> +set_use_interface(struct spp_core_port_info *dst,
> +		struct spp_core_port_info *src)
> +{
> +	dst->if_type   = src->if_type;
> +	dst->if_no     = src->if_no;
> +	dst->dpdk_port = src->dpdk_port;
> +}
> +
> +/*
> + * Merge/Forward
> + */
> +int
> +spp_forward(void *arg)
> +{
> +	unsigned int lcore_id = rte_lcore_id();
> +	struct spp_core_info *core_info = (struct spp_core_info *)arg;
> +	int if_cnt, rxtx_num = 0;
> +	struct rxtx patch[RTE_MAX_ETHPORTS];
> +
> +	/* RX/TX Info setting */
> +	rxtx_num = core_info->num_rx_port;
> +	for (if_cnt = 0; if_cnt < rxtx_num; if_cnt++) {
> +		set_use_interface(&patch[if_cnt].rx,
> +				&core_info->rx_ports[if_cnt]);
> +		if (core_info->type == SPP_CONFIG_FORWARD) {
> +			/* FORWARD */
> +			set_use_interface(&patch[if_cnt].tx,
> +					&core_info->tx_ports[if_cnt]);
> +		} else {
> +			/* MERGE */
> +			set_use_interface(&patch[if_cnt].tx,
> +					&core_info->tx_ports[0]);
> +		}
> +	}
> +
> +	/* Thread IDLE */
> +	core_info->status = SPP_CORE_IDLE;
> +	RTE_LOG(INFO, FORWARD, "Core[%d] Start. (type = %d)\n", lcore_id,
> +			core_info->type);
> +
> +	int cnt, nb_rx, nb_tx, buf;
> +	struct spp_core_port_info *rx;
> +	struct spp_core_port_info *tx;
> +	struct rte_mbuf *bufs[MAX_PKT_BURST];
> +	while (likely(core_info->status == SPP_CORE_IDLE) ||
> +			likely(core_info->status == SPP_CORE_FORWARD)) {
> +		while (likely(core_info->status == SPP_CORE_FORWARD)) {
> +			for (cnt = 0; cnt < rxtx_num; cnt++) {
> +				rx = &patch[cnt].rx;
> +				tx = &patch[cnt].tx;
> +
> +				/* Packet receive */
> +				nb_rx = rte_eth_rx_burst(rx->dpdk_port, 0, bufs, MAX_PKT_BURST);
> +				if (unlikely(nb_rx == 0)) {
> +					continue;
> +				}
> +
> +#ifdef SPP_RINGLATENCYSTATS_ENABLE /* RING滞留時間 */
> +				if (rx->if_type == RING) {
> +					/* Receive port is RING */
> +					spp_ringlatencystats_calculate_latency(rx->if_no,
> +							bufs, nb_rx);
> +				}
> +				if (tx->if_type == RING) {
> +					/* Send port is RING */
> +					spp_ringlatencystats_add_time_stamp(tx->if_no,
> +							bufs, nb_rx);
> +				}
> +#endif /* SPP_RINGLATENCYSTATS_ENABLE */
> +
> +				/* Send packet */
> +				nb_tx = rte_eth_tx_burst(tx->dpdk_port, 0, bufs, nb_rx);
> +
> +				/* Free any unsent packets. */
> +				if (unlikely(nb_tx < nb_rx)) {
> +					for (buf = nb_tx; buf < nb_rx; buf++) {
> +						rte_pktmbuf_free(bufs[buf]);
> +					}
> +				}
> +			}
> +		}
> +	}
> +
> +	/* Thread STOP */
> +	RTE_LOG(INFO, FORWARD, "Core[%d] End. (type = %d)\n", lcore_id,
> +			core_info->type);
> +	core_info->status = SPP_CORE_STOP;
> +	return 0;
> +}
> diff --git a/src/vf/spp_forward.h b/src/vf/spp_forward.h
> new file mode 100644
> index 0000000..33208bf
> --- /dev/null
> +++ b/src/vf/spp_forward.h
> @@ -0,0 +1,9 @@
> +#ifndef __SPP_FORWARD_H__
> +#define __SPP_FORWARD_H__
> +
> +/*
> + * Merge/Forward
> + */
> +int spp_forward(void *arg);
> +
> +#endif /* __SPP_FORWARD_H__ */
> diff --git a/src/vf/spp_vf.c b/src/vf/spp_vf.c
> new file mode 100644
> index 0000000..ee5cf63
> --- /dev/null
> +++ b/src/vf/spp_vf.c
> @@ -0,0 +1,794 @@
> +#include <arpa/inet.h>
> +#include <getopt.h>
> +
> +#include <rte_eth_ring.h>
> +#include <rte_eth_vhost.h>
> +#include <rte_memzone.h>
> +
> +#include "spp_vf.h"
> +#include "ringlatencystats.h"
> +#include "classifier_mac.h"
> +#include "spp_forward.h"
> +
> +/* define */
> +#define SPP_CORE_STATUS_CHECK_MAX 5
> +#define SPP_RING_LATENCY_STATS_SAMPLING_INTERVAL 1000000
> +
> +/* struct */
> +struct startup_param {
> +	uint64_t cpu;
> +};
> +
> +struct patch_info {
> +	int	use_flg;
> +	int	dpdk_port;
> +	struct spp_config_mac_table_element *mac_info;
> +	struct spp_core_port_info *rx_core;
> +	struct spp_core_port_info *tx_core;
> +};
> +
> +struct if_info {
> +	int num_nic;
> +	int num_vhost;
> +	int num_ring;
> +	struct patch_info nic_patchs[RTE_MAX_ETHPORTS];
> +	struct patch_info vhost_patchs[RTE_MAX_ETHPORTS];
> +	struct patch_info ring_patchs[RTE_MAX_ETHPORTS];
> +};
> +
> +static struct spp_config_area	g_config;
> +static struct startup_param	g_startup_param;
> +static struct if_info		g_if_info;
> +static struct spp_core_info	g_core_info[SPP_CONFIG_CORE_MAX];
> +
> +/*
> + * print a usage message
> + */
> +static void
> +usage(const char *progname)
> +{
> +	RTE_LOG(INFO, APP, "Usage: %s [EAL args]\n\n", progname);
> +}
> +
> +/*
> + * Set RING PMD
> + */
> +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 (unlikely(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;
> +}
> +
> +/*
> + * Set VHOST PMD
> + */
> +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 (unlikely(mp == NULL)) {
> +		RTE_LOG(ERR, APP, "Cannot get mempool for mbufs. (name = %s)\n",
> +				PKTMBUF_POOL_NAME);
> +		return -1;
> +	}
> +
> +	/* 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 (unlikely(ret < 0)) {
> +		RTE_LOG(ERR, APP, "rte_eth_dev_attach error. (ret = %d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = rte_eth_dev_configure(vhost_port_id, nr_queues, nr_queues,
> +		&port_conf);
> +	if (unlikely(ret < 0)) {
> +		RTE_LOG(ERR, APP, "rte_eth_dev_configure error. (ret = %d)\n",
> +				ret);
> +		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 (unlikely(ret < 0)) {
> +			RTE_LOG(ERR, APP,
> +				"rte_eth_rx_queue_setup error. (ret = %d)\n",
> +				ret);
> +			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 (unlikely(ret < 0)) {
> +			RTE_LOG(ERR, APP,
> +				"rte_eth_tx_queue_setup error. (ret = %d)\n",
> +				ret);
> +			return ret;
> +		}
> +	}
> +
> +	/* Start the Ethernet port. */
> +	ret = rte_eth_dev_start(vhost_port_id);
> +	if (unlikely(ret < 0)) {
> +		RTE_LOG(ERR, APP, "rte_eth_dev_start error. (ret = %d)\n", ret);
> +		return ret;
> +	}
> +
> +	RTE_LOG(DEBUG, APP, "vhost port id %d\n", vhost_port_id);
> +
> +	return vhost_port_id;
> +}
> +
> +/*
> + * Check core status
> + */
> +static int
> +check_core_status(enum spp_core_status status)
> +{
> +	int cnt;
> +	for (cnt = 0; cnt < SPP_CONFIG_CORE_MAX; cnt++) {
> +		if (g_core_info[cnt].type == SPP_CONFIG_UNUSE) {
> +			continue;
> +		}
> +		if (g_core_info[cnt].status != status) {
> +			/* Status mismatch */
> +			return -1;
> +		}
> +	}
> +	return 0;
> +}
> +
> +/*
> + * Wait for core status check
> + */
> +static int
> +check_core_status_wait(enum spp_core_status status)
> +{
> +	int cnt = 0;
> +	for (cnt = 0; cnt < SPP_CORE_STATUS_CHECK_MAX; cnt++) {
> +		sleep(1);
> +		int ret = check_core_status(status);
> +		if (ret == 0) {
> +			return 0;
> +		}
> +	}
> +
> +	RTE_LOG(ERR, APP, "Status check time out. (status = %d)\n", status);
> +	return -1;
> +}
> +
> +/*
> + * Set core status
> + */
> +static void
> +set_core_status(enum spp_core_status status)
> +{
> +	int core_cnt = 0;
> +	for(core_cnt = 0; core_cnt < SPP_CONFIG_CORE_MAX; core_cnt++) {
> +		g_core_info[core_cnt].status = status;
> +	}
> +}
> +
> +/*
> + * Process stop
> + */
> +void
> +stop_process(int signal) {
> +	if (unlikely(signal != SIGTERM) &&
> +			unlikely(signal != SIGINT)) {
> +		/* Other signals */
> +		return;
> +	}
> +
> +	set_core_status(SPP_CORE_STOP_REQUEST);
> +}
> +
> +/*
> + * 起動パラメータのCPUのbitmapを数値へ変換
> + */
> +static int
> +parse_cpu_bit(uint64_t *cpu, const char *cpu_bit)
> +{
> +	char *endptr = NULL;
> +	uint64_t temp;
> +
> +	temp = strtoull(cpu_bit, &endptr, 0);
> +	if (unlikely(endptr == cpu_bit) || unlikely(*endptr != '\0')) {
> +		return -1;
> +	}
> +
> +	*cpu = temp;
> +	RTE_LOG(DEBUG, APP, "cpu = %lu", *cpu);
> +	return 0;
> +}
> +
> +/*
> + * 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} };
> +
> +	/* Check DPDK parameter */
> +	optind = 0;
> +	opterr = 0;
> +	while ((opt = getopt_long(argc, argvopt, "c:", lgopts,
> +			&option_index)) != EOF) {
> +		switch (opt) {
> +		case 'c':
> +			/* CPU */
> +			if (parse_cpu_bit(&g_startup_param.cpu, optarg) != 0) {
> +				usage(progname);
> +				return -1;
> +			}
> +			break;
> +		default:
> +			/* CPU */
> +			/* DPDKのパラメータは他にもあるので、エラーとはしない */
> +			break;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * IF種別&IF番号のIF情報の領域取得
> + */
> +static struct patch_info *
> +get_if_area(enum port_type if_type, int if_no)
> +{
> +	switch (if_type) {
> +	case PHY:
> +		return &g_if_info.nic_patchs[if_no];
> +		break;
> +	case VHOST:
> +		return &g_if_info.vhost_patchs[if_no];
> +		break;
> +	case RING:
> +		return &g_if_info.ring_patchs[if_no];
> +		break;
> +	default:
> +		/* エラー出力は呼び元でチェック */
> +		return NULL;
> +		break;
> +	}
> +}
> +
> +/*
> + * IF情報初期化
> + */
> +static void
> +init_if_info()
> +{
> +	memset(&g_if_info, 0x00, sizeof(g_if_info));
> +}
> +
> +/*
> + * CORE情報初期化
> + */
> +static void
> +init_core_info()
> +{
> +	memset(&g_core_info, 0x00, sizeof(g_core_info));
> +	int core_cnt, port_cnt;
> +	for (core_cnt = 0; core_cnt < SPP_CONFIG_CORE_MAX; core_cnt++) {
> +		for (port_cnt = 0; port_cnt < RTE_MAX_ETHPORTS; port_cnt++) {
> +			g_core_info[core_cnt].rx_ports[port_cnt].if_type = UNDEF;
> +			g_core_info[core_cnt].tx_ports[port_cnt].if_type = UNDEF;
> +		}
> +	}
> +}
> +
> +/*
> + * Configのプロセス情報から管理情報に設定
> + */
> +static int
> +set_form_proc_info(struct spp_config_area *config)
> +{
> +	/* Configのproc_infoから内部管理情報へ設定 */
> +	int core_cnt, rx_start, rx_cnt, tx_start, tx_cnt;
> +	enum port_type if_type;
> +	int if_no;
> +	int64_t cpu_bit = 0;
> +	struct spp_config_functions *core_func = NULL;
> +	struct spp_core_info *core_info = NULL;
> +	struct patch_info *patch_info = NULL;
> +	for (core_cnt = 0; core_cnt < config->proc.num_func; core_cnt++) {
> +		core_func = &config->proc.functions[core_cnt];
> +		core_info = &g_core_info[core_func->core_no];
> +
> +		if (core_func->type == SPP_CONFIG_UNUSE) {
> +			continue;
> +		}
> +
> +		/* Forwardをまとめる事は可、他種別は不可 */
> +		if ((core_info->type != SPP_CONFIG_UNUSE) &&
> +				((core_info->type != SPP_CONFIG_FORWARD) &&
> +				(core_func->type != SPP_CONFIG_FORWARD))) {
> +			RTE_LOG(ERR, APP, "Core in use. (core = %d, type = %d/%d)\n",
> +					core_func->core_no,
> +					core_func->type, core_info->type);
> +			return -1;
> +		}
> +
> +		/* Set CORE type */
> +		core_info->type = core_func->type;
> +		cpu_bit |= 1 << core_func->core_no;
> +
> +		/* Set RX port */
> +		rx_start = core_info->num_rx_port;
> +		core_info->num_rx_port += core_func->num_rx_port;
> +		for (rx_cnt = 0; rx_cnt < core_func->num_rx_port; rx_cnt++) {
> +			if_type = core_func->rx_ports[rx_cnt].if_type;
> +			if_no   = core_func->rx_ports[rx_cnt].if_no;
> +
> +			core_info->rx_ports[rx_start + rx_cnt].if_type = if_type;
> +			core_info->rx_ports[rx_start + rx_cnt].if_no   = if_no;
> +
> +			/* IF種別とIF番号に対応するIF情報の領域取得 */
> +			patch_info = get_if_area(if_type, if_no);
> +
> +			patch_info->use_flg = 1;
> +			if (unlikely(patch_info->rx_core != NULL)) {
> +				RTE_LOG(ERR, APP, "Used RX port (if_type = %d, if_no = %d)\n",
> +						if_type, if_no);
> +				return -1;
> +			}
> +
> +			/* IF情報からCORE情報を変更する場合用に設定 */
> +			patch_info->rx_core = &core_info->rx_ports[rx_start + rx_cnt];
> +		}
> +
> +		/* Set TX port */
> +		tx_start = core_info->num_tx_port;
> +		core_info->num_tx_port += core_func->num_tx_port;
> +		for (tx_cnt = 0; tx_cnt < core_func->num_tx_port; tx_cnt++) {
> +			if_type = core_func->tx_ports[tx_cnt].if_type;
> +			if_no   = core_func->tx_ports[tx_cnt].if_no;
> +
> +			core_info->tx_ports[tx_start + tx_cnt].if_type = if_type;
> +			core_info->tx_ports[tx_start + tx_cnt].if_no   = if_no;
> +
> +			/* IF種別とIF番号に対応するIF情報の領域取得 */
> +			patch_info = get_if_area(if_type, if_no);
> +
> +			patch_info->use_flg = 1;
> +			if (unlikely(patch_info->tx_core != NULL)) {
> +				RTE_LOG(ERR, APP, "Used TX port (if_type = %d, if_no = %d)\n",
> +						if_type, if_no);
> +				return -1;
> +			}
> +
> +			/* IF情報からCORE情報を変更する場合用に設定 */
> +			patch_info->tx_core = &core_info->tx_ports[tx_start + tx_cnt];
> +		}
> +	}
> +
> +	if (unlikely((cpu_bit & g_startup_param.cpu) != cpu_bit)) {
> +		/* CPU mismatch */
> +		RTE_LOG(ERR, APP, "CPU mismatch (cpu param = %lx, config = %lx)\n",
> +				g_startup_param.cpu, cpu_bit);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +/*
> + * ConfigのMACテーブル情報から管理情報に設定
> + */
> +static int
> +set_from_classifier_table(struct spp_config_area *config)
> +{
> +	/* MAC table */
> +	enum port_type if_type;
> +	int if_no = 0;
> +	int mac_cnt = 0;
> +	struct spp_config_mac_table_element *mac_table = NULL;
> +	struct patch_info *patch_info = NULL;
> +	for (mac_cnt = 0; mac_cnt < config->classifier_table.num_table; mac_cnt++) {
> +		mac_table = &config->classifier_table.mac_tables[mac_cnt];
> +
> +		if_type = mac_table->port.if_type;
> +		if_no   = mac_table->port.if_no;
> +
> +		/* IF種別とIF番号に対応するIF情報の領域取得 */
> +		patch_info = get_if_area(if_type, if_no);
> +
> +		if (unlikely(patch_info->use_flg == 0)) {
> +			RTE_LOG(ERR, APP, "Not used interface (if_type = %d, if_no = %d)\n",
> +					if_type, if_no);
> +			return -1;
> +		}
> +
> +		/* CORE情報側にもMACアドレスの情報設定 */
> +		/* MACアドレスは送信側のみに影響する為、送信側のみ設定 */
> +		patch_info->mac_info = mac_table;
> +		if (unlikely(patch_info->tx_core != NULL)) {
> +			patch_info->tx_core->mac_addr = mac_table->mac_addr;
> +			strcpy(patch_info->tx_core->mac_addr_str, mac_table->mac_addr_str);
> +		}
> +	}
> +	return 0;
> +}
> +
> +/*
> + * NIC用の情報設定
> + */
> +static int
> +set_nic_interface(struct spp_config_area *config)
> +{
> +	/* NIC Setting */
> +	g_if_info.num_nic = rte_eth_dev_count();
> +	if (g_if_info.num_nic > RTE_MAX_ETHPORTS) {
> +		g_if_info.num_nic = RTE_MAX_ETHPORTS;
> +	}
> +
> +	int nic_cnt, nic_num = 0;
> +	struct patch_info *patch_info = NULL;
> +	for (nic_cnt = 0; nic_cnt < RTE_MAX_ETHPORTS; nic_cnt++) {
> +		patch_info = &g_if_info.nic_patchs[nic_cnt];
> +		/* Set DPDK port */
> +		patch_info->dpdk_port = nic_cnt;
> +
> +		if (patch_info->use_flg == 0) {
> +			/* Not Used */
> +			continue;
> +		}
> +
> +		/* CORE情報側にもDPDKポート番号の情報設定 */
> +		if (patch_info->rx_core != NULL) {
> +			patch_info->rx_core->dpdk_port = nic_cnt;
> +		}
> +		if (patch_info->tx_core != NULL) {
> +			patch_info->tx_core->dpdk_port = nic_cnt;
> +		}
> +
> +		/* NICの設定数カウント */
> +		nic_num++;
> +	}
> +
> +	if (unlikely(nic_num > g_if_info.num_nic)) {
> +		RTE_LOG(ERR, APP, "NIC Setting mismatch. (IF = %d, config = %d)\n",
> +				nic_num, g_if_info.num_nic);
> +		return -1;
> +	}
> +	
> +	return 0;
> +}
> +
> +/*
> + * VHOST用の情報設定
> + */
> +static int
> +set_vhost_interface(struct spp_config_area *config)
> +{
> +	/* VHOST Setting */
> +	int vhost_cnt, vhost_num = 0;
> +	g_if_info.num_vhost = config->proc.num_vhost;
> +	struct patch_info *patch_info = NULL;
> +	for (vhost_cnt = 0; vhost_cnt < RTE_MAX_ETHPORTS; vhost_cnt++) {
> +		patch_info = &g_if_info.vhost_patchs[vhost_cnt];
> +		if (patch_info->use_flg == 0) {
> +			/* Not Used */
> +			continue;
> +		}
> +
> +		/* Set DPDK port */
> +		int dpdk_port = add_vhost_pmd(vhost_cnt);
> +		if (unlikely(dpdk_port < 0)) {
> +			RTE_LOG(ERR, APP, "VHOST add failed. (no = %d)\n",
> +					vhost_cnt);
> +			return -1;
> +		}
> +		patch_info->dpdk_port = dpdk_port;
> +
> +		/* CORE情報側にもDPDKポート番号の情報設定 */
> +		if (patch_info->rx_core != NULL) {
> +			patch_info->rx_core->dpdk_port = dpdk_port;
> +		}
> +		if (patch_info->tx_core != NULL) {
> +			patch_info->tx_core->dpdk_port = dpdk_port;
> +		}
> +		vhost_num++;
> +	}
> +	if (unlikely(vhost_num > g_if_info.num_vhost)) {
> +		RTE_LOG(ERR, APP, "VHOST Setting mismatch. (IF = %d, config = %d)\n",
> +				vhost_num, g_if_info.num_vhost);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +/*
> + * RING用の情報設定
> + */
> +static int
> +set_ring_interface(struct spp_config_area *config)
> +{
> +	/* RING Setting */
> +	int ring_cnt, ring_num = 0;
> +	g_if_info.num_ring = config->proc.num_ring;
> +	struct patch_info *patch_info = NULL;
> +	for (ring_cnt = 0; ring_cnt < RTE_MAX_ETHPORTS; ring_cnt++) {
> +		patch_info = &g_if_info.ring_patchs[ring_cnt];
> +		if (patch_info->use_flg == 0) {
> +			/* Not Used */
> +			continue;
> +		}
> +
> +		/* Set DPDK port */
> +		int dpdk_port = add_ring_pmd(ring_cnt);
> +		if (unlikely(dpdk_port < 0)) {
> +			RTE_LOG(ERR, APP, "RING add failed. (no = %d)\n",
> +					ring_cnt);
> +			return -1;
> +		}
> +		patch_info->dpdk_port = dpdk_port;
> +
> +		/* CORE情報側にもDPDKポート番号の情報設定 */
> +		if (patch_info->rx_core != NULL) {
> +			patch_info->rx_core->dpdk_port = dpdk_port;
> +		}
> +		if (patch_info->tx_core != NULL) {
> +			patch_info->tx_core->dpdk_port = dpdk_port;
> +		}
> +		ring_num++;
> +	}
> +	if (unlikely(ring_num > g_if_info.num_ring)) {
> +		RTE_LOG(ERR, APP, "RING Setting mismatch. (IF = %d, config = %d)\n",
> +				ring_num, g_if_info.num_ring);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +/*
> + * 管理データ初期設定
> + */
> +static int
> +init_manage_data(struct spp_config_area *config)
> +{
> +	/* Initialize */
> +	init_if_info(config);
> +	init_core_info(config);
> +
> +	/* Set config data */
> +	int ret_proc = set_form_proc_info(config);
> +	if (unlikely(ret_proc != 0)) {
> +		/* 関数内でログ出力済みなので、省略 */
> +		return -1;
> +	}
> +	int ret_classifier = set_from_classifier_table(config);
> +	if (unlikely(ret_classifier != 0)) {
> +		/* 関数内でログ出力済みなので、省略 */
> +		return -1;
> +	}
> +
> +	/* Set interface data */
> +	int ret_nic = set_nic_interface(config);
> +	if (unlikely(ret_nic != 0)) {
> +		/* 関数内でログ出力済みなので、省略 */
> +		return -1;
> +	}
> +
> +	int ret_vhost = set_vhost_interface(config);
> +	if (unlikely(ret_vhost != 0)) {
> +		/* 関数内でログ出力済みなので、省略 */
> +		return -1;
> +	}
> +
> +	int ret_ring = set_ring_interface(config);
> +	if (unlikely(ret_ring != 0)) {
> +		/* 関数内でログ出力済みなので、省略 */
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +#ifdef SPP_RINGLATENCYSTATS_ENABLE /* RING滞留時間 */
> +static void
> +print_ring_latency_stats()
> +{
> +	/* Clear screen and move to top left */
> +	const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' };
> +	const char clr[] = { 27, '[', '2', 'J', '\0' };
> +	printf("%s%s", clr, topLeft);
> +
> +	/* Print per RING */
> +	int ring_cnt, stats_cnt;
> +	struct spp_ringlatencystats_ring_latency_stats stats[RTE_MAX_ETHPORTS];
> +	memset(&stats, 0x00, sizeof(stats));
> +
> +	printf("RING Latency\n");
> +	printf(" RING");
> +	for (ring_cnt = 0; ring_cnt < RTE_MAX_ETHPORTS; ring_cnt++) {
> +		if (g_if_info.ring_patchs[ring_cnt].use_flg == 0) {
> +			continue;
> +		}
> +		spp_ringlatencystats_get_stats(ring_cnt, &stats[ring_cnt]);
> +		printf(", %-18d", ring_cnt);
> +	}
> +	printf("\n");
> +
> +	for (stats_cnt = 0; stats_cnt < SPP_RINGLATENCYSTATS_STATS_SLOT_COUNT; stats_cnt++) {
> +		printf("%3dns", stats_cnt);
> +		for (ring_cnt = 0; ring_cnt < RTE_MAX_ETHPORTS; ring_cnt++) {
> +			if (g_if_info.ring_patchs[ring_cnt].use_flg == 0) {
> +				continue;
> +			}
> +
> +			printf(", 0x%-16lx", stats[ring_cnt].slot[stats_cnt]);
> +		}
> +		printf("\n");
> +	}
> +
> +	return;
> +}
> +#endif /* SPP_RINGLATENCYSTATS_ENABLE */
> +
> +/*
> + * main
> + */
> +int
> +#ifndef USE_UT_SPP_VF
> +main(int argc, char *argv[])
> +#else /* ifndef USE_UT_SPP_VF */
> +ut_main(int argc, char *argv[])
> +#endif  /* ifndef USE_UT_SPP_VF */
> +{
> +	int ret = -1;
> +#ifdef SPP_DEMONIZE
> +	/* Daemonize process */
> +	int ret_daemon = daemon(0, 0);
> +	if (unlikely(ret_daemon != 0)) {
> +		RTE_LOG(ERR, APP, "daemonize is faild. (ret = %d)\n", ret_daemon);
> +		return ret_daemon;
> +	}
> +#endif
> +
> +	/* Signal handler registration (SIGTERM/SIGINT) */
> +	signal(SIGTERM, stop_process);
> +	signal(SIGINT,  stop_process);
> +
> +	unsigned int main_lcore_id = 0xffffffff;
> +	while(1) {
> +		/* Parse parameters */
> +		int ret_parse = parse_app_args(argc, argv);
> +		if (unlikely(ret_parse != 0)) {
> +			break;
> +		}
> +
> +		/* Load config */
> +		int ret_config = spp_config_load_file(0, &g_config);
> +		if (unlikely(ret_config != 0)) {
> +			break;
> +		}
> +
> +		/* DPDK initialize */
> +		int ret_dpdk = rte_eal_init(argc, argv);
> +		if (unlikely(ret_dpdk < 0)) {
> +			break;
> +		}
> +		/* Get core id. */
> +		main_lcore_id = rte_lcore_id();
> +
> +		/* 起動パラメータとコンフィグチェック */
> +		/* 各IF情報設定 */
> +		int ret_manage = init_manage_data(&g_config);
> +		if (unlikely(ret_manage != 0)) {
> +			break;
> +		}
> +
> +		/* 他機能部初期化 */
> +#ifdef SPP_RINGLATENCYSTATS_ENABLE /* RING滞留時間 */
> +		int ret_ringlatency = spp_ringlatencystats_init(
> +				SPP_RING_LATENCY_STATS_SAMPLING_INTERVAL, g_if_info.num_ring);
> +		if (unlikely(ret_ringlatency != 0)) {
> +			break;
> +		}
> +#endif /* SPP_RINGLATENCYSTATS_ENABLE */
> +
> +		/* Start  thread */
> +		unsigned int lcore_id = 0;
> +		RTE_LCORE_FOREACH_SLAVE(lcore_id) {
> +			if (g_core_info[lcore_id].type == SPP_CONFIG_CLASSIFIER_MAC) {
> +				rte_eal_remote_launch(spp_classifier_mac_do,
> +						(void *)&g_core_info[lcore_id],
> +						lcore_id);
> +			} else {
> +				rte_eal_remote_launch(spp_forward,
> +						(void *)&g_core_info[lcore_id],
> +						lcore_id);
> +			}
> +		}
> +
> +		/* スレッド状態確認 */
> +		g_core_info[main_lcore_id].status = SPP_CORE_IDLE;
> +		int ret_wait = check_core_status_wait(SPP_CORE_IDLE);
> +		if (unlikely(ret_wait != 0)) {
> +			break;
> +		}
> +
> +		/* Start forward */
> +		set_core_status(SPP_CORE_FORWARD);
> +		RTE_LOG(INFO, APP, "My ID %d start handling message\n", 0);
> +		RTE_LOG(INFO, APP, "[Press Ctrl-C to quit ...]\n");
> +
> +		/* loop */
> +#ifndef USE_UT_SPP_VF
> +		while(likely(g_core_info[main_lcore_id].status != SPP_CORE_STOP_REQUEST)) {
> +#else
> +		{
> +#endif
> +			sleep(1);
> +
> +#ifdef SPP_RINGLATENCYSTATS_ENABLE /* RING滞留時間 */
> +			print_ring_latency_stats();
> +#endif /* SPP_RINGLATENCYSTATS_ENABLE */
> +		}
> +
> +		/* 正常終了 */
> +		ret = 0;
> +		break;
> +	}
> +
> +	/* exit */
> +	stop_process(SIGINT);
> +	if (main_lcore_id == rte_lcore_id())
> +	{
> +		g_core_info[main_lcore_id].status = SPP_CORE_STOP;
> +		int ret_core_end = check_core_status_wait(SPP_CORE_STOP);
> +		if (unlikely(ret_core_end != 0)) {
> +			RTE_LOG(ERR, APP, "Core did not stop.\n");
> +		}
> +	}
> +
> +	/* 他機能部終了処理 */
> +#ifdef SPP_RINGLATENCYSTATS_ENABLE /* RING滞留時間 */
> +	spp_ringlatencystats_uninit();
> +#endif /* SPP_RINGLATENCYSTATS_ENABLE */
> +	RTE_LOG(INFO, APP, "spp_vf exit.\n");
> +	return ret;
> +}
> diff --git a/src/vf/spp_vf.h b/src/vf/spp_vf.h
> new file mode 100644
> index 0000000..feb59b6
> --- /dev/null
> +++ b/src/vf/spp_vf.h
> @@ -0,0 +1,40 @@
> +#ifndef __SPP_VF_H__
> +#define __SPP_VF_H__
> +
> +#include "common.h"
> +#include "spp_config.h"
> +
> +/*
> + * State on core
> + */
> +enum spp_core_status {
> +	SPP_CORE_STOP,
> +	SPP_CORE_IDLE,
> +	SPP_CORE_FORWARD,
> +	SPP_CORE_STOP_REQUEST
> +};
> +
> +/*
> + * Port info on core
> + */
> +struct spp_core_port_info {
> +	enum		port_type if_type;
> +	int		if_no;
> +	int		dpdk_port;
> +	uint64_t	mac_addr;
> +	char		mac_addr_str[SPP_CONFIG_STR_LEN];
> +};
> +
> +/*
> + * Core info
> + */
> +struct spp_core_info {
> +	enum	spp_core_status status;
> +	enum	spp_core_type type;
> +	int	num_rx_port;
> +	int	num_tx_port;
> +	struct spp_core_port_info rx_ports[RTE_MAX_ETHPORTS];
> +	struct spp_core_port_info tx_ports[RTE_MAX_ETHPORTS];
> +};
> +
> +#endif /* __SPP_VF_H__ */
> 


-- 
Yasufumi Ogawa
NTT Network Service Systems Labs



More information about the spp mailing list