blob: 8713fe4d9a8298265306908f7c8616452fdf96b2 [file] [log] [blame] [edit]
/*
* Copyright (c) 2004 Topspin Communications. All rights reserved.
* Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
* Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved.
* Copyright (c) 2019, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* 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.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdlib.h>
#include <util/bitmap.h>
#include "mlx5dv_dr.h"
struct dr_icm_pool;
struct dr_icm_buddy_mem;
static int dr_find_first_bit(const unsigned long *set_addr,
const unsigned long *addr,
unsigned int size)
{
unsigned int set_size = (size - 1) / BITS_PER_LONG + 1;
unsigned long set_idx;
/* find the first free in the first level */
set_idx = bitmap_find_first_bit(set_addr, 0, set_size);
/* find the next level */
return bitmap_find_first_bit(addr, set_idx * BITS_PER_LONG, size);
}
int dr_buddy_init(struct dr_icm_buddy_mem *buddy, uint32_t max_order)
{
int i, s;
buddy->max_order = max_order;
list_node_init(&buddy->list_node);
list_head_init(&buddy->used_list);
list_head_init(&buddy->hot_list);
buddy->bits = calloc(buddy->max_order + 1, sizeof(long *));
if (!buddy->bits) {
errno = ENOMEM;
return ENOMEM;
}
buddy->num_free = calloc(buddy->max_order + 1, sizeof(*buddy->num_free));
if (!buddy->num_free)
goto err_out_free_bits;
buddy->set_bit = calloc(buddy->max_order + 1, sizeof(long *));
if (!buddy->set_bit)
goto err_out_free_num_free;
/* Allocating max_order bitmaps, one for each order.
* only the bitmap for the maximum size will be available for use and
* the first bit there will be set.
*/
for (i = 0; i <= buddy->max_order; ++i) {
s = 1 << (buddy->max_order - i);
buddy->bits[i] = bitmap_alloc0(s);
if (!buddy->bits[i])
goto err_out_free_each_bit_per_order;
}
for (i = 0; i <= buddy->max_order; ++i) {
s = BITS_TO_LONGS(1 << (buddy->max_order - i));
buddy->set_bit[i] = bitmap_alloc0(s);
if (!buddy->set_bit[i])
goto err_out_free_set;
}
bitmap_set_bit(buddy->bits[buddy->max_order], 0);
bitmap_set_bit(buddy->set_bit[buddy->max_order], 0);
buddy->num_free[buddy->max_order] = 1;
return 0;
err_out_free_set:
for (i = 0; i <= buddy->max_order; ++i)
free(buddy->set_bit[i]);
err_out_free_each_bit_per_order:
free(buddy->set_bit);
for (i = 0; i <= buddy->max_order; ++i)
free(buddy->bits[i]);
err_out_free_num_free:
free(buddy->num_free);
err_out_free_bits:
free(buddy->bits);
errno = ENOMEM;
return ENOMEM;
}
void dr_buddy_cleanup(struct dr_icm_buddy_mem *buddy)
{
int i;
list_del(&buddy->list_node);
for (i = 0; i <= buddy->max_order; ++i) {
free(buddy->bits[i]);
free(buddy->set_bit[i]);
}
free(buddy->set_bit);
free(buddy->num_free);
free(buddy->bits);
}
/*
* Find the borders (high and low) of specific seg (segment location)
* of the lower level of the bitmap in order to mark the upper layer
* of bitmap.
*/
static void dr_buddy_get_seg_borders(uint32_t seg,
uint32_t *low,
uint32_t *high)
{
*low = (seg / BITS_PER_LONG) * BITS_PER_LONG;
*high = ((seg / BITS_PER_LONG) + 1) * BITS_PER_LONG;
}
/*
* We have two layers of searching in the bitmaps, so when needed update the
* second layer of search.
*/
static void dr_buddy_update_upper_bitmap(struct dr_icm_buddy_mem *buddy,
uint32_t seg, int order)
{
uint32_t h, l, m;
/* clear upper layer of search if needed */
dr_buddy_get_seg_borders(seg, &l, &h);
m = bitmap_find_first_bit(buddy->bits[order], l, h);
if (m == h) /* nothing in the long that includes seg */
bitmap_clear_bit(buddy->set_bit[order], seg / BITS_PER_LONG);
}
/*
* This function finds the first area of the managed memory by the buddy.
* It uses the data structures of the buddy-system in order to find the first
* area of free place, starting from the current order till the maximum order
* in the system.
* The function returns the location (seg) in the whole buddy memory area, this
* indicates the place of the memory to use, it is the index of the mem segment.
*/
int dr_buddy_alloc_mem(struct dr_icm_buddy_mem *buddy, int order)
{
int seg;
int o, m;
for (o = order; o <= buddy->max_order; ++o)
if (buddy->num_free[o]) {
m = 1 << (buddy->max_order - o);
seg = dr_find_first_bit(buddy->set_bit[o], buddy->bits[o], m);
if (m <= seg) {
/* not found free mem, but there are free mem */
assert(false);
return -1;
}
goto found;
}
return -1;
found:
bitmap_clear_bit(buddy->bits[o], seg);
/* clear upper layer of search if needed */
dr_buddy_update_upper_bitmap(buddy, seg, o);
--buddy->num_free[o];
/* if we find free memory in some order that it is bigger than the
* required order, we need to devied each order between the required to
* the found one to 2, and mark accordingly.
*/
while (o > order) {
--o;
seg <<= 1;
bitmap_set_bit(buddy->bits[o], seg ^ 1);
bitmap_set_bit(buddy->set_bit[o], (seg ^ 1) / BITS_PER_LONG);
++buddy->num_free[o];
}
seg <<= order;
return seg;
}
void
dr_buddy_free_mem(struct dr_icm_buddy_mem *buddy, uint32_t seg, int order)
{
seg >>= order;
/* whenever a segment is free, the mem is added to the buddy that gave it */
while (bitmap_test_bit(buddy->bits[order], seg ^ 1)) {
bitmap_clear_bit(buddy->bits[order], seg ^ 1);
dr_buddy_update_upper_bitmap(buddy, seg ^ 1, order);
--buddy->num_free[order];
seg >>= 1;
++order;
}
bitmap_set_bit(buddy->bits[order], seg);
bitmap_set_bit(buddy->set_bit[order], seg / BITS_PER_LONG);
++buddy->num_free[order];
}