/*
// Copyright (c) 2010-2017 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

#ifndef _LCONF_H_
#define _LCONF_H_

#include "task_init.h"
#include "stats.h"

enum lconf_msg_type {
	LCONF_MSG_STOP,
	LCONF_MSG_START,
	LCONF_MSG_DUMP,
	LCONF_MSG_TRACE,
	LCONF_MSG_DUMP_RX,
	LCONF_MSG_DUMP_TX,
	LCONF_MSG_RX_DISTR_START,
	LCONF_MSG_RX_DISTR_STOP,
	LCONF_MSG_RX_DISTR_RESET,
	LCONF_MSG_TX_DISTR_START,
	LCONF_MSG_TX_DISTR_STOP,
	LCONF_MSG_TX_DISTR_RESET,
	LCONF_MSG_RX_BW_START,
	LCONF_MSG_RX_BW_STOP,
	LCONF_MSG_TX_BW_START,
	LCONF_MSG_TX_BW_STOP,
};

struct lconf_msg {
	/* Set by master core (if not set), unset by worker after consumption. */
	uint32_t            req;
	enum lconf_msg_type type;
	int                 task_id;
	int                 val;
};

#define LCONF_FLAG_RX_DISTR_ACTIVE 0x00000001
#define LCONF_FLAG_RUNNING         0x00000002
#define LCONF_FLAG_TX_DISTR_ACTIVE 0x00000004
#define LCONF_FLAG_RX_BW_ACTIVE    0x00000008
#define LCONF_FLAG_TX_BW_ACTIVE    0x00000010

struct lcore_cfg {
	/* All tasks running at the moment. This is empty when the core is stopped. */
	struct task_base	*tasks_run[MAX_TASKS_PER_CORE];
	uint8_t			n_tasks_run;

	void (*flush_queues[MAX_TASKS_PER_CORE])(struct task_base *tbase);

	void (*period_func)(void *data);
	void                    *period_data;
	/* call periodic_func after periodic_timeout cycles */
	uint64_t                period_timeout;

	uint64_t                ctrl_timeout;
	void (*ctrl_func_m[MAX_TASKS_PER_CORE])(struct task_base *tbase, void **data, uint16_t n_msgs);
	struct rte_ring         *ctrl_rings_m[MAX_TASKS_PER_CORE];

	void (*ctrl_func_p[MAX_TASKS_PER_CORE])(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts);
	struct rte_ring         *ctrl_rings_p[MAX_TASKS_PER_CORE];

	struct lconf_msg        msg __attribute__((aligned(4)));
	struct task_base	*tasks_all[MAX_TASKS_PER_CORE];
	int                     task_is_running[MAX_TASKS_PER_CORE];
	uint8_t			n_tasks_all;
	pthread_t		thread_id;

	/* Following variables are not accessed in main loop */
	uint32_t		flags;
	uint8_t			active_task;
	uint8_t			id;
	char			name[MAX_NAME_SIZE];
	struct task_args        targs[MAX_TASKS_PER_CORE];
	int (*thread_x)(struct lcore_cfg *lconf);
	uint32_t		cache_set;
} __rte_cache_aligned;

extern struct lcore_cfg     *lcore_cfg;
extern struct lcore_cfg      lcore_cfg_init[];

/* This function is only run on low load (when no bulk was sent within
   last drain_timeout (16kpps if DRAIN_TIMEOUT = 2 ms) */
static inline void lconf_flush_all_queues(struct lcore_cfg *lconf)
{
	struct task_base *task;

	for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) {
		task = lconf->tasks_all[task_id];
		if (!(task->flags & FLAG_TX_FLUSH) || (task->flags & FLAG_NEVER_FLUSH)) {
			task->flags |= FLAG_TX_FLUSH;
			continue;
		}
		lconf->flush_queues[task_id](task);
	}
}

static inline void lconf_set_req(struct lcore_cfg *lconf)
{
	(*(volatile uint32_t *)&lconf->msg.req) = 1;
}

static inline void lconf_unset_req(struct lcore_cfg *lconf)
{
	(*(volatile uint32_t *)&lconf->msg.req) = 0;
}

static inline int lconf_is_req(struct lcore_cfg *lconf)
{
	return (*(volatile uint32_t *)&lconf->msg.req);
}

/* Returns non-zero when terminate has been requested */
int lconf_do_flags(struct lcore_cfg *lconf);

int lconf_get_task_id(const struct lcore_cfg *lconf, const struct task_base *task);
int lconf_task_is_running(const struct lcore_cfg *lconf, uint8_t task_id);

int lconf_run(void *dummy);

void lcore_cfg_alloc_hp(void);

/* Returns the next active lconf/targ pair. If *lconf = NULL, the
   first active lconf/targ pair is returned. If the last lconf/targ
   pair is passed, the function returns non-zero. */
int core_targ_next(struct lcore_cfg **lconf, struct task_args **targ, const int with_master);
/* Same as above, but uses non-huge page memory (used before
   lcore_cfg_alloc_hp is called). */
int core_targ_next_early(struct lcore_cfg **lconf, struct task_args **targ, const int with_master);

struct task_args *core_targ_get(uint32_t lcore_id, uint32_t task_id);

#endif /* _LCONF_H_ */