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