1// SPDX-License-Identifier: GPL-2.0+
2// Copyright 2017 IBM Corp.
3#include "ocxl_internal.h"
4
5
6struct id_range {
7	struct list_head list;
8	u32 start;
9	u32 end;
10};
11
12#ifdef DEBUG
13static void dump_list(struct list_head *head, char *type_str)
14{
15	struct id_range *cur;
16
17	pr_debug("%s ranges allocated:\n", type_str);
18	list_for_each_entry(cur, head, list) {
19		pr_debug("Range %d->%d\n", cur->start, cur->end);
20	}
21}
22#endif
23
24static int range_alloc(struct list_head *head, u32 size, int max_id,
25		char *type_str)
26{
27	struct list_head *pos;
28	struct id_range *cur, *new;
29	int rc, last_end;
30
31	new = kmalloc(sizeof(struct id_range), GFP_KERNEL);
32	if (!new)
33		return -ENOMEM;
34
35	pos = head;
36	last_end = -1;
37	list_for_each_entry(cur, head, list) {
38		if ((cur->start - last_end) > size)
39			break;
40		last_end = cur->end;
41		pos = &cur->list;
42	}
43
44	new->start = last_end + 1;
45	new->end = new->start + size - 1;
46
47	if (new->end > max_id) {
48		kfree(new);
49		rc = -ENOSPC;
50	} else {
51		list_add(&new->list, pos);
52		rc = new->start;
53	}
54
55#ifdef DEBUG
56	dump_list(head, type_str);
57#endif
58	return rc;
59}
60
61static void range_free(struct list_head *head, u32 start, u32 size,
62		char *type_str)
63{
64	bool found = false;
65	struct id_range *cur, *tmp;
66
67	list_for_each_entry_safe(cur, tmp, head, list) {
68		if (cur->start == start && cur->end == (start + size - 1)) {
69			found = true;
70			list_del(&cur->list);
71			kfree(cur);
72			break;
73		}
74	}
75	WARN_ON(!found);
76#ifdef DEBUG
77	dump_list(head, type_str);
78#endif
79}
80
81int ocxl_pasid_afu_alloc(struct ocxl_fn *fn, u32 size)
82{
83	int max_pasid;
84
85	if (fn->config.max_pasid_log < 0)
86		return -ENOSPC;
87	max_pasid = 1 << fn->config.max_pasid_log;
88	return range_alloc(&fn->pasid_list, size, max_pasid, "afu pasid");
89}
90
91void ocxl_pasid_afu_free(struct ocxl_fn *fn, u32 start, u32 size)
92{
93	return range_free(&fn->pasid_list, start, size, "afu pasid");
94}
95
96int ocxl_actag_afu_alloc(struct ocxl_fn *fn, u32 size)
97{
98	int max_actag;
99
100	max_actag = fn->actag_enabled;
101	return range_alloc(&fn->actag_list, size, max_actag, "afu actag");
102}
103
104void ocxl_actag_afu_free(struct ocxl_fn *fn, u32 start, u32 size)
105{
106	return range_free(&fn->actag_list, start, size, "afu actag");
107}
108