-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a persistent bitset as a wrapper around dm-array. Signed-off-by: Joe Thornber <ejt@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com>
- Loading branch information
Joe Thornber
authored and
Alasdair G Kergon
committed
Mar 1, 2013
1 parent
6513c29
commit 7a87edf
Showing
3 changed files
with
329 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
/* | ||
* Copyright (C) 2012 Red Hat, Inc. | ||
* | ||
* This file is released under the GPL. | ||
*/ | ||
|
||
#include "dm-bitset.h" | ||
#include "dm-transaction-manager.h" | ||
|
||
#include <linux/export.h> | ||
#include <linux/device-mapper.h> | ||
|
||
#define DM_MSG_PREFIX "bitset" | ||
#define BITS_PER_ARRAY_ENTRY 64 | ||
|
||
/*----------------------------------------------------------------*/ | ||
|
||
static struct dm_btree_value_type bitset_bvt = { | ||
.context = NULL, | ||
.size = sizeof(__le64), | ||
.inc = NULL, | ||
.dec = NULL, | ||
.equal = NULL, | ||
}; | ||
|
||
/*----------------------------------------------------------------*/ | ||
|
||
void dm_disk_bitset_init(struct dm_transaction_manager *tm, | ||
struct dm_disk_bitset *info) | ||
{ | ||
dm_array_info_init(&info->array_info, tm, &bitset_bvt); | ||
info->current_index_set = false; | ||
} | ||
EXPORT_SYMBOL_GPL(dm_disk_bitset_init); | ||
|
||
int dm_bitset_empty(struct dm_disk_bitset *info, dm_block_t *root) | ||
{ | ||
return dm_array_empty(&info->array_info, root); | ||
} | ||
EXPORT_SYMBOL_GPL(dm_bitset_empty); | ||
|
||
int dm_bitset_resize(struct dm_disk_bitset *info, dm_block_t root, | ||
uint32_t old_nr_entries, uint32_t new_nr_entries, | ||
bool default_value, dm_block_t *new_root) | ||
{ | ||
uint32_t old_blocks = dm_div_up(old_nr_entries, BITS_PER_ARRAY_ENTRY); | ||
uint32_t new_blocks = dm_div_up(new_nr_entries, BITS_PER_ARRAY_ENTRY); | ||
__le64 value = default_value ? cpu_to_le64(~0) : cpu_to_le64(0); | ||
|
||
__dm_bless_for_disk(&value); | ||
return dm_array_resize(&info->array_info, root, old_blocks, new_blocks, | ||
&value, new_root); | ||
} | ||
EXPORT_SYMBOL_GPL(dm_bitset_resize); | ||
|
||
int dm_bitset_del(struct dm_disk_bitset *info, dm_block_t root) | ||
{ | ||
return dm_array_del(&info->array_info, root); | ||
} | ||
EXPORT_SYMBOL_GPL(dm_bitset_del); | ||
|
||
int dm_bitset_flush(struct dm_disk_bitset *info, dm_block_t root, | ||
dm_block_t *new_root) | ||
{ | ||
int r; | ||
__le64 value; | ||
|
||
if (!info->current_index_set) | ||
return 0; | ||
|
||
value = cpu_to_le64(info->current_bits); | ||
|
||
__dm_bless_for_disk(&value); | ||
r = dm_array_set_value(&info->array_info, root, info->current_index, | ||
&value, new_root); | ||
if (r) | ||
return r; | ||
|
||
info->current_index_set = false; | ||
return 0; | ||
} | ||
EXPORT_SYMBOL_GPL(dm_bitset_flush); | ||
|
||
static int read_bits(struct dm_disk_bitset *info, dm_block_t root, | ||
uint32_t array_index) | ||
{ | ||
int r; | ||
__le64 value; | ||
|
||
r = dm_array_get_value(&info->array_info, root, array_index, &value); | ||
if (r) | ||
return r; | ||
|
||
info->current_bits = le64_to_cpu(value); | ||
info->current_index_set = true; | ||
info->current_index = array_index; | ||
return 0; | ||
} | ||
|
||
static int get_array_entry(struct dm_disk_bitset *info, dm_block_t root, | ||
uint32_t index, dm_block_t *new_root) | ||
{ | ||
int r; | ||
unsigned array_index = index / BITS_PER_ARRAY_ENTRY; | ||
|
||
if (info->current_index_set) { | ||
if (info->current_index == array_index) | ||
return 0; | ||
|
||
r = dm_bitset_flush(info, root, new_root); | ||
if (r) | ||
return r; | ||
} | ||
|
||
return read_bits(info, root, array_index); | ||
} | ||
|
||
int dm_bitset_set_bit(struct dm_disk_bitset *info, dm_block_t root, | ||
uint32_t index, dm_block_t *new_root) | ||
{ | ||
int r; | ||
unsigned b = index % BITS_PER_ARRAY_ENTRY; | ||
|
||
r = get_array_entry(info, root, index, new_root); | ||
if (r) | ||
return r; | ||
|
||
set_bit(b, (unsigned long *) &info->current_bits); | ||
return 0; | ||
} | ||
EXPORT_SYMBOL_GPL(dm_bitset_set_bit); | ||
|
||
int dm_bitset_clear_bit(struct dm_disk_bitset *info, dm_block_t root, | ||
uint32_t index, dm_block_t *new_root) | ||
{ | ||
int r; | ||
unsigned b = index % BITS_PER_ARRAY_ENTRY; | ||
|
||
r = get_array_entry(info, root, index, new_root); | ||
if (r) | ||
return r; | ||
|
||
clear_bit(b, (unsigned long *) &info->current_bits); | ||
return 0; | ||
} | ||
EXPORT_SYMBOL_GPL(dm_bitset_clear_bit); | ||
|
||
int dm_bitset_test_bit(struct dm_disk_bitset *info, dm_block_t root, | ||
uint32_t index, dm_block_t *new_root, bool *result) | ||
{ | ||
int r; | ||
unsigned b = index % BITS_PER_ARRAY_ENTRY; | ||
|
||
r = get_array_entry(info, root, index, new_root); | ||
if (r) | ||
return r; | ||
|
||
*result = test_bit(b, (unsigned long *) &info->current_bits); | ||
return 0; | ||
} | ||
EXPORT_SYMBOL_GPL(dm_bitset_test_bit); | ||
|
||
/*----------------------------------------------------------------*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
/* | ||
* Copyright (C) 2012 Red Hat, Inc. | ||
* | ||
* This file is released under the GPL. | ||
*/ | ||
#ifndef _LINUX_DM_BITSET_H | ||
#define _LINUX_DM_BITSET_H | ||
|
||
#include "dm-array.h" | ||
|
||
/*----------------------------------------------------------------*/ | ||
|
||
/* | ||
* This bitset type is a thin wrapper round a dm_array of 64bit words. It | ||
* uses a tiny, one word cache to reduce the number of array lookups and so | ||
* increase performance. | ||
* | ||
* Like the dm-array that it's based on, the caller needs to keep track of | ||
* the size of the bitset separately. The underlying dm-array implicitly | ||
* knows how many words it's storing and will return -ENODATA if you try | ||
* and access an out of bounds word. However, an out of bounds bit in the | ||
* final word will _not_ be detected, you have been warned. | ||
* | ||
* Bits are indexed from zero. | ||
* Typical use: | ||
* | ||
* a) Initialise a dm_disk_bitset structure with dm_disk_bitset_init(). | ||
* This describes the bitset and includes the cache. It's not called it | ||
* dm_bitset_info in line with other data structures because it does | ||
* include instance data. | ||
* | ||
* b) Get yourself a root. The root is the index of a block of data on the | ||
* disk that holds a particular instance of an bitset. You may have a | ||
* pre existing root in your metadata that you wish to use, or you may | ||
* want to create a brand new, empty bitset with dm_bitset_empty(). | ||
* | ||
* Like the other data structures in this library, dm_bitset objects are | ||
* immutable between transactions. Update functions will return you the | ||
* root for a _new_ array. If you've incremented the old root, via | ||
* dm_tm_inc(), before calling the update function you may continue to use | ||
* it in parallel with the new root. | ||
* | ||
* Even read operations may trigger the cache to be flushed and as such | ||
* return a root for a new, updated bitset. | ||
* | ||
* c) resize a bitset with dm_bitset_resize(). | ||
* | ||
* d) Set a bit with dm_bitset_set_bit(). | ||
* | ||
* e) Clear a bit with dm_bitset_clear_bit(). | ||
* | ||
* f) Test a bit with dm_bitset_test_bit(). | ||
* | ||
* g) Flush all updates from the cache with dm_bitset_flush(). | ||
* | ||
* h) Destroy the bitset with dm_bitset_del(). This tells the transaction | ||
* manager that you're no longer using this data structure so it can | ||
* recycle it's blocks. (dm_bitset_dec() would be a better name for it, | ||
* but del is in keeping with dm_btree_del()). | ||
*/ | ||
|
||
/* | ||
* Opaque object. Unlike dm_array_info, you should have one of these per | ||
* bitset. Initialise with dm_disk_bitset_init(). | ||
*/ | ||
struct dm_disk_bitset { | ||
struct dm_array_info array_info; | ||
|
||
uint32_t current_index; | ||
uint64_t current_bits; | ||
|
||
bool current_index_set:1; | ||
}; | ||
|
||
/* | ||
* Sets up a dm_disk_bitset structure. You don't need to do anything with | ||
* this structure when you finish using it. | ||
* | ||
* tm - the transaction manager that should supervise this structure | ||
* info - the structure being initialised | ||
*/ | ||
void dm_disk_bitset_init(struct dm_transaction_manager *tm, | ||
struct dm_disk_bitset *info); | ||
|
||
/* | ||
* Create an empty, zero length bitset. | ||
* | ||
* info - describes the bitset | ||
* new_root - on success, points to the new root block | ||
*/ | ||
int dm_bitset_empty(struct dm_disk_bitset *info, dm_block_t *new_root); | ||
|
||
/* | ||
* Resize the bitset. | ||
* | ||
* info - describes the bitset | ||
* old_root - the root block of the array on disk | ||
* old_nr_entries - the number of bits in the old bitset | ||
* new_nr_entries - the number of bits you want in the new bitset | ||
* default_value - the value for any new bits | ||
* new_root - on success, points to the new root block | ||
*/ | ||
int dm_bitset_resize(struct dm_disk_bitset *info, dm_block_t old_root, | ||
uint32_t old_nr_entries, uint32_t new_nr_entries, | ||
bool default_value, dm_block_t *new_root); | ||
|
||
/* | ||
* Frees the bitset. | ||
*/ | ||
int dm_bitset_del(struct dm_disk_bitset *info, dm_block_t root); | ||
|
||
/* | ||
* Set a bit. | ||
* | ||
* info - describes the bitset | ||
* root - the root block of the bitset | ||
* index - the bit index | ||
* new_root - on success, points to the new root block | ||
* | ||
* -ENODATA will be returned if the index is out of bounds. | ||
*/ | ||
int dm_bitset_set_bit(struct dm_disk_bitset *info, dm_block_t root, | ||
uint32_t index, dm_block_t *new_root); | ||
|
||
/* | ||
* Clears a bit. | ||
* | ||
* info - describes the bitset | ||
* root - the root block of the bitset | ||
* index - the bit index | ||
* new_root - on success, points to the new root block | ||
* | ||
* -ENODATA will be returned if the index is out of bounds. | ||
*/ | ||
int dm_bitset_clear_bit(struct dm_disk_bitset *info, dm_block_t root, | ||
uint32_t index, dm_block_t *new_root); | ||
|
||
/* | ||
* Tests a bit. | ||
* | ||
* info - describes the bitset | ||
* root - the root block of the bitset | ||
* index - the bit index | ||
* new_root - on success, points to the new root block (cached values may have been written) | ||
* result - the bit value you're after | ||
* | ||
* -ENODATA will be returned if the index is out of bounds. | ||
*/ | ||
int dm_bitset_test_bit(struct dm_disk_bitset *info, dm_block_t root, | ||
uint32_t index, dm_block_t *new_root, bool *result); | ||
|
||
/* | ||
* Flush any cached changes to disk. | ||
* | ||
* info - describes the bitset | ||
* root - the root block of the bitset | ||
* new_root - on success, points to the new root block | ||
*/ | ||
int dm_bitset_flush(struct dm_disk_bitset *info, dm_block_t root, | ||
dm_block_t *new_root); | ||
|
||
/*----------------------------------------------------------------*/ | ||
|
||
#endif /* _LINUX_DM_BITSET_H */ |