| // 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_obj(struct id_range); | |
| 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"); | |
| } |