diff options
Diffstat (limited to 'linux/drivers/soc/fsl/qbman/dpaa_resource.c')
-rw-r--r-- | linux/drivers/soc/fsl/qbman/dpaa_resource.c | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/linux/drivers/soc/fsl/qbman/dpaa_resource.c b/linux/drivers/soc/fsl/qbman/dpaa_resource.c new file mode 100644 index 00000000..3f7b74bf --- /dev/null +++ b/linux/drivers/soc/fsl/qbman/dpaa_resource.c @@ -0,0 +1,363 @@ +#include <machine/rtems-bsd-kernel-space.h> + +#include <rtems/bsd/local/opt_dpaa.h> + +/* Copyright 2009 - 2015 Freescale Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(CONFIG_FSL_BMAN_PORTAL) || \ + defined(CONFIG_FSL_BMAN_PORTAL_MODULE) || \ + defined(CONFIG_FSL_QMAN_PORTAL) || \ + defined(CONFIG_FSL_QMAN_PORTAL_MODULE) +#include "dpaa_sys.h" + +/* The allocator is a (possibly-empty) list of these */ +struct dpaa_resource_node { + struct list_head list; + u32 base; + u32 num; + /* refcount and is_alloced are only set + when the node is in the used list */ + unsigned int refcount; + int is_alloced; +}; + +#ifdef DPAA_RESOURCE_DEBUG +#define DPRINT pr_info +static void DUMP(struct dpaa_resource *alloc) +{ + int off = 0; + char buf[256]; + struct dpaa_resource_node *p; + + pr_info("Free Nodes\n"); + list_for_each_entry(p, &alloc->free, list) { + if (off < 255) + off += snprintf(buf + off, 255-off, "{%d,%d}", + p->base, p->base + p->num - 1); + } + pr_info("%s\n", buf); + + off = 0; + pr_info("Used Nodes\n"); + list_for_each_entry(p, &alloc->used, list) { + if (off < 255) + off += snprintf(buf + off, 255-off, "{%d,%d}", + p->base, p->base + p->num - 1); + } + pr_info("%s\n", buf); +} +#else +#define DPRINT(x...) +#define DUMP(a) +#endif + +int dpaa_resource_new(struct dpaa_resource *alloc, u32 *result, + u32 count, u32 align, int partial) +{ + struct dpaa_resource_node *i = NULL, *next_best = NULL, + *used_node = NULL; + u32 base, next_best_base = 0, num = 0, next_best_num = 0; + struct dpaa_resource_node *margin_left, *margin_right; + + *result = (u32)-1; + DPRINT("alloc_range(%d,%d,%d)\n", count, align, partial); + DUMP(alloc); + /* If 'align' is 0, it should behave as though it was 1 */ + if (!align) + align = 1; + margin_left = kmalloc(sizeof(*margin_left), GFP_KERNEL); + if (!margin_left) + goto err; + margin_right = kmalloc(sizeof(*margin_right), GFP_KERNEL); + if (!margin_right) { + kfree(margin_left); + goto err; + } + spin_lock_irq(&alloc->lock); + list_for_each_entry(i, &alloc->free, list) { + base = (i->base + align - 1) / align; + base *= align; + if ((base - i->base) >= i->num) + /* alignment is impossible, regardless of count */ + continue; + num = i->num - (base - i->base); + if (num >= count) { + /* this one will do nicely */ + num = count; + goto done; + } + if (num > next_best_num) { + next_best = i; + next_best_base = base; + next_best_num = num; + } + } + if (partial && next_best) { + i = next_best; + base = next_best_base; + num = next_best_num; + } else + i = NULL; +done: + if (i) { + if (base != i->base) { + margin_left->base = i->base; + margin_left->num = base - i->base; + list_add_tail(&margin_left->list, &i->list); + } else + kfree(margin_left); + if ((base + num) < (i->base + i->num)) { + margin_right->base = base + num; + margin_right->num = (i->base + i->num) - + (base + num); + list_add(&margin_right->list, &i->list); + } else + kfree(margin_right); + list_del(&i->list); + kfree(i); + *result = base; + } + spin_unlock_irq(&alloc->lock); +err: + DPRINT("returning %d\n", i ? num : -ENOMEM); + DUMP(alloc); + if (!i) + return -ENOMEM; + + /* Add the allocation to the used list with a refcount of 1 */ + used_node = kmalloc(sizeof(*used_node), GFP_KERNEL); + if (!used_node) + return -ENOMEM; + used_node->base = *result; + used_node->num = num; + used_node->refcount = 1; + used_node->is_alloced = 1; + list_add_tail(&used_node->list, &alloc->used); + return (int)num; +} +EXPORT_SYMBOL(dpaa_resource_new); + +/* Allocate the list node using GFP_ATOMIC, because we *really* want to avoid + * forcing error-handling on to users in the deallocation path. */ +static void _dpaa_resource_free(struct dpaa_resource *alloc, u32 base_id, + u32 count) +{ + struct dpaa_resource_node *i, + *node = kmalloc(sizeof(*node), GFP_ATOMIC); + + BUG_ON(!node); + DPRINT("release_range(%d,%d)\n", base_id, count); + DUMP(alloc); + BUG_ON(!count); + spin_lock_irq(&alloc->lock); + + node->base = base_id; + node->num = count; + list_for_each_entry(i, &alloc->free, list) { + if (i->base >= node->base) { + /* BUG_ON(any overlapping) */ + BUG_ON(i->base < (node->base + node->num)); + list_add_tail(&node->list, &i->list); + goto done; + } + } + list_add_tail(&node->list, &alloc->free); +done: + /* Merge to the left */ + i = list_entry(node->list.prev, struct dpaa_resource_node, list); + if (node->list.prev != &alloc->free) { + BUG_ON((i->base + i->num) > node->base); + if ((i->base + i->num) == node->base) { + node->base = i->base; + node->num += i->num; + list_del(&i->list); + kfree(i); + } + } + /* Merge to the right */ + i = list_entry(node->list.next, struct dpaa_resource_node, list); + if (node->list.next != &alloc->free) { + BUG_ON((node->base + node->num) > i->base); + if ((node->base + node->num) == i->base) { + node->num += i->num; + list_del(&i->list); + kfree(i); + } + } + spin_unlock_irq(&alloc->lock); + DUMP(alloc); +} + +static void dpaa_resource_free(struct dpaa_resource *alloc, u32 base_id, + u32 count) +{ + struct dpaa_resource_node *i = NULL; + + spin_lock_irq(&alloc->lock); + + /* First find the node in the used list and decrement its ref count */ + list_for_each_entry(i, &alloc->used, list) { + if (i->base == base_id && i->num == count) { + --i->refcount; + if (i->refcount == 0) { + list_del(&i->list); + spin_unlock_irq(&alloc->lock); + if (i->is_alloced) + _dpaa_resource_free(alloc, base_id, + count); + kfree(i); + return; + } + spin_unlock_irq(&alloc->lock); + return; + } + } + /* Couldn't find the allocation */ + pr_err("Attempt to free ID 0x%x COUNT %d that wasn't alloc'd or reserved\n", + base_id, count); + spin_unlock_irq(&alloc->lock); +} + +/* Same as free but no previous allocation checking is needed */ +void dpaa_resource_seed(struct dpaa_resource *alloc, u32 base_id, u32 count) +{ + _dpaa_resource_free(alloc, base_id, count); +} +EXPORT_SYMBOL(dpaa_resource_seed); + +/* Like 'new' but specifies the desired range, returns -ENOMEM if the entire + * desired range is not available, or 0 for success + */ +int dpaa_resource_reserve(struct dpaa_resource *alloc, u32 base, u32 num) +{ + struct dpaa_resource_node *i = NULL, *used_node; + + DPRINT("alloc_reserve(%d,%d)\n", base, num); + DUMP(alloc); + + spin_lock_irq(&alloc->lock); + + /* Check for the node in the used list. + If found, increase it's refcount */ + list_for_each_entry(i, &alloc->used, list) { + if ((i->base == base) && (i->num == num)) { + ++i->refcount; + spin_unlock_irq(&alloc->lock); + return 0; + } + if ((base >= i->base) && (base < (i->base + i->num))) { + /* This is an attempt to reserve a region that was + already reserved or alloced with a different + base or num */ + pr_err("Cannot reserve %d - %d, it overlaps with" + " existing reservation from %d - %d\n", + base, base + num - 1, i->base, + i->base + i->num - 1); + spin_unlock_irq(&alloc->lock); + return -1; + } + } + /* Check to make sure this ID isn't in the free list */ + list_for_each_entry(i, &alloc->free, list) { + if ((base >= i->base) && (base < (i->base + i->num))) { + /* yep, the reservation is within this node */ + pr_err("Cannot reserve %d - %d, it overlaps with" + " free range %d - %d and must be alloced\n", + base, base + num - 1, + i->base, i->base + i->num - 1); + spin_unlock_irq(&alloc->lock); + return -1; + } + } + /* Add the allocation to the used list with a refcount of 1 */ + used_node = kmalloc(sizeof(*used_node), GFP_KERNEL); + if (!used_node) { + spin_unlock_irq(&alloc->lock); + return -ENOMEM; + + } + used_node->base = base; + used_node->num = num; + used_node->refcount = 1; + used_node->is_alloced = 0; + list_add_tail(&used_node->list, &alloc->used); + spin_unlock_irq(&alloc->lock); + return 0; +} +EXPORT_SYMBOL(dpaa_resource_reserve); + +/* This is a sort-of-conditional dpaa_resource_free() routine. Eg. when + * releasing FQIDs (probably from user-space), it can filter out those + * that aren't in the OOS state (better to leak a h/w resource than to + * crash). This function returns the number of invalid IDs that were not + * released. +*/ +u32 dpaa_resource_release(struct dpaa_resource *alloc, + u32 id, u32 count, int (*is_valid)(u32 id)) +{ + int valid_mode = 0; + u32 loop = id, total_invalid = 0; + + while (loop < (id + count)) { + int isvalid = is_valid ? is_valid(loop) : 1; + + if (!valid_mode) { + /* We're looking for a valid ID to terminate an invalid + * range */ + if (isvalid) { + /* We finished a range of invalid IDs, a valid + * range is now underway */ + valid_mode = 1; + count -= (loop - id); + id = loop; + } else + total_invalid++; + } else { + /* We're looking for an invalid ID to terminate a + * valid range */ + if (!isvalid) { + /* Release the range of valid IDs, an unvalid + * range is now underway */ + if (loop > id) + dpaa_resource_free(alloc, id, + loop - id); + valid_mode = 0; + } + } + loop++; + } + /* Release any unterminated range of valid IDs */ + if (valid_mode && count) + dpaa_resource_free(alloc, id, count); + return total_invalid; +} +EXPORT_SYMBOL(dpaa_resource_release); +#endif /* CONFIG_FSL_*MAN_PORTAL* */ |