| // 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]; |
| } |
| |