blob: 7df11a019df96f6d73a38277f2f7f8ebc7a7f7b8 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2004 Topspin Communications. All rights reserved.
* Copyright (c) 2005 - 2008 Mellanox Technologies. All rights reserved.
* Copyright (c) 2006 - 2007 Cisco Systems, Inc. All rights reserved.
* Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
*/
#include "dr_types.h"
int mlx5dr_buddy_init(struct mlx5dr_icm_buddy_mem *buddy,
unsigned int max_order)
{
int i;
buddy->max_order = max_order;
INIT_LIST_HEAD(&buddy->list_node);
INIT_LIST_HEAD(&buddy->used_list);
INIT_LIST_HEAD(&buddy->hot_list);
buddy->bitmap = kcalloc(buddy->max_order + 1,
sizeof(*buddy->bitmap),
GFP_KERNEL);
buddy->num_free = kcalloc(buddy->max_order + 1,
sizeof(*buddy->num_free),
GFP_KERNEL);
if (!buddy->bitmap || !buddy->num_free)
goto err_free_all;
/* Allocating max_order bitmaps, one for each order */
for (i = 0; i <= buddy->max_order; ++i) {
unsigned int size = 1 << (buddy->max_order - i);
buddy->bitmap[i] = bitmap_zalloc(size, GFP_KERNEL);
if (!buddy->bitmap[i])
goto err_out_free_each_bit_per_order;
}
/* In the beginning, we have only one order that is available for
* use (the biggest one), so mark the first bit in both bitmaps.
*/
bitmap_set(buddy->bitmap[buddy->max_order], 0, 1);
buddy->num_free[buddy->max_order] = 1;
return 0;
err_out_free_each_bit_per_order:
for (i = 0; i <= buddy->max_order; ++i)
bitmap_free(buddy->bitmap[i]);
err_free_all:
kfree(buddy->num_free);
kfree(buddy->bitmap);
return -ENOMEM;
}
void mlx5dr_buddy_cleanup(struct mlx5dr_icm_buddy_mem *buddy)
{
int i;
list_del(&buddy->list_node);
for (i = 0; i <= buddy->max_order; ++i)
bitmap_free(buddy->bitmap[i]);
kfree(buddy->num_free);
kfree(buddy->bitmap);
}
static int dr_buddy_find_free_seg(struct mlx5dr_icm_buddy_mem *buddy,
unsigned int start_order,
unsigned int *segment,
unsigned int *order)
{
unsigned int seg, order_iter, m;
for (order_iter = start_order;
order_iter <= buddy->max_order; ++order_iter) {
if (!buddy->num_free[order_iter])
continue;
m = 1 << (buddy->max_order - order_iter);
seg = find_first_bit(buddy->bitmap[order_iter], m);
if (WARN(seg >= m,
"ICM Buddy: failed finding free mem for order %d\n",
order_iter))
return -ENOMEM;
break;
}
if (order_iter > buddy->max_order)
return -ENOMEM;
*segment = seg;
*order = order_iter;
return 0;
}
/**
* mlx5dr_buddy_alloc_mem() - Update second level bitmap.
* @buddy: Buddy to update.
* @order: Order of the buddy to update.
* @segment: Segment number.
*
* This function finds the first area of the ICM memory managed by this 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.
*
* Return: 0 when segment is set, non-zero error status otherwise.
*
* The function returns the location (segment) in the whole buddy ICM memory
* area - the index of the memory segment that is available for use.
*/
int mlx5dr_buddy_alloc_mem(struct mlx5dr_icm_buddy_mem *buddy,
unsigned int order,
unsigned int *segment)
{
unsigned int seg, order_iter;
int err;
err = dr_buddy_find_free_seg(buddy, order, &seg, &order_iter);
if (err)
return err;
bitmap_clear(buddy->bitmap[order_iter], seg, 1);
--buddy->num_free[order_iter];
/* If we found free memory in some order that is bigger than the
* required order, we need to split every order between the required
* order and the order that we found into two parts, and mark accordingly.
*/
while (order_iter > order) {
--order_iter;
seg <<= 1;
bitmap_set(buddy->bitmap[order_iter], seg ^ 1, 1);
++buddy->num_free[order_iter];
}
seg <<= order;
*segment = seg;
return 0;
}
void mlx5dr_buddy_free_mem(struct mlx5dr_icm_buddy_mem *buddy,
unsigned int seg, unsigned int order)
{
seg >>= order;
/* Whenever a segment is free,
* the mem is added to the buddy that gave it.
*/
while (test_bit(seg ^ 1, buddy->bitmap[order])) {
bitmap_clear(buddy->bitmap[order], seg ^ 1, 1);
--buddy->num_free[order];
seg >>= 1;
++order;
}
bitmap_set(buddy->bitmap[order], seg, 1);
++buddy->num_free[order];
}