GNU Linux-libre 4.19.286-gnu1
[releases.git] / drivers / misc / ocxl / pasid.c
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 }