| /****************************************************************************** |
| * Copyright (c) 2013 IBM Corporation |
| * All rights reserved. |
| * This program and the accompanying materials |
| * are made available under the terms of the BSD License |
| * which accompanies this distribution, and is available at |
| * http://www.opensource.org/licenses/bsd-license.php |
| * |
| * Contributors: |
| * IBM Corporation - initial implementation |
| *****************************************************************************/ |
| /* |
| * All functions concerning interface to slof |
| */ |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdbool.h> |
| #include <helpers.h> |
| |
| #undef DEBUG |
| //#define DEBUG |
| #ifdef DEBUG |
| #define dprintf(_x ...) do { printf ("%s: ", __func__); printf(_x); } while (0); |
| #else |
| #define dprintf(_x ...) |
| #endif |
| |
| #define DEFAULT_BLOCK_SIZE 4096 |
| |
| #define BM_WORD_SIZE (sizeof(unsigned long)) |
| #define BM_WORD_BITS (BM_WORD_SIZE * 8) |
| |
| struct bitmap { |
| unsigned long start; |
| unsigned long size; |
| unsigned long bm_size; |
| unsigned long block_size; |
| unsigned long free_blocks; |
| unsigned long bmw[]; |
| }; |
| |
| #define BIT(x) (1UL << x) |
| #define BM_WORD(bmw, n) (bmw[n/BM_WORD_BITS]) |
| #define BM_WORD_MODULO(n) (n % BM_WORD_BITS) |
| #define BM_NUM_BITS(reqsize, bsize) ((reqsize / bsize) + (reqsize % bsize? 1 : 0)) |
| |
| void bm_clear_bit(unsigned long *bmw, int n) |
| { |
| BM_WORD(bmw, n) &= ~BIT(BM_WORD_MODULO(n)); |
| } |
| |
| void bm_set_bit(unsigned long *bmw, int n) |
| { |
| BM_WORD(bmw, n) |= BIT(BM_WORD_MODULO(n)); |
| } |
| |
| bool bm_test_bit(unsigned long *bmw, int n) |
| { |
| #ifdef DEBUG |
| //printf("BMW %x, bitpos %d, value %d\n", &BM_WORD(bmw, n), n, !!(BM_WORD(bmw, n) & BIT(BM_WORD_MODULO(n)))); |
| #endif |
| return !!(BM_WORD(bmw, n) & BIT(BM_WORD_MODULO(n))); |
| } |
| |
| /* Improvement: can use FFS routines to get faster results */ |
| int bm_find_bits(struct bitmap *bm, unsigned int n_bits) |
| { |
| unsigned int i, j, total_bits; |
| int found = -1; |
| dprintf("Finding %d bits set\n", n_bits); |
| total_bits = BM_NUM_BITS(bm->size, bm->block_size); |
| for(i = 0; i < total_bits; i++) { |
| if (!bm_test_bit(bm->bmw, i)) |
| continue; |
| /* we have hit the boundary now, give up */ |
| if (i + n_bits > total_bits) |
| break; |
| /* Lets find if we have consecutive bits set */ |
| for(j = i; j < (i + n_bits); j++) { |
| if (!bm_test_bit(bm->bmw, (j))) |
| break; |
| } |
| /* Got it */ |
| if (j == (i + n_bits)) { |
| found = i; |
| break; |
| } |
| } |
| return found; |
| } |
| |
| void SLOF_bm_print(unsigned long handle) |
| { |
| struct bitmap *bm; |
| unsigned int i; |
| |
| if (!handle) |
| return; |
| |
| bm = (struct bitmap *) handle; |
| printf("BITMAP: start %lx, size %ld, blocksize %ld\n\n", |
| bm->start, bm->size, bm->block_size); |
| printf("0 16 32 48 63\n"); |
| for(i = 0; i < BM_NUM_BITS(bm->size, bm->block_size); i++) { |
| if (i > 0 && (i % 64 == 0)) |
| printf("\n"); |
| else if (i > 0 && (i % 8 == 0)) |
| printf(" "); |
| printf("%d", bm_test_bit(bm->bmw, i)); |
| } |
| printf("\n\n"); |
| } |
| |
| unsigned long SLOF_bm_allocator_init(unsigned long start, unsigned long size, |
| unsigned long blocksize) |
| { |
| struct bitmap *bm; |
| unsigned long alloc_size, bm_size, n_bits; |
| |
| dprintf("enter start %x, size %d, block-size %d\n", start, size, blocksize); |
| |
| if (!size) |
| return 0; |
| if (!blocksize) |
| blocksize = DEFAULT_BLOCK_SIZE; |
| |
| n_bits = BM_NUM_BITS(size, blocksize); |
| bm_size = (n_bits / BM_WORD_BITS) + ((n_bits % BM_WORD_BITS)? 1 : 0); |
| alloc_size = sizeof(struct bitmap) + bm_size * BM_WORD_SIZE; |
| dprintf("Size %ld, blocksize %ld, bm_size %ld, alloc_size %ld\n", |
| size, blocksize, bm_size, alloc_size); |
| bm = (struct bitmap *) SLOF_alloc_mem(alloc_size); |
| if (!bm) |
| return 0; |
| bm->start = start; |
| bm->size = size; |
| bm->bm_size = bm_size; |
| bm->block_size = blocksize; |
| bm->free_blocks = n_bits; |
| memset(bm->bmw, 0xFF, bm_size*BM_WORD_SIZE); |
| return (unsigned long)bm; |
| } |
| |
| unsigned long SLOF_bm_alloc(unsigned long handle, unsigned long size) |
| { |
| struct bitmap *bm; |
| unsigned long n_bits; |
| unsigned long addr; |
| unsigned int i; |
| int bitpos; |
| |
| if (!handle) |
| return -1; |
| |
| bm = (struct bitmap *) handle; |
| |
| n_bits = BM_NUM_BITS(size, bm->block_size); |
| if (n_bits > bm->free_blocks) |
| return -1; |
| |
| bitpos = bm_find_bits(bm, n_bits); |
| if (bitpos == -1) |
| return -1; |
| |
| dprintf("BMW %d, bitpos %d\n", i, bitpos); |
| dprintf("size %d, block_size %d, n_bits %d\n", size, bm->block_size, n_bits); |
| for(i = bitpos; i < (bitpos + n_bits); i++) { |
| #ifdef DEBUG |
| if (!bm_test_bit(bm->bmw, i)) |
| dprintf("Warning: Bit already in use: %d\n", i); |
| #endif |
| bm_clear_bit(bm->bmw, i); |
| } |
| bm->free_blocks -= n_bits; |
| addr = bm->start + bitpos * bm->block_size; |
| dprintf("BMW %d, bitpos %d addr %lx free_blocks %d\n", i, bitpos, addr, bm->free_blocks); |
| return addr; |
| } |
| |
| void SLOF_bm_free(unsigned long handle, unsigned long ptr, unsigned long size) |
| { |
| struct bitmap *bm; |
| unsigned long bitpos, n_bits; |
| unsigned long addr; |
| unsigned int i; |
| |
| if (!handle) |
| return; |
| |
| bm = (struct bitmap *) handle; |
| addr = (unsigned long ) ptr; |
| n_bits = BM_NUM_BITS(size, bm->block_size); |
| if (addr < bm->start || (bm->start + bm->size) < (addr + size)) { |
| printf("Error: Bitmap start %lx, size %ld, requested address %lx, size %ld\n", |
| bm->start, bm->size, addr, size); |
| return; |
| } |
| bitpos = (addr - bm->start) / bm->block_size; |
| bm->free_blocks += n_bits; |
| |
| #ifdef DEBUG |
| dprintf("addr %lx, bitpos %d\n", addr, bitpos); |
| dprintf("size %d, block_size %d, n_bits %d, free_blocks %d\n", size, bm->block_size, n_bits, bm->free_blocks); |
| if (addr % bm->block_size) { |
| dprintf("Warning: Address not aligned addr %lx\n", addr); |
| } |
| #endif |
| |
| for(i = bitpos; i < (bitpos + n_bits); i++) { |
| #ifdef DEBUG |
| if (bm_test_bit(bm->bmw, i)) |
| dprintf("Warning: Bit already set: %d\n", i); |
| #endif |
| bm_set_bit(bm->bmw, i); |
| } |
| |
| return; |
| } |