scoutfs: add sparse bitmap library

Add a quick library for maintaining a very large bitmap with sparse
allocation.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2018-08-07 12:53:40 -07:00
committed by Zach Brown
parent 1ed0c6017f
commit 00adbd31be
3 changed files with 169 additions and 1 deletions

View File

@@ -8,7 +8,7 @@ CFLAGS_scoutfs_trace.o = -I$(src) # define_trace.h double include
scoutfs-y += bio.o block.o btree.o client.o compact.o counters.o data.o dir.o \
export.o extents.o file.o inode.o ioctl.o item.o lock.o \
manifest.o msg.o net.o options.o per_task.o seg.o server.o \
scoutfs_trace.o sort_priv.o super.o sysfs.o trans.o \
scoutfs_trace.o sort_priv.o spbm.o super.o sysfs.o trans.o \
triggers.o tseq.o xattr.o
#

153
kmod/src/spbm.c Normal file
View File

@@ -0,0 +1,153 @@
/*
* Copyright (C) 2018 Versity Software, Inc. 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 v2 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.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/log2.h>
#include <linux/bitmap.h>
#include <linux/rbtree.h>
#include "spbm.h"
#define SPBM_BITS 128
#define SPBM_SHIFT ilog2(SPBM_BITS)
#define SPBM_MASK ((u64)SPBM_BITS - 1)
#define SPBM_LONGS (SPBM_BITS / BITS_PER_LONG)
/*
* Maintain a sparse bitmap in an rbtree. Setting bits can allocate and
* fail but clearing will always succeed. Locking is left up to the
* caller.
*/
struct spbm_node {
struct rb_node node;
u64 index;
unsigned long bits[SPBM_LONGS];
};
void scoutfs_spbm_init(struct scoutfs_spbm *spbm)
{
BUILD_BUG_ON(!is_power_of_2(SPBM_BITS));
spbm->root = RB_ROOT;
}
enum {
/* if a node isn't found then return an allocated new node */
SPBM_FIND_ALLOC = 0x1,
};
static struct spbm_node *find_node(struct scoutfs_spbm *spbm, u64 index,
int flags)
{
struct rb_node *parent;
struct rb_node **node;
struct spbm_node *sn;
node = &spbm->root.rb_node;
parent = NULL;
sn = NULL;
while (*node) {
parent = *node;
sn = container_of(*node, struct spbm_node, node);
if (index < sn->index) {
node = &(*node)->rb_left;
} else if (index > sn->index) {
node = &(*node)->rb_right;
} else {
break;
}
sn = NULL;
}
if (!sn && (flags & SPBM_FIND_ALLOC)) {
sn = kzalloc(sizeof(struct spbm_node), GFP_NOFS);
if (sn) {
sn->index = index;
rb_link_node(&sn->node, parent, node);
rb_insert_color(&sn->node, &spbm->root);
}
}
return sn;
}
static void calc_index_nr(u64 *index, int *nr, u64 bit)
{
*index = bit >> SPBM_SHIFT;
*nr = bit & SPBM_MASK;
}
int scoutfs_spbm_set(struct scoutfs_spbm *spbm, u64 bit)
{
struct spbm_node *sn;
u64 index;
int nr;
calc_index_nr(&index, &nr, bit);
sn = find_node(spbm, index, SPBM_FIND_ALLOC);
if (!sn)
return -ENOMEM;
set_bit(nr, sn->bits);
return 0;
}
int scoutfs_spbm_test(struct scoutfs_spbm *spbm, u64 bit)
{
struct spbm_node *sn;
u64 index;
int nr;
calc_index_nr(&index, &nr, bit);
sn = find_node(spbm, index, 0);
if (sn)
return !!test_bit(nr, sn->bits);
return 0;
}
static void free_node(struct scoutfs_spbm *spbm, struct spbm_node *sn)
{
rb_erase(&sn->node, &spbm->root);
kfree(sn);
}
void scoutfs_spbm_clear(struct scoutfs_spbm *spbm, u64 bit)
{
struct spbm_node *sn;
u64 index;
int nr;
calc_index_nr(&index, &nr, bit);
sn = find_node(spbm, index, 0);
if (sn) {
clear_bit(nr, sn->bits);
if (bitmap_empty(sn->bits, SPBM_BITS))
free_node(spbm, sn);
}
}
void scoutfs_spbm_destroy(struct scoutfs_spbm *spbm)
{
struct spbm_node *sn;
struct spbm_node *pos;
rbtree_postorder_for_each_entry_safe(sn, pos, &spbm->root, node)
free_node(spbm, sn);
}

15
kmod/src/spbm.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef _SCOUTFS_SPBM_H_
#define _SCOUTFS_SPBM_H_
struct scoutfs_spbm {
struct rb_root root;
};
void scoutfs_spbm_init(struct scoutfs_spbm *spbm);
void scoutfs_spbm_destroy(struct scoutfs_spbm *spbm);
int scoutfs_spbm_set(struct scoutfs_spbm *spbm, u64 bit);
int scoutfs_spbm_test(struct scoutfs_spbm *spbm, u64 bit);
void scoutfs_spbm_clear(struct scoutfs_spbm *spbm, u64 bit);
#endif