/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * bitmap.c * * Basic bitmap routines for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include "bitmap.h" /* The public API */ void ocfs2_bitmap_free(ocfs2_bitmap *bitmap) { struct rb_node *node; struct ocfs2_bitmap_region *br; /* * If the bitmap needs to do extra cleanup of region, * it should have done it in destroy_notify. Same with the * private pointers. */ if (bitmap->b_ops->destroy_notify) (*bitmap->b_ops->destroy_notify)(bitmap); while ((node = rb_first(&bitmap->b_regions)) != NULL) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); rb_erase(&br->br_node, &bitmap->b_regions); ocfs2_bitmap_free_region(br); } ocfs2_free(&bitmap->b_description); ocfs2_free(&bitmap); } errcode_t ocfs2_bitmap_set(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval) { errcode_t ret; int old_tmp; if (bitno >= bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; ret = (*bitmap->b_ops->set_bit)(bitmap, bitno, &old_tmp); if (ret) return ret; if (!old_tmp) bitmap->b_set_bits++; if (oldval) *oldval = old_tmp; return 0; } errcode_t ocfs2_bitmap_clear(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval) { errcode_t ret; int old_tmp; if (bitno >= bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; ret = (*bitmap->b_ops->clear_bit)(bitmap, bitno, &old_tmp); if (ret) return ret; if (old_tmp) bitmap->b_set_bits--; if (oldval) *oldval = old_tmp; return 0; } errcode_t ocfs2_bitmap_test(ocfs2_bitmap *bitmap, uint64_t bitno, int *val) { errcode_t ret; if (bitno >= bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; ret = (*bitmap->b_ops->test_bit)(bitmap, bitno, val); return ret; } errcode_t ocfs2_bitmap_find_next_set(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found) { if (start >= bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; return (*bitmap->b_ops->find_next_set)(bitmap, start, found); } errcode_t ocfs2_bitmap_find_next_clear(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found) { if (start >= bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; return (*bitmap->b_ops->find_next_clear)(bitmap, start, found); } /* kind of poorly named, but I couldn't come up with something nicer */ errcode_t ocfs2_bitmap_alloc_range(ocfs2_bitmap *bitmap, uint64_t min, uint64_t len, uint64_t *first_bit, uint64_t *bits_found) { errcode_t ret; if (min == 0 || len == 0 || len >= bitmap->b_total_bits || min > len) return OCFS2_ET_INVALID_ARGUMENT; ret = (*bitmap->b_ops->alloc_range)(bitmap, min, len, first_bit, bits_found); if (ret == 0 && *bits_found < min) abort(); return ret; } errcode_t ocfs2_bitmap_clear_range(ocfs2_bitmap *bitmap, uint64_t len, uint64_t first_bit) { if (len == 0 || len + first_bit > bitmap->b_total_bits) return OCFS2_ET_INVALID_ARGUMENT; return (*bitmap->b_ops->clear_range)(bitmap, len, first_bit); } errcode_t ocfs2_bitmap_read(ocfs2_bitmap *bitmap) { if (!bitmap->b_ops->read_bitmap) return OCFS2_ET_INVALID_ARGUMENT; /* FIXME: Some sane error, or handle in ->read_bitmap() */ if (rb_first(&bitmap->b_regions)) return OCFS2_ET_INVALID_BIT; return (*bitmap->b_ops->read_bitmap)(bitmap); } errcode_t ocfs2_bitmap_write(ocfs2_bitmap *bitmap) { if (!bitmap->b_ops->write_bitmap) return OCFS2_ET_INVALID_ARGUMENT; return (*bitmap->b_ops->write_bitmap)(bitmap); } uint64_t ocfs2_bitmap_get_set_bits(ocfs2_bitmap *bitmap) { return bitmap->b_set_bits; } /* * The remaining functions are private to the library. */ /* * This function is private to the library. Bitmap subtypes will * use this to allocate their structure, but their b_ops will * determine how they work. */ errcode_t ocfs2_bitmap_new(ocfs2_filesys *fs, uint64_t total_bits, const char *description, struct ocfs2_bitmap_operations *ops, void *private_data, ocfs2_bitmap **ret_bitmap) { errcode_t ret; ocfs2_bitmap *bitmap; if (!ops->set_bit || !ops->clear_bit || !ops->test_bit) return OCFS2_ET_INVALID_ARGUMENT; ret = ocfs2_malloc0(sizeof(struct _ocfs2_bitmap), &bitmap); if (ret) return ret; bitmap->b_fs = fs; bitmap->b_total_bits = total_bits; bitmap->b_ops = ops; bitmap->b_regions = RB_ROOT; bitmap->b_private = private_data; if (description) { ret = ocfs2_malloc0(sizeof(char) * (strlen(description) + 1), &bitmap->b_description); if (ret) goto out_free; strcpy(bitmap->b_description, description); } *ret_bitmap = bitmap; return 0; out_free: ocfs2_free(&bitmap); return ret; } static size_t ocfs2_align_total(int total_bits) { return (total_bits + 7) / 8; } errcode_t ocfs2_bitmap_alloc_region(ocfs2_bitmap *bitmap, uint64_t start_bit, int bitmap_start, int total_bits, struct ocfs2_bitmap_region **ret_br) { errcode_t ret; struct ocfs2_bitmap_region *br; if (total_bits < 0) return OCFS2_ET_INVALID_BIT; ret = ocfs2_malloc0(sizeof(struct ocfs2_bitmap_region), &br); if (ret) return ret; br->br_start_bit = start_bit; br->br_bitmap_start = bitmap_start; br->br_valid_bits = total_bits; br->br_total_bits = total_bits + bitmap_start; br->br_bytes = ocfs2_align_total(br->br_total_bits); ret = ocfs2_malloc0(br->br_bytes, &br->br_bitmap); if (ret) ocfs2_free(&br); else *ret_br = br; return ret; } void ocfs2_bitmap_free_region(struct ocfs2_bitmap_region *br) { if (br->br_bitmap) ocfs2_free(&br->br_bitmap); ocfs2_free(&br); } errcode_t ocfs2_bitmap_realloc_region(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *br, int total_bits) { errcode_t ret; size_t new_bytes; if ((br->br_start_bit + total_bits) > bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; new_bytes = ocfs2_align_total(total_bits + br->br_bitmap_start); if (new_bytes > br->br_bytes) { ret = ocfs2_realloc0(new_bytes, &br->br_bitmap, br->br_bytes); if (ret) return ret; br->br_bytes = new_bytes; } br->br_valid_bits = total_bits; br->br_total_bits = total_bits + br->br_bitmap_start; return 0; } errcode_t ocfs2_bitmap_foreach_region(ocfs2_bitmap *bitmap, ocfs2_bitmap_foreach_func func, void *private_data) { struct ocfs2_bitmap_region *br; struct rb_node *node; errcode_t ret = 0; for (node = rb_first(&bitmap->b_regions); node; node = rb_next(node)) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); ret = func(br, private_data); if (ret == OCFS2_ET_ITERATION_COMPLETE) { ret = 0; break; } if (ret) break; } return ret; } /* * Attempt to merge two regions. If the merge is successful, 0 will * be returned and prev will be the only valid region. Next will * be freed. */ static errcode_t ocfs2_bitmap_merge_region(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *prev, struct ocfs2_bitmap_region *next) { errcode_t ret; uint64_t new_bits; size_t prev_bytes; uint8_t *pbm, *nbm, offset, diff; if ((prev->br_start_bit + prev->br_valid_bits) != next->br_start_bit) return OCFS2_ET_INVALID_BIT; if (bitmap->b_ops->merge_region && !(*bitmap->b_ops->merge_region)(bitmap, prev, next)) return OCFS2_ET_INVALID_BIT; /* XXX: We don't support merge if the bitmap isn't aligned now. */ if (prev->br_bitmap_start || next->br_bitmap_start) return OCFS2_ET_INVALID_BIT; new_bits = (uint64_t)(prev->br_total_bits) + (uint64_t)(next->br_total_bits); if (new_bits > INT_MAX) return OCFS2_ET_INVALID_BIT; /* grab before realloc changes them */ prev_bytes = prev->br_bytes; offset = prev->br_total_bits % 8; ret = ocfs2_bitmap_realloc_region(bitmap, prev, (int)new_bits); if (ret) return ret; /* if prev's last bit ends on a byte boundary then we can just * copy everything over */ if (offset == 0) { memcpy(prev->br_bitmap + prev_bytes, next->br_bitmap, next->br_bytes); goto done; } /* otherwise we have to shift next in. we're about to free * next, so we consume it as we go. */ pbm = &prev->br_bitmap[prev_bytes - 1]; nbm = &next->br_bitmap[0]; diff = 8 - offset; while(next->br_bytes-- && next->br_total_bits > 0) { /* mask off just the offset bytes in the prev */ *pbm &= ((1 << offset) - 1); /* move 'diff' LSB from next into prevs MSB */ *pbm |= (*nbm & ((1 << diff) - 1)) << offset; pbm++; next->br_total_bits -= diff; if (next->br_total_bits > 0) { /* then set prev's LSB to the next offset MSB. this relies * on 0s being shifted into the MSB */ *pbm = *nbm >> diff; nbm++; next->br_total_bits -= offset; } } done: prev->br_set_bits = prev->br_set_bits + next->br_set_bits; rb_erase(&next->br_node, &bitmap->b_regions); ocfs2_bitmap_free_region(next); return 0; } /* * Find a bitmap_region in the tree that intersects the bit region * that is passed in. * * _p and _parent are set so that callers can use rb_link_node and * rb_insert_color to insert a node after finding that their bit * wasn't found. * * _next is only used if a bitmap_region isn't found. it is set * to the next node in the tree greater than the bitmap range * that was searched. */ static struct ocfs2_bitmap_region *ocfs2_bitmap_lookup(ocfs2_bitmap *bitmap, uint64_t bitno, uint64_t total_bits, struct rb_node ***ret_p, struct rb_node **ret_parent, struct rb_node **ret_next) { struct rb_node **p = &bitmap->b_regions.rb_node; struct rb_node *parent = NULL, *last_left = NULL; struct ocfs2_bitmap_region *br = NULL; while (*p) { parent = *p; br = rb_entry(parent, struct ocfs2_bitmap_region, br_node); if (bitno + total_bits <= br->br_start_bit) { last_left = parent; p = &(*p)->rb_left; br = NULL; } else if (bitno >= (br->br_start_bit + br->br_valid_bits)) { p = &(*p)->rb_right; br = NULL; } else break; } if (ret_p != NULL) *ret_p = p; if (ret_parent != NULL) *ret_parent = parent; if (br == NULL && ret_next != NULL) *ret_next = last_left; return br; } errcode_t ocfs2_bitmap_insert_region(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *br) { struct ocfs2_bitmap_region *br_tmp; struct rb_node **p, *parent, *node; if (br->br_start_bit > bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; /* we shouldn't find an existing region that intersects our new one */ br_tmp = ocfs2_bitmap_lookup(bitmap, br->br_start_bit, br->br_valid_bits, &p, &parent, NULL); if (br_tmp) return OCFS2_ET_INVALID_BIT; rb_link_node(&br->br_node, parent, p); rb_insert_color(&br->br_node, &bitmap->b_regions); /* try to merge our new extent with its neighbours in the tree */ node = rb_prev(&br->br_node); if (node) { br_tmp = rb_entry(node, struct ocfs2_bitmap_region, br_node); ocfs2_bitmap_merge_region(bitmap, br_tmp, br); br = br_tmp; } node = rb_next(&br->br_node); if (node != NULL) { br_tmp = rb_entry(node, struct ocfs2_bitmap_region, br_node); ocfs2_bitmap_merge_region(bitmap, br, br_tmp); } return 0; } static int set_generic_shared(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *br, uint64_t bitno) { int old_tmp; old_tmp = ocfs2_set_bit(bitno - br->br_start_bit + br->br_bitmap_start, br->br_bitmap); if (!old_tmp) { br->br_set_bits++; if (bitmap->b_ops->bit_change_notify) (*bitmap->b_ops->bit_change_notify)(bitmap, br, bitno, 1); } return old_tmp; } /* * Helper functions for the most generic of bitmaps. If there is no * memory allocated for the bit, it fails. */ errcode_t ocfs2_bitmap_set_generic(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval) { struct ocfs2_bitmap_region *br; int old_tmp; br = ocfs2_bitmap_lookup(bitmap, bitno, 1, NULL, NULL, NULL); if (!br) return OCFS2_ET_INVALID_BIT; old_tmp = set_generic_shared(bitmap, br, bitno); if (oldval) *oldval = old_tmp; return 0; } static int clear_generic_shared(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *br, uint64_t bitno) { int old_tmp; old_tmp = ocfs2_clear_bit(bitno - br->br_start_bit + br->br_bitmap_start, br->br_bitmap); if (old_tmp) { br->br_set_bits--; if (bitmap->b_ops->bit_change_notify) (*bitmap->b_ops->bit_change_notify)(bitmap, br, bitno, 0); } return old_tmp; } errcode_t ocfs2_bitmap_clear_generic(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval) { struct ocfs2_bitmap_region *br; int old_tmp; br = ocfs2_bitmap_lookup(bitmap, bitno, 1, NULL, NULL, NULL); if (!br) return OCFS2_ET_INVALID_BIT; old_tmp = clear_generic_shared(bitmap, br, bitno); if (oldval) *oldval = old_tmp; return 0; } errcode_t ocfs2_bitmap_test_generic(ocfs2_bitmap *bitmap, uint64_t bitno, int *val) { struct ocfs2_bitmap_region *br; br = ocfs2_bitmap_lookup(bitmap, bitno, 1, NULL, NULL, NULL); if (!br) return OCFS2_ET_INVALID_BIT; *val = ocfs2_test_bit(bitno - br->br_start_bit + br->br_bitmap_start, br->br_bitmap) ? 1 : 0; return 0; } errcode_t ocfs2_bitmap_find_next_set_generic(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found) { struct ocfs2_bitmap_region *br; struct rb_node *node = NULL; int offset, ret; /* start from either the node whose's br contains the bit or * the next greatest node in the tree */ br = ocfs2_bitmap_lookup(bitmap, start, 1, NULL, NULL, &node); if (br) node = &br->br_node; for (; node != NULL; node = rb_next(node)) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); if (start > br->br_start_bit) offset = start - br->br_start_bit; else offset = 0; ret = ocfs2_find_next_bit_set(br->br_bitmap, br->br_total_bits, offset + br->br_bitmap_start); if (ret != br->br_total_bits) { *found = br->br_start_bit + ret - br->br_bitmap_start; return 0; } } return OCFS2_ET_BIT_NOT_FOUND; } errcode_t ocfs2_bitmap_find_next_clear_generic(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found) { struct ocfs2_bitmap_region *br; struct rb_node *node = NULL; int offset, ret; /* start from either the node whose's br contains the bit or * the next greatest node in the tree */ br = ocfs2_bitmap_lookup(bitmap, start, 1, NULL, NULL, &node); if (br) node = &br->br_node; for (; node != NULL; node = rb_next(node)) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); if (start > br->br_start_bit) offset = start - br->br_start_bit; else offset = 0; ret = ocfs2_find_next_bit_clear(br->br_bitmap, br->br_total_bits, offset + br->br_bitmap_start); if (ret != br->br_total_bits) { *found = br->br_start_bit + ret - br->br_bitmap_start; return 0; } } return OCFS2_ET_BIT_NOT_FOUND; } struct alloc_range_args { ocfs2_bitmap *ar_bitmap; uint64_t ar_min_len; uint64_t ar_len; uint64_t ar_first_bit; uint64_t ar_bits_found; errcode_t ar_ret; }; /* Our strategy here to aid discontiguous allocation is to track the * largest free regions (which still fit within ar_min_len) and if the * max allocation fails, fall back to returning one of those. */ static errcode_t alloc_range_func(struct ocfs2_bitmap_region *br, void *private_data) { struct alloc_range_args *ar = private_data; errcode_t ret = 0; uint64_t best_start = UINT64_MAX, best_len = 0; int start, end; if ((br->br_valid_bits - br->br_set_bits) < ar->ar_min_len) goto out; for (start = br->br_bitmap_start; start + ar->ar_min_len <= br->br_total_bits;) { start = ocfs2_find_next_bit_clear(br->br_bitmap, br->br_total_bits, start); if (start == br->br_total_bits) break; /* avoiding start + 1 here so that start at total_bits - 1 * just works out */ end = ocfs2_find_next_bit_set(br->br_bitmap, br->br_total_bits, start); /* We've found a region large enough to hold our max. */ if ((end - start) >= ar->ar_len) { end = start + ar->ar_len; goto found; } if ((end - start) > best_len) { best_len = end - start; best_start = start; } start = end + 1; } /* Nothing found at all */ if (best_start == UINT64_MAX || best_len < ar->ar_min_len) goto out; /* Best fit works */ start = best_start; end = best_start + best_len; found: ar->ar_first_bit = br->br_start_bit + start - br->br_bitmap_start; ar->ar_bits_found = end - start; for (; start < end; start++) set_generic_shared(ar->ar_bitmap, br, br->br_start_bit + start - br->br_bitmap_start); ar->ar_ret = 0; ret = OCFS2_ET_ITERATION_COMPLETE; out: return ret; } errcode_t ocfs2_bitmap_alloc_range_generic(ocfs2_bitmap *bitmap, uint64_t min_len, uint64_t len, uint64_t *first_bit, uint64_t *bits_found) { errcode_t ret; struct alloc_range_args ar = { .ar_bitmap = bitmap, .ar_min_len = min_len, .ar_len = len, .ar_ret = OCFS2_ET_BIT_NOT_FOUND, }; ret = ocfs2_bitmap_foreach_region(bitmap, alloc_range_func, &ar); if (ret == 0) ret = ar.ar_ret; if (ret == 0) { *first_bit = ar.ar_first_bit; *bits_found = ar.ar_bits_found; } return ret; } errcode_t ocfs2_bitmap_clear_range_generic(ocfs2_bitmap *bitmap, uint64_t len, uint64_t first_bit) { struct ocfs2_bitmap_region *br; uint64_t end; br = ocfs2_bitmap_lookup(bitmap, first_bit, len, NULL, NULL, NULL); if (!br) return OCFS2_ET_INVALID_BIT; for (end = first_bit + len; first_bit < end; first_bit++) clear_generic_shared(bitmap, br, first_bit); return 0; } /* * Helper functions for a bitmap with holes in it. * If a bit doesn't have memory allocated for it, we allocate. */ errcode_t ocfs2_bitmap_set_holes(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval) { errcode_t ret; struct ocfs2_bitmap_region *br; if (!ocfs2_bitmap_set_generic(bitmap, bitno, oldval)) return 0; ret = ocfs2_bitmap_alloc_region(bitmap, bitno, 0, 1, &br); if (ret) return ret; ret = ocfs2_bitmap_insert_region(bitmap, br); if (ret) return ret; return ocfs2_bitmap_set_generic(bitmap, bitno, oldval); } errcode_t ocfs2_bitmap_clear_holes(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval) { errcode_t ret; struct ocfs2_bitmap_region *br; if (!ocfs2_bitmap_clear_generic(bitmap, bitno, oldval)) return 0; ret = ocfs2_bitmap_alloc_region(bitmap, bitno, 0, 1, &br); if (ret) return ret; ret = ocfs2_bitmap_insert_region(bitmap, br); return ret; } errcode_t ocfs2_bitmap_test_holes(ocfs2_bitmap *bitmap, uint64_t bitno, int *val) { if (ocfs2_bitmap_test_generic(bitmap, bitno, val)) *val = 0; return 0; } errcode_t ocfs2_bitmap_find_next_set_holes(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found) { return ocfs2_bitmap_find_next_set_generic(bitmap, start, found); } errcode_t ocfs2_bitmap_find_next_clear_holes(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found) { struct ocfs2_bitmap_region *br; struct rb_node *node = NULL; uint64_t seen; int offset, ret; /* start from either the node whose's br contains the bit or * the next greatest node in the tree */ br = ocfs2_bitmap_lookup(bitmap, start, 1, NULL, NULL, &node); if (br) node = &br->br_node; else if (!node) { /* There was nothing past start */ *found = start; return 0; } seen = start; for (; node != NULL; node = rb_next(node)) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); /* Did we find a hole? */ if (seen < br->br_start_bit) { *found = seen; return 0; } if (start > br->br_start_bit) offset = start - br->br_start_bit; else offset = 0; ret = ocfs2_find_next_bit_clear(br->br_bitmap, br->br_total_bits, offset + br->br_bitmap_start); if (ret != br->br_total_bits) { *found = br->br_start_bit + ret - br->br_bitmap_start; return 0; } seen = br->br_start_bit + br->br_valid_bits; } return OCFS2_ET_BIT_NOT_FOUND; } static struct ocfs2_bitmap_operations global_cluster_ops = { .set_bit = ocfs2_bitmap_set_generic, .clear_bit = ocfs2_bitmap_clear_generic, .test_bit = ocfs2_bitmap_test_generic, .find_next_set = ocfs2_bitmap_find_next_set_generic, .find_next_clear = ocfs2_bitmap_find_next_clear_generic, .alloc_range = ocfs2_bitmap_alloc_range_generic, .clear_range = ocfs2_bitmap_clear_range_generic, }; errcode_t ocfs2_cluster_bitmap_new(ocfs2_filesys *fs, const char *description, ocfs2_bitmap **ret_bitmap) { errcode_t ret; ocfs2_bitmap *bitmap; uint64_t max_bits, num_bits, bitoff, alloc_bits; struct ocfs2_bitmap_region *br; num_bits = fs->fs_clusters; ret = ocfs2_bitmap_new(fs, num_bits, description ? description : "Generic cluster bitmap", &global_cluster_ops, NULL, &bitmap); if (ret) return ret; bitoff = 0; max_bits = INT_MAX - (fs->fs_clustersize - 1); while (bitoff < num_bits) { alloc_bits = num_bits; if (num_bits > max_bits) alloc_bits = max_bits; ret = ocfs2_bitmap_alloc_region(bitmap, bitoff, 0, alloc_bits, &br); if (ret) { ocfs2_bitmap_free(bitmap); return ret; } ret = ocfs2_bitmap_insert_region(bitmap, br); if (ret) { ocfs2_bitmap_free_region(br); ocfs2_bitmap_free(bitmap); return ret; } bitoff += alloc_bits; } *ret_bitmap = bitmap; return 0; } static struct ocfs2_bitmap_operations global_block_ops = { .set_bit = ocfs2_bitmap_set_holes, .clear_bit = ocfs2_bitmap_clear_holes, .test_bit = ocfs2_bitmap_test_holes, .find_next_set = ocfs2_bitmap_find_next_set_holes, .find_next_clear = ocfs2_bitmap_find_next_clear_holes, /* XXX can't allocate a range yet, would need to fill holes and merge * with adjacent */ }; errcode_t ocfs2_block_bitmap_new(ocfs2_filesys *fs, const char *description, ocfs2_bitmap **ret_bitmap) { errcode_t ret; ocfs2_bitmap *bitmap; ret = ocfs2_bitmap_new(fs, fs->fs_blocks, description ? description : "Generic block bitmap", &global_block_ops, NULL, &bitmap); if (ret) return ret; *ret_bitmap = bitmap; return 0; } #ifdef DEBUG_EXE #include #include #include static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "debug_bitmap [-a] \n"); } extern int opterr, optind; extern char *optarg; static void dump_regions(ocfs2_bitmap *bitmap) { struct ocfs2_bitmap_region *br; struct rb_node *node; fprintf(stdout, "Bitmap \"%s\": total = %"PRIu64", set = %"PRIu64"\n", bitmap->b_description, bitmap->b_total_bits, bitmap->b_set_bits); for (node = rb_first(&bitmap->b_regions);node; node = rb_next(node)) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); fprintf(stdout, "(start: %"PRIu64", n: %d, set: %d)\n", br->br_start_bit, br->br_valid_bits, br->br_set_bits); } } static void print_bitmap(ocfs2_bitmap *bitmap) { uint64_t bitno; uint64_t gap_start = 0; /* GCC is dumb */ errcode_t ret; int val, gap; gap = 0; for (bitno = 0; bitno < bitmap->b_total_bits; bitno++) { ret = ocfs2_bitmap_test(bitmap, bitno, &val); if (ret) { if (ret == OCFS2_ET_INVALID_BIT) { if (!gap) { gap = 1; gap_start = bitno; } continue; } com_err("print_bitmap", ret, "while testing bit %"PRIu64"\n", bitno); break; } if (gap) { fprintf(stdout, "\nGap of length %"PRIu64" at %"PRIu64"\n", bitno - gap_start, gap_start); gap = bitno % 72; gap += gap / 8; for (; gap; gap--) fprintf(stdout, " "); fflush(stdout); } else { if (bitno && !(bitno % 72)) fprintf(stdout, "\n"); else if (bitno && !(bitno % 8)) fprintf(stdout, " "); } fprintf(stdout, "%d", val); fflush(stdout); } if ((bitno - 1) % 72) fprintf(stdout, "\n"); } static int try_op(ocfs2_bitmap *bitmap, errcode_t (*func)(ocfs2_bitmap *bitmap, uint64_t bitno, int *val), char *bit_val, int *ret_val) { errcode_t ret; uint64_t bitno; char *ptr; if (!bit_val) { fprintf(stderr, "You must provide a bit offset\n"); return 1; } bitno = read_number(bit_val); if (!bitno) { for (ptr = bit_val; *ptr; ptr++) { if (*ptr != '0') break; } if ((ptr == bit_val) || *ptr) { fprintf(stderr, "Invalid bit offset: %s\n", bit_val); return 1; } } ret = (*func)(bitmap, bitno, ret_val); if (ret) { com_err("try_op", ret, "while setting bit %"PRIu64"\n", bitno); return 1; } return 0; } static int try_op64(ocfs2_bitmap *bitmap, errcode_t (*func)(ocfs2_bitmap *bitmap, uint64_t bitno, uint64_t *val), char *bit_val, uint64_t *ret_val) { errcode_t ret; uint64_t bitno; char *ptr; if (!bit_val) { fprintf(stderr, "You must provide a bit offset\n"); return 1; } bitno = read_number(bit_val); if (!bitno) { for (ptr = bit_val; *ptr; ptr++) { if (*ptr != '0') break; } if ((ptr == bit_val) || *ptr) { fprintf(stderr, "Invalid bit offset: %s\n", bit_val); return 1; } } ret = (*func)(bitmap, bitno, ret_val); if (ret) { com_err("try_op64", ret, "while setting bit %"PRIu64"\n", bitno); return 1; } return 0; } static void run_test(ocfs2_bitmap *bitmap) { char buf[256]; char *ptr, *cmd; uint64_t val64; int val; while (1) { fprintf(stdout, "Command: "); fflush(stdout); if (!fgets(buf, sizeof(buf), stdin)) break; ptr = buf + strlen(buf) - 1; if (*ptr == '\n') *ptr = '\0'; for (cmd = buf; (*cmd == ' ') || (*cmd == '\t'); cmd++); if (!(*cmd)) continue; ptr = strchr(cmd, ' '); if (ptr) { *ptr = '\0'; ptr++; } if (!strcmp(cmd, "set")) { try_op(bitmap, ocfs2_bitmap_set, ptr, NULL); } else if (!strcmp(cmd, "clear")) { try_op(bitmap, ocfs2_bitmap_clear, ptr, NULL); } else if (!strcmp(cmd, "test")) { if (!try_op(bitmap, ocfs2_bitmap_test, ptr, &val)) { fprintf(stdout, "Bit %s is %s\n", ptr, val ? "set" : "clear"); } } else if (!strcmp(cmd, "fns")) { if (!try_op64(bitmap, ocfs2_bitmap_find_next_set, ptr, &val64)) { fprintf(stdout, "Found %"PRIu64"\n", val64); } } else if (!strcmp(cmd, "fnc")) { if (!try_op64(bitmap, ocfs2_bitmap_find_next_clear, ptr, &val64)) { fprintf(stdout, "Found %"PRIu64"\n", val64); } } else if (!strcmp(cmd, "print")) { print_bitmap(bitmap); } else if (!strcmp(cmd, "dump")) { dump_regions(bitmap); } else if (!strcmp(cmd, "quit")) { break; } else { fprintf(stderr, "Invalid command: \"%s\"\n", cmd); } } } int main(int argc, char *argv[]) { errcode_t ret; int c; int alloc = 0; char *filename; ocfs2_filesys *fs; ocfs2_bitmap *bitmap; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "a")) != EOF) { switch (c) { case 'a': alloc = 1; break; default: print_usage(); return 1; break; } } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); return 1; } if (alloc) ret = ocfs2_block_bitmap_new(fs, "Testing", &bitmap); else ret = ocfs2_cluster_bitmap_new(fs, "Testing", &bitmap); if (ret) { com_err(argv[0], ret, "while creating bitmap"); goto out_close; } run_test(bitmap); ocfs2_bitmap_free(bitmap); out_close: ocfs2_close(fs); return ret; } #endif /* DEBUG_EXE */