diff options
Diffstat (limited to 'drivers/misc/ocxl/pasid.c')
-rw-r--r-- | drivers/misc/ocxl/pasid.c | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/drivers/misc/ocxl/pasid.c b/drivers/misc/ocxl/pasid.c new file mode 100644 index 000000000000..d14cb56e6920 --- /dev/null +++ b/drivers/misc/ocxl/pasid.c | |||
@@ -0,0 +1,107 @@ | |||
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 | ||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | } | ||