/*

 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.

 *

 * This program is free software; you can redistribute it and/or modify

 * it under the terms of the GNU General Public License version 2 and

 * only version 2 as published by the Free Software Foundation.

 *

 * This program is distributed in the hope that it will be useful,

 * but WITHOUT ANY WARRANTY; without even the implied warranty of

 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

 * GNU General Public License for more details.

 */

#ifndef QUEUE_UT

#include "ka_hashtable_pub.h"

#include "queue_module.h"

#include "queue_fops.h"

#include "queue_status_record.h"

#include "queue_context.h"



#define CONTEXT_HASH_TABLE_BIT 10

#define CONTEXT_HASH_TABLE_MASK ((0x1 << CONTEXT_HASH_TABLE_BIT) - 1)



static KA_TASK_DEFINE_SPINLOCK(context_spinlock);

static KA_DEFINE_HASHTABLE(context_table, CONTEXT_HASH_TABLE_BIT);



static void queue_context_add(struct queue_context *context)

{

    u32 key = (u32)context->pid & CONTEXT_HASH_TABLE_MASK;



    ka_hash_add(context_table, &context->link, key);

}



static void queue_context_del(struct queue_context *context)

{

    ka_hash_del(&context->link);

}



STATIC struct queue_context *queue_context_find(ka_pid_t pid)

{

    struct queue_context *context = NULL;

    u32 key = (u32)pid & CONTEXT_HASH_TABLE_MASK;



    ka_hash_for_each_possible(context_table, context, link, key) {

        if (context->pid == pid) {

            return context;

        }

    }



    return NULL;

}



struct queue_context *queue_context_get(ka_pid_t pid)

{

    struct queue_context *ctx = NULL;



    ka_task_spin_lock_bh(&context_spinlock);

    ctx = queue_context_find(pid);

    if (ctx != NULL) {

        ka_base_atomic_inc(&ctx->refcnt);

    }

    ka_task_spin_unlock_bh(&context_spinlock);



    return ctx;

}



void queue_context_put(struct queue_context *ctx)

{

    ka_task_spin_lock_bh(&context_spinlock);

    if (!ka_base_atomic_dec_and_test(&ctx->refcnt)) {

        ka_task_spin_unlock_bh(&context_spinlock);

        return;

    }

    queue_context_del(ctx);

    ka_task_spin_unlock_bh(&context_spinlock);



    queue_context_private_data_destroy(ctx->private_data);

    queue_free_ctx_all_qid_status(ctx);

    ctx->private_data = NULL;

    queue_drv_kfree(ctx);



    return;

}



struct queue_context *queue_context_init(ka_pid_t pid)

{

    struct queue_context *ctx = NULL;

    void *private_data = NULL;



    private_data = queue_context_private_data_create();

    if (private_data == NULL) {

        queue_err("Private data create failed. (tgid=%d)\n", pid);

        return NULL;

    }



    ka_task_spin_lock_bh(&context_spinlock);

    ctx = queue_context_find(pid);

    if (ctx != NULL) {

        ka_task_spin_unlock_bh(&context_spinlock);

        queue_context_private_data_destroy(private_data);

        queue_err("Queue context had existed. (tgid=%d)\n", pid);

        return NULL;

    }

    ctx = (struct queue_context *)queue_drv_kzalloc(sizeof(struct queue_context), KA_GFP_ATOMIC | __KA_GFP_ACCOUNT);

    if (ctx == NULL) {

        ka_task_spin_unlock_bh(&context_spinlock);

        queue_context_private_data_destroy(private_data);

        queue_err("Context kmalloc failed. (tgid=%d)\n", pid);

        return NULL;

    }



    ctx->private_data = private_data;

    ka_task_spin_lock_init(&ctx->qid_status_lock);



    ka_base_atomic_set(&ctx->refcnt, 1);

    ctx->pid = ka_task_get_current()->tgid;

    queue_context_add(ctx);



    ka_task_spin_unlock_bh(&context_spinlock);



    return ctx;

}



void queue_context_uninit(struct queue_context *ctx)

{

    queue_context_put(ctx);

}

#else

void queue_context_common(void)

{

    return;

}

#endif