root/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Support for Intel Camera Imaging ISP subsystem.
 * Copyright (c) 2015, Intel Corporation.
 */

#include "hmm.h"

#include "ia_css_refcount.h"
#include "sh_css_defs.h"

#include "platform_support.h"

#include "assert_support.h"

#include "ia_css_debug.h"

/* TODO: enable for other memory aswell
         now only for ia_css_ptr */
struct ia_css_refcount_entry {
        u32 count;
        ia_css_ptr data;
        s32 id;
};

struct ia_css_refcount_list {
        u32 size;
        struct ia_css_refcount_entry *items;
};

static struct ia_css_refcount_list myrefcount;

static struct ia_css_refcount_entry *refcount_find_entry(ia_css_ptr ptr,
        bool firstfree)
{
        u32 i;

        if (ptr == 0)
                return NULL;
        if (!myrefcount.items) {
                ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR,
                                    "%s(): Ref count not initialized!\n", __func__);
                return NULL;
        }

        for (i = 0; i < myrefcount.size; i++) {
                if ((&myrefcount.items[i])->data == 0) {
                        if (firstfree) {
                                /* for new entry */
                                return &myrefcount.items[i];
                        }
                }
                if ((&myrefcount.items[i])->data == ptr) {
                        /* found entry */
                        return &myrefcount.items[i];
                }
        }
        return NULL;
}

int ia_css_refcount_init(uint32_t size)
{
        int err = 0;

        if (size == 0) {
                ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
                                    "%s(): Size of 0 for Ref count init!\n", __func__);
                return -EINVAL;
        }
        if (myrefcount.items) {
                ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
                                    "%s(): Ref count is already initialized\n", __func__);
                return -EINVAL;
        }
        myrefcount.items =
            kvmalloc(sizeof(struct ia_css_refcount_entry) * size, GFP_KERNEL);
        if (!myrefcount.items)
                err = -ENOMEM;
        if (!err) {
                memset(myrefcount.items, 0,
                       sizeof(struct ia_css_refcount_entry) * size);
                myrefcount.size = size;
        }
        return err;
}

void ia_css_refcount_uninit(void)
{
        struct ia_css_refcount_entry *entry;
        u32 i;

        ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
                            "%s() entry\n", __func__);
        for (i = 0; i < myrefcount.size; i++) {
                /* driver verifier tool has issues with &arr[i]
                   and prefers arr + i; as these are actually equivalent
                   the line below uses + i
                */
                entry = myrefcount.items + i;
                if (entry->data != mmgr_NULL) {
                        /*      ia_css_debug_dtrace(IA_CSS_DBG_TRACE,
                                "ia_css_refcount_uninit: freeing (%x)\n",
                                entry->data);*/
                        hmm_free(entry->data);
                        entry->data = mmgr_NULL;
                        entry->count = 0;
                        entry->id = 0;
                }
        }
        kvfree(myrefcount.items);
        myrefcount.items = NULL;
        myrefcount.size = 0;
        ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
                            "%s() leave\n", __func__);
}

ia_css_ptr ia_css_refcount_increment(s32 id, ia_css_ptr ptr)
{
        struct ia_css_refcount_entry *entry;

        if (ptr == mmgr_NULL)
                return ptr;

        entry = refcount_find_entry(ptr, false);

        ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
                            "%s(%x) 0x%x\n", __func__, id, ptr);

        if (!entry) {
                entry = refcount_find_entry(ptr, true);
                assert(entry);
                if (!entry)
                        return mmgr_NULL;
                entry->id = id;
        }

        if (entry->id != id) {
                ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR,
                                    "%s(): Ref count IDS do not match!\n", __func__);
                return mmgr_NULL;
        }

        if (entry->data == ptr)
                entry->count += 1;
        else if (entry->data == mmgr_NULL) {
                entry->data = ptr;
                entry->count = 1;
        } else
                return mmgr_NULL;

        return ptr;
}

bool ia_css_refcount_decrement(s32 id, ia_css_ptr ptr)
{
        struct ia_css_refcount_entry *entry;

        ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
                            "%s(%x) 0x%x\n", __func__, id, ptr);

        if (ptr == mmgr_NULL)
                return false;

        entry = refcount_find_entry(ptr, false);

        if (entry) {
                if (entry->id != id) {
                        ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR,
                                            "%s(): Ref count IDS do not match!\n", __func__);
                        return false;
                }
                if (entry->count > 0) {
                        entry->count -= 1;
                        if (entry->count == 0) {
                                /* ia_css_debug_dtrace(IA_CSS_DBEUG_TRACE,
                                   "ia_css_refcount_decrement: freeing\n");*/
                                hmm_free(ptr);
                                entry->data = mmgr_NULL;
                                entry->id = 0;
                        }
                        return true;
                }
        }

        /* SHOULD NOT HAPPEN: ptr not managed by refcount, or not
           valid anymore */
        if (entry)
                IA_CSS_ERROR("id %x, ptr 0x%x entry %p entry->id %x entry->count %d\n",
                             id, ptr, entry, entry->id, entry->count);
        else
                IA_CSS_ERROR("entry NULL\n");
        assert(false);

        return false;
}

bool ia_css_refcount_is_single(ia_css_ptr ptr)
{
        struct ia_css_refcount_entry *entry;

        if (ptr == mmgr_NULL)
                return false;

        entry = refcount_find_entry(ptr, false);

        if (entry)
                return (entry->count == 1);

        return true;
}

void ia_css_refcount_clear(s32 id, clear_func clear_func_ptr)
{
        struct ia_css_refcount_entry *entry;
        u32 i;
        u32 count = 0;

        assert(clear_func_ptr);
        ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "%s(%x)\n",
                            __func__, id);

        for (i = 0; i < myrefcount.size; i++) {
                /* driver verifier tool has issues with &arr[i]
                   and prefers arr + i; as these are actually equivalent
                   the line below uses + i
                */
                entry = myrefcount.items + i;
                if ((entry->data != mmgr_NULL) && (entry->id == id)) {
                        ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
                                            "%s: %x: 0x%x\n", __func__,
                                            id, entry->data);
                        if (clear_func_ptr) {
                                /* clear using provided function */
                                clear_func_ptr(entry->data);
                        } else {
                                ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
                                                    "%s: using hmm_free: no clear_func\n", __func__);
                                hmm_free(entry->data);
                        }

                        if (entry->count != 0) {
                                IA_CSS_WARNING("Ref count for entry %x is not zero!", entry->id);
                        }

                        assert(entry->count == 0);

                        entry->data = mmgr_NULL;
                        entry->count = 0;
                        entry->id = 0;
                        count++;
                }
        }
        ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
                            "%s(%x): cleared %d\n", __func__, id,
                            count);
}

bool ia_css_refcount_is_valid(ia_css_ptr ptr)
{
        struct ia_css_refcount_entry *entry;

        if (ptr == mmgr_NULL)
                return false;

        entry = refcount_find_entry(ptr, false);

        return entry;
}