mirror of https://github.com/F-Stack/f-stack.git
344 lines
8.2 KiB
C
344 lines
8.2 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(C) 2023 Marvell.
|
|
*/
|
|
|
|
#include "roc_api.h"
|
|
#include "roc_priv.h"
|
|
|
|
int
|
|
npc_aged_flows_bitmap_alloc(struct roc_npc *roc_npc)
|
|
{
|
|
struct roc_npc_flow_age *flow_age;
|
|
uint8_t *age_mem = NULL;
|
|
uint32_t bmap_sz;
|
|
int rc = 0;
|
|
|
|
bmap_sz = plt_bitmap_get_memory_footprint(MCAM_ARR_ELEM_SZ *
|
|
MCAM_ARR_SIZE);
|
|
age_mem = plt_zmalloc(bmap_sz, 0);
|
|
if (age_mem == NULL) {
|
|
plt_err("Bmap alloc failed");
|
|
rc = NPC_ERR_NO_MEM;
|
|
goto done;
|
|
}
|
|
|
|
flow_age = &roc_npc->flow_age;
|
|
flow_age->age_mem = age_mem;
|
|
flow_age->aged_flows = plt_bitmap_init(MCAM_ARR_ELEM_SZ * MCAM_ARR_SIZE,
|
|
age_mem, bmap_sz);
|
|
if (!flow_age->aged_flows) {
|
|
plt_err("Bitmap init failed");
|
|
plt_free(age_mem);
|
|
rc = NPC_ERR_NO_MEM;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
return rc;
|
|
}
|
|
|
|
void
|
|
npc_aged_flows_bitmap_free(struct roc_npc *roc_npc)
|
|
{
|
|
struct roc_npc_flow_age *flow_age;
|
|
|
|
flow_age = &roc_npc->flow_age;
|
|
plt_bitmap_free(flow_age->aged_flows);
|
|
if (flow_age->age_mem)
|
|
plt_free(roc_npc->flow_age.age_mem);
|
|
}
|
|
|
|
static void
|
|
check_timeout_cycles(struct roc_npc *roc_npc, uint32_t mcam_id)
|
|
{
|
|
struct npc *npc = roc_npc_to_npc_priv(roc_npc);
|
|
struct npc_age_flow_list_head *list;
|
|
struct npc_age_flow_entry *fl_iter;
|
|
struct roc_npc_flow_age *flow_age;
|
|
|
|
flow_age = &roc_npc->flow_age;
|
|
list = &npc->age_flow_list;
|
|
TAILQ_FOREACH(fl_iter, list, next) {
|
|
if (fl_iter->flow->mcam_id == mcam_id &&
|
|
fl_iter->flow->timeout_cycles < plt_tsc_cycles()) {
|
|
/* update bitmap */
|
|
plt_bitmap_set(flow_age->aged_flows, mcam_id);
|
|
if (flow_age->aged_flows_cnt == 0) {
|
|
flow_age->start_id = mcam_id;
|
|
flow_age->end_id = mcam_id;
|
|
}
|
|
if (flow_age->start_id > mcam_id)
|
|
flow_age->start_id = mcam_id;
|
|
else if (flow_age->end_id < mcam_id)
|
|
flow_age->end_id = mcam_id;
|
|
flow_age->aged_flows_cnt += 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_timeout_cycles(struct roc_npc *roc_npc, uint32_t mcam_id)
|
|
{
|
|
struct npc *npc = roc_npc_to_npc_priv(roc_npc);
|
|
struct npc_age_flow_list_head *list;
|
|
struct npc_age_flow_entry *fl_iter;
|
|
|
|
list = &npc->age_flow_list;
|
|
TAILQ_FOREACH(fl_iter, list, next) {
|
|
if (fl_iter->flow->mcam_id == mcam_id) {
|
|
fl_iter->flow->timeout_cycles = plt_tsc_cycles() +
|
|
fl_iter->flow->timeout * plt_tsc_hz();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
npc_mcam_get_hit_status(struct npc *npc, uint64_t *mcam_ids, uint16_t start_id,
|
|
uint16_t end_id, uint64_t *hit_status, bool clear)
|
|
{
|
|
struct npc_mcam_get_hit_status_req *req;
|
|
struct npc_mcam_get_hit_status_rsp *rsp;
|
|
struct mbox *mbox = mbox_get(npc->mbox);
|
|
uint8_t idx_start;
|
|
uint8_t idx_end;
|
|
int rc;
|
|
int i;
|
|
|
|
req = mbox_alloc_msg_npc_mcam_get_hit_status(mbox);
|
|
if (req == NULL)
|
|
return -ENOSPC;
|
|
|
|
idx_start = start_id / MCAM_ARR_ELEM_SZ;
|
|
idx_end = end_id / MCAM_ARR_ELEM_SZ;
|
|
|
|
for (i = idx_start; i <= idx_end; i++)
|
|
req->mcam_ids[i] = mcam_ids[i];
|
|
|
|
req->range_valid_mcam_ids_start = start_id;
|
|
req->range_valid_mcam_ids_end = end_id;
|
|
req->clear = clear;
|
|
|
|
rc = mbox_process_msg(mbox, (void *)&rsp);
|
|
if (rc)
|
|
goto exit;
|
|
|
|
for (i = idx_start; i <= idx_end; i++)
|
|
hit_status[i] = rsp->mcam_hit_status[i];
|
|
|
|
rc = 0;
|
|
exit:
|
|
mbox_put(mbox);
|
|
return rc;
|
|
}
|
|
|
|
static void
|
|
npc_age_wait_until(struct roc_npc_flow_age *flow_age)
|
|
{
|
|
#define NPC_AGE_WAIT_TIMEOUT_MS 1000
|
|
#define NPC_AGE_WAIT_TIMEOUT_US (NPC_AGE_WAIT_TIMEOUT_MS * NPC_AGE_WAIT_TIMEOUT_MS)
|
|
uint64_t timeout = 0;
|
|
uint64_t sleep = 10 * NPC_AGE_WAIT_TIMEOUT_MS;
|
|
|
|
do {
|
|
plt_delay_us(sleep);
|
|
timeout += sleep;
|
|
} while (!flow_age->aged_flows_get_thread_exit &&
|
|
(timeout < ((uint64_t)flow_age->aging_poll_freq * NPC_AGE_WAIT_TIMEOUT_US)));
|
|
}
|
|
|
|
uint32_t
|
|
npc_aged_flows_get(void *args)
|
|
{
|
|
uint64_t hit_status[MCAM_ARR_SIZE] = {0};
|
|
uint64_t mcam_ids[MCAM_ARR_SIZE] = {0};
|
|
struct npc_age_flow_list_head *list;
|
|
struct npc_age_flow_entry *fl_iter;
|
|
struct roc_npc *roc_npc = args;
|
|
struct npc *npc = roc_npc_to_npc_priv(roc_npc);
|
|
struct roc_npc_flow_age *flow_age;
|
|
bool aging_enabled;
|
|
uint32_t start_id;
|
|
uint32_t end_id;
|
|
uint32_t mcam_id;
|
|
uint32_t idx;
|
|
uint32_t i;
|
|
int rc;
|
|
|
|
flow_age = &roc_npc->flow_age;
|
|
list = &npc->age_flow_list;
|
|
while (!flow_age->aged_flows_get_thread_exit) {
|
|
start_id = 0;
|
|
end_id = 0;
|
|
aging_enabled = false;
|
|
memset(mcam_ids, 0, sizeof(mcam_ids));
|
|
TAILQ_FOREACH(fl_iter, list, next) {
|
|
mcam_id = fl_iter->flow->mcam_id;
|
|
idx = mcam_id / MCAM_ARR_ELEM_SZ;
|
|
mcam_ids[idx] |= BIT_ULL(mcam_id % MCAM_ARR_ELEM_SZ);
|
|
|
|
if (!aging_enabled) {
|
|
start_id = mcam_id;
|
|
end_id = mcam_id;
|
|
aging_enabled = true;
|
|
}
|
|
|
|
if (mcam_id < start_id)
|
|
start_id = mcam_id;
|
|
else if (mcam_id > end_id)
|
|
end_id = mcam_id;
|
|
}
|
|
|
|
if (!aging_enabled)
|
|
goto lbl_sleep;
|
|
|
|
rc = npc_mcam_get_hit_status(npc, mcam_ids, start_id, end_id,
|
|
hit_status, true);
|
|
if (rc)
|
|
return 0;
|
|
|
|
plt_seqcount_write_begin(&flow_age->seq_cnt);
|
|
flow_age->aged_flows_cnt = 0;
|
|
for (i = start_id; i <= end_id; i++) {
|
|
idx = i / MCAM_ARR_ELEM_SZ;
|
|
if (mcam_ids[idx] & BIT_ULL(i % MCAM_ARR_ELEM_SZ)) {
|
|
if (!(hit_status[idx] & BIT_ULL(i % MCAM_ARR_ELEM_SZ)))
|
|
check_timeout_cycles(roc_npc, i);
|
|
else
|
|
update_timeout_cycles(roc_npc, i);
|
|
}
|
|
}
|
|
plt_seqcount_write_end(&flow_age->seq_cnt);
|
|
|
|
lbl_sleep:
|
|
npc_age_wait_until(flow_age);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
npc_age_flow_list_entry_add(struct roc_npc *roc_npc, struct roc_npc_flow *flow)
|
|
{
|
|
struct npc *npc = roc_npc_to_npc_priv(roc_npc);
|
|
struct npc_age_flow_entry *age_fl_iter;
|
|
struct npc_age_flow_entry *new_entry;
|
|
|
|
new_entry = plt_zmalloc(sizeof(*new_entry), 0);
|
|
if (new_entry == NULL) {
|
|
plt_err("flow entry alloc failed");
|
|
return;
|
|
}
|
|
|
|
new_entry->flow = flow;
|
|
roc_npc->flow_age.age_flow_refcnt++;
|
|
/* List in ascending order of mcam entries */
|
|
TAILQ_FOREACH(age_fl_iter, &npc->age_flow_list, next) {
|
|
if (age_fl_iter->flow->mcam_id > flow->mcam_id) {
|
|
TAILQ_INSERT_BEFORE(age_fl_iter, new_entry, next);
|
|
return;
|
|
}
|
|
}
|
|
TAILQ_INSERT_TAIL(&npc->age_flow_list, new_entry, next);
|
|
}
|
|
|
|
void
|
|
npc_age_flow_list_entry_delete(struct roc_npc *roc_npc,
|
|
struct roc_npc_flow *flow)
|
|
{
|
|
struct npc *npc = roc_npc_to_npc_priv(roc_npc);
|
|
struct npc_age_flow_list_head *list;
|
|
struct roc_npc_flow_age *flow_age;
|
|
struct npc_age_flow_entry *curr;
|
|
|
|
flow_age = &roc_npc->flow_age;
|
|
|
|
list = &npc->age_flow_list;
|
|
curr = TAILQ_FIRST(list);
|
|
|
|
if (!curr)
|
|
return;
|
|
|
|
while (curr) {
|
|
if (flow->mcam_id == curr->flow->mcam_id) {
|
|
plt_bitmap_clear(flow_age->aged_flows, flow->mcam_id);
|
|
TAILQ_REMOVE(list, curr, next);
|
|
plt_free(curr);
|
|
break;
|
|
}
|
|
curr = TAILQ_NEXT(curr, next);
|
|
}
|
|
roc_npc->flow_age.age_flow_refcnt--;
|
|
}
|
|
|
|
int
|
|
npc_aging_ctrl_thread_create(struct roc_npc *roc_npc,
|
|
const struct roc_npc_action_age *age,
|
|
struct roc_npc_flow *flow)
|
|
{
|
|
struct roc_npc_flow_age *flow_age;
|
|
int errcode = 0;
|
|
|
|
flow_age = &roc_npc->flow_age;
|
|
if (age->timeout < flow_age->aging_poll_freq) {
|
|
plt_err("Age timeout should be greater or equal to %u seconds",
|
|
flow_age->aging_poll_freq);
|
|
errcode = NPC_ERR_ACTION_NOTSUP;
|
|
goto done;
|
|
}
|
|
|
|
flow->age_context = age->context == NULL ? flow : age->context;
|
|
flow->timeout = age->timeout;
|
|
flow->timeout_cycles = plt_tsc_cycles() + age->timeout * plt_tsc_hz();
|
|
flow->has_age_action = true;
|
|
|
|
if (flow_age->age_flow_refcnt == 0) {
|
|
errcode = npc_aged_flows_bitmap_alloc(roc_npc);
|
|
if (errcode != 0)
|
|
goto done;
|
|
|
|
flow_age->aged_flows_get_thread_exit = false;
|
|
if (plt_thread_create_control(&flow_age->aged_flows_poll_thread,
|
|
"Aged Flows Get Ctrl Thread",
|
|
npc_aged_flows_get, roc_npc) != 0) {
|
|
plt_err("Failed to create thread for age flows");
|
|
npc_aged_flows_bitmap_free(roc_npc);
|
|
errcode = NPC_ERR_ACTION_NOTSUP;
|
|
goto done;
|
|
}
|
|
}
|
|
done:
|
|
return errcode;
|
|
}
|
|
|
|
void
|
|
npc_aging_ctrl_thread_destroy(struct roc_npc *roc_npc)
|
|
{
|
|
struct roc_npc_flow_age *flow_age;
|
|
|
|
flow_age = &roc_npc->flow_age;
|
|
if (plt_thread_is_valid(flow_age->aged_flows_poll_thread)) {
|
|
flow_age->aged_flows_get_thread_exit = true;
|
|
plt_thread_join(flow_age->aged_flows_poll_thread, NULL);
|
|
npc_aged_flows_bitmap_free(roc_npc);
|
|
}
|
|
}
|
|
|
|
void *
|
|
roc_npc_aged_flow_ctx_get(struct roc_npc *roc_npc, uint32_t mcam_id)
|
|
{
|
|
struct npc *npc = roc_npc_to_npc_priv(roc_npc);
|
|
struct npc_age_flow_list_head *list;
|
|
struct npc_age_flow_entry *fl_iter;
|
|
|
|
list = &npc->age_flow_list;
|
|
|
|
TAILQ_FOREACH(fl_iter, list, next) {
|
|
if (fl_iter->flow->mcam_id == mcam_id)
|
|
return fl_iter->flow->age_context;
|
|
}
|
|
|
|
return NULL;
|
|
}
|