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