xref: /openbmc/linux/drivers/misc/ocxl/pasid.c (revision 5ef3166e)
1 // SPDX-License-Identifier: GPL-2.0+
2 // Copyright 2017 IBM Corp.
3 #include "ocxl_internal.h"
4 
5 
6 struct id_range {
7 	struct list_head list;
8 	u32 start;
9 	u32 end;
10 };
11 
12 #ifdef DEBUG
dump_list(struct list_head * head,char * type_str)13 static 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 
range_alloc(struct list_head * head,u32 size,int max_id,char * type_str)24 static 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 
range_free(struct list_head * head,u32 start,u32 size,char * type_str)61 static 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 
ocxl_pasid_afu_alloc(struct ocxl_fn * fn,u32 size)81 int 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 
ocxl_pasid_afu_free(struct ocxl_fn * fn,u32 start,u32 size)91 void 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 
ocxl_actag_afu_alloc(struct ocxl_fn * fn,u32 size)96 int 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 
ocxl_actag_afu_free(struct ocxl_fn * fn,u32 start,u32 size)104 void 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