/*
* Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved.
* gazelle is licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*     http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v2 for more details.
*/

#include <malloc.h>
#include <sys/time.h>
#include <pthread.h>

#include <rte_malloc.h>
#include <rte_errno.h>
#include <rte_cycles.h>
#include <lwip/lwipgz_hlist.h>

#include "ltran_param.h"
#include "ltran_log.h"
#include "ltran_tcp_sock.h"
#include "ltran_tcp_conn.h"
#include "ltran_instance.h"
#include "ltran_timer.h"

uint64_t gazelle_now_us(void)
{
    static uint64_t g_cycles_per_us = 0;
    if (unlikely(g_cycles_per_us == 0)) {
        g_cycles_per_us = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S;
    }

    return (rte_rdtsc() / g_cycles_per_us);
}

void gazelle_detect_sock_logout(struct gazelle_tcp_sock_htable *tcp_sock_htable)
{
    uint32_t i;
    struct gazelle_tcp_sock *tcp_sock = NULL;
    struct hlist_node *node = NULL;

    if (tcp_sock_htable == NULL) {
        return;
    }

    if (pthread_mutex_trylock(&tcp_sock_htable->mlock) != 0) {
        LTRAN_ERR("lock failed, errno %d.\n", errno);
        return;
    }

    for (i = 0; i < GAZELLE_MAX_TCP_SOCK_HTABLE_SIZE; i++) {
        node = tcp_sock_htable->array[i].chain.first;
        while (node != NULL) {
            tcp_sock = hlist_entry(node, typeof(*tcp_sock), tcp_sock_node);
            node = node->next;
            if (!INSTANCE_IS_ON(tcp_sock)) {
                hlist_del_node(&tcp_sock->tcp_sock_node);
                tcp_sock_htable->cur_tcp_sock_num--;
                tcp_sock_htable->array[i].chain_size--;
                LTRAN_DEBUG("delete the tcp sock htable: tid %u ip %u port %u\n",
                    tcp_sock->tid, tcp_sock->ip, (uint32_t)ntohs(tcp_sock->port));
                free(tcp_sock);
            }
        }
    }

    if (pthread_mutex_unlock(&tcp_sock_htable->mlock) != 0) {
        LTRAN_WARN("read tcp_sock_htable: unlock failed, errno %d.\n", errno);
    }
}
void gazelle_detect_conn_logout(struct gazelle_tcp_conn_htable *conn_htable)
{
    struct gazelle_tcp_sock_htable *sock_htable = gazelle_get_tcp_sock_htable();
    struct gazelle_tcp_conn *conn = NULL;
    struct hlist_node *node = NULL;
    uint32_t i;

    if (conn_htable == NULL) {
        return;
    }

    if (pthread_mutex_trylock(&sock_htable->mlock) != 0) {
        LTRAN_ERR("lock failed, errno %d\n", errno);
        return;
    }

    for (i = 0; i < GAZELLE_MAX_CONN_HTABLE_SIZE; i++) {
        node = conn_htable->array[i].chain.first;
        while (node != NULL) {
            conn = hlist_entry(node, typeof(*conn), conn_node);
            node = node->next;
            if (!INSTANCE_IS_ON(conn)) {
                hlist_del_node(&conn->conn_node);
                conn_htable->cur_conn_num--;
                conn_htable->array[i].chain_size--;
                LTRAN_DEBUG("delete the tcp conn htable: tid %u quintuple[%u %u %u %u %u]\n",
                    conn->tid, conn->quintuple.protocol,
                    conn->quintuple.src_ip.u_addr.ip4.addr, (uint32_t)ntohs(conn->quintuple.src_port),
                    conn->quintuple.dst_ip.u_addr.ip4.addr, (uint32_t)ntohs(conn->quintuple.dst_port));
                rte_free(conn);
            }
        }
    }

    if (pthread_mutex_unlock(&sock_htable->mlock) != 0) {
        LTRAN_WARN("unlock failed, errno %d.\n", errno);
    }
}

void gazelle_delete_aging_conn(struct gazelle_tcp_conn_htable *conn_htable)
{
    struct gazelle_tcp_sock_htable *sock_htable = gazelle_get_tcp_sock_htable();
    struct gazelle_tcp_conn *conn = NULL;
    struct hlist_node *node = NULL;
    uint32_t i;

    if (conn_htable == NULL) {
        return;
    }

    if (pthread_mutex_trylock(&sock_htable->mlock) != 0) {
        LTRAN_ERR("lock failed, errno %d\n", errno);
        return;
    }

    for (i = 0; i < GAZELLE_MAX_CONN_HTABLE_SIZE; i++) {
        node = conn_htable->array[i].chain.first;
        while (node != NULL) {
            conn = hlist_entry(node, typeof(*conn), conn_node);
            node = node->next;
            if (conn->conn_timeout < 0) {
                continue;
            }

            conn->conn_timeout--;
            if (conn->conn_timeout > 0) {
                continue;
            }

            hlist_del_node(&conn->conn_node);
            conn_htable->cur_conn_num--;
            conn_htable->array[i].chain_size--;
            if (conn->sock) {
                conn->sock->tcp_con_num--;
            }
            rte_free(conn);
        }
    }

    if (pthread_mutex_unlock(&sock_htable->mlock) != 0) {
        LTRAN_WARN("unlock failed, errno %d.\n", errno);
    }
}