root/drivers/misc/ocxl/pasid.c
// SPDX-License-Identifier: GPL-2.0+
// Copyright 2017 IBM Corp.
#include "ocxl_internal.h"


struct id_range {
        struct list_head list;
        u32 start;
        u32 end;
};

#ifdef DEBUG
static void dump_list(struct list_head *head, char *type_str)
{
        struct id_range *cur;

        pr_debug("%s ranges allocated:\n", type_str);
        list_for_each_entry(cur, head, list) {
                pr_debug("Range %d->%d\n", cur->start, cur->end);
        }
}
#endif

static int range_alloc(struct list_head *head, u32 size, int max_id,
                char *type_str)
{
        struct list_head *pos;
        struct id_range *cur, *new;
        int rc, last_end;

        new = kmalloc_obj(struct id_range);
        if (!new)
                return -ENOMEM;

        pos = head;
        last_end = -1;
        list_for_each_entry(cur, head, list) {
                if ((cur->start - last_end) > size)
                        break;
                last_end = cur->end;
                pos = &cur->list;
        }

        new->start = last_end + 1;
        new->end = new->start + size - 1;

        if (new->end > max_id) {
                kfree(new);
                rc = -ENOSPC;
        } else {
                list_add(&new->list, pos);
                rc = new->start;
        }

#ifdef DEBUG
        dump_list(head, type_str);
#endif
        return rc;
}

static void range_free(struct list_head *head, u32 start, u32 size,
                char *type_str)
{
        bool found = false;
        struct id_range *cur, *tmp;

        list_for_each_entry_safe(cur, tmp, head, list) {
                if (cur->start == start && cur->end == (start + size - 1)) {
                        found = true;
                        list_del(&cur->list);
                        kfree(cur);
                        break;
                }
        }
        WARN_ON(!found);
#ifdef DEBUG
        dump_list(head, type_str);
#endif
}

int ocxl_pasid_afu_alloc(struct ocxl_fn *fn, u32 size)
{
        int max_pasid;

        if (fn->config.max_pasid_log < 0)
                return -ENOSPC;
        max_pasid = 1 << fn->config.max_pasid_log;
        return range_alloc(&fn->pasid_list, size, max_pasid, "afu pasid");
}

void ocxl_pasid_afu_free(struct ocxl_fn *fn, u32 start, u32 size)
{
        return range_free(&fn->pasid_list, start, size, "afu pasid");
}

int ocxl_actag_afu_alloc(struct ocxl_fn *fn, u32 size)
{
        int max_actag;

        max_actag = fn->actag_enabled;
        return range_alloc(&fn->actag_list, size, max_actag, "afu actag");
}

void ocxl_actag_afu_free(struct ocxl_fn *fn, u32 start, u32 size)
{
        return range_free(&fn->actag_list, start, size, "afu actag");
}