| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * GXP ranged resource allocator. |
| * |
| * Copyright (C) 2021 Google LLC |
| */ |
| |
| #include "gxp-range-alloc.h" |
| |
| struct range_alloc *range_alloc_create(int start, int end) |
| { |
| struct range_alloc *ra; |
| int count; |
| int size; |
| |
| count = end - start; |
| if (count <= 0) |
| return ERR_PTR(-EINVAL); |
| |
| size = sizeof(struct range_alloc) + count * sizeof(int); |
| ra = kzalloc(size, GFP_KERNEL); |
| if (!ra) |
| return ERR_PTR(-ENOMEM); |
| |
| ra->total_count = count; |
| ra->free_count = count; |
| ra->start_index = start; |
| mutex_init(&ra->lock); |
| |
| return ra; |
| } |
| |
| int range_alloc_get(struct range_alloc *r, int element) |
| { |
| int index = element - r->start_index; |
| |
| mutex_lock(&r->lock); |
| if (index < 0 || index >= r->total_count) { |
| mutex_unlock(&r->lock); |
| return -EINVAL; |
| } |
| |
| if (r->elements[index]) { |
| mutex_unlock(&r->lock); |
| return -EBUSY; |
| } |
| |
| r->elements[index] = 1; |
| r->free_count--; |
| |
| mutex_unlock(&r->lock); |
| return 0; |
| } |
| |
| int range_alloc_get_any(struct range_alloc *r, int *element) |
| { |
| int i; |
| |
| mutex_lock(&r->lock); |
| if (!r->free_count) { |
| mutex_unlock(&r->lock); |
| return -ENOMEM; |
| } |
| |
| for (i = 0; i < r->total_count; i++) { |
| if (r->elements[i] == 0) { |
| r->elements[i] = 1; |
| r->free_count--; |
| *element = i + r->start_index; |
| mutex_unlock(&r->lock); |
| return 0; |
| } |
| } |
| mutex_unlock(&r->lock); |
| return -ENOMEM; |
| } |
| |
| int range_alloc_put(struct range_alloc *r, int element) |
| { |
| int index = element - r->start_index; |
| |
| mutex_lock(&r->lock); |
| if (index < 0 || index >= r->total_count) { |
| mutex_unlock(&r->lock); |
| return -EINVAL; |
| } |
| |
| if (r->elements[index] == 0) { |
| mutex_unlock(&r->lock); |
| return -EBUSY; |
| } |
| |
| r->elements[index] = 0; |
| r->free_count++; |
| |
| mutex_unlock(&r->lock); |
| return 0; |
| } |
| |
| int range_alloc_num_free(struct range_alloc *r) |
| { |
| int free_count; |
| |
| mutex_lock(&r->lock); |
| free_count = r->free_count; |
| mutex_unlock(&r->lock); |
| |
| return free_count; |
| } |
| |
| int range_alloc_destroy(struct range_alloc *r) |
| { |
| if (!r) |
| return -EFAULT; |
| kfree(r); |
| |
| return 0; |
| } |