forked from Minki/linux
108 lines
2.1 KiB
C
108 lines
2.1 KiB
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(sizeof(struct id_range), GFP_KERNEL);
|
||
|
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");
|
||
|
}
|