Files
flexnbd-c/src/server/bitset.h

439 lines
11 KiB
C
Raw Normal View History

#ifndef BITSET_H
#define BITSET_H
#include "util.h"
#include <inttypes.h>
#include <string.h>
#include <pthread.h>
2014-02-26 15:19:03 +00:00
/*
* Make the bitfield words 'opaque' to prevent code
* poking at the bits directly without using these
* accessors/macros
*/
typedef uint64_t bitfield_word_t;
typedef bitfield_word_t * bitfield_p;
#define BITFIELD_WORD_SIZE sizeof(bitfield_word_t)
#define BITS_PER_WORD (BITFIELD_WORD_SIZE * 8)
#define BIT_MASK(_idx) \
(1LL << ((_idx) & (BITS_PER_WORD - 1)))
#define BIT_WORD(_b, _idx) \
((bitfield_word_t*)(_b))[(_idx) / BITS_PER_WORD]
2014-02-26 15:19:03 +00:00
/* Calculates the number of words needed to store _bytes number of bytes
* this is added to accommodate code that wants to use bytes sizes
*/
#define BIT_WORDS_FOR_SIZE(_bytes) \
((_bytes + (BITFIELD_WORD_SIZE-1)) / BITFIELD_WORD_SIZE)
2014-02-26 15:19:03 +00:00
/** Return the bit value ''idx'' in array ''b'' */
static inline int bit_get(bitfield_p b, uint64_t idx) {
return (BIT_WORD(b, idx) >> (idx & (BITS_PER_WORD-1))) & 1;
}
/** Return 1 if the bit at ''idx'' in array ''b'' is set */
2014-02-26 15:19:03 +00:00
static inline int bit_is_set(bitfield_p b, uint64_t idx) {
return bit_get(b, idx);
}
/** Return 1 if the bit at ''idx'' in array ''b'' is clear */
2014-02-26 15:19:03 +00:00
static inline int bit_is_clear(bitfield_p b, uint64_t idx) {
return !bit_get(b, idx);
}
/** Tests whether the bit at ''idx'' in array ''b'' has value ''value'' */
2014-02-26 15:19:03 +00:00
static inline int bit_has_value(bitfield_p b, uint64_t idx, int value) {
return bit_get(b, idx) == !!value;
}
/** Sets the bit ''idx'' in array ''b'' */
2014-02-26 15:19:03 +00:00
static inline void bit_set(bitfield_p b, uint64_t idx) {
BIT_WORD(b, idx) |= BIT_MASK(idx);
}
/** Clears the bit ''idx'' in array ''b'' */
2014-02-26 15:19:03 +00:00
static inline void bit_clear(bitfield_p b, uint64_t idx) {
BIT_WORD(b, idx) &= ~BIT_MASK(idx);
}
/** Sets ''len'' bits in array ''b'' starting at offset ''from'' */
2014-02-26 15:19:03 +00:00
static inline void bit_set_range(bitfield_p b, uint64_t from, uint64_t len)
{
2014-02-26 15:19:03 +00:00
for ( ; (from % BITS_PER_WORD) != 0 && len > 0 ; len-- ) {
bit_set( b, from++ );
}
2014-02-26 15:19:03 +00:00
if (len >= BITS_PER_WORD) {
memset(&BIT_WORD(b, from), 0xff, len / 8 );
from += len;
2014-02-26 15:19:03 +00:00
len = len % BITS_PER_WORD;
from -= len;
}
for ( ; len > 0 ; len-- ) {
bit_set( b, from++ );
}
}
/** Clears ''len'' bits in array ''b'' starting at offset ''from'' */
2014-02-26 15:19:03 +00:00
static inline void bit_clear_range(bitfield_p b, uint64_t from, uint64_t len)
{
2014-02-26 15:19:03 +00:00
for ( ; (from % BITS_PER_WORD) != 0 && len > 0 ; len-- ) {
bit_clear( b, from++ );
}
2014-02-26 15:19:03 +00:00
if (len >= BITS_PER_WORD) {
memset(&BIT_WORD(b, from), 0, len / 8 );
from += len;
2014-02-26 15:19:03 +00:00
len = len % BITS_PER_WORD;
from -= len;
}
for ( ; len > 0 ; len-- ) {
bit_clear( b, from++ );
}
}
/** Counts the number of contiguous bits in array ''b'', starting at ''from''
* up to a maximum number of bits ''len''. Returns the number of contiguous
* bits that are the same as the first one specified. If ''run_is_set'' is
* non-NULL, the value of that bit is placed into it.
*/
2014-02-26 15:19:03 +00:00
static inline uint64_t bit_run_count(bitfield_p b, uint64_t from, uint64_t len, int *run_is_set) {
uint64_t count = 0;
2014-02-26 15:19:03 +00:00
int first_value = bit_get(b, from);
bitfield_word_t word_match = first_value ? -1 : 0;
if ( run_is_set != NULL ) {
*run_is_set = first_value;
}
2014-02-26 15:19:03 +00:00
for ( ; ((from + count) % BITS_PER_WORD) != 0 && len > 0; len--) {
if (bit_has_value(b, from + count, first_value)) {
count++;
} else {
return count;
}
}
2014-02-26 15:19:03 +00:00
for ( ; len >= BITS_PER_WORD ; len -= BITS_PER_WORD ) {
if (BIT_WORD(b, from + count) == word_match) {
count += BITS_PER_WORD;
} else {
break;
}
}
for ( ; len > 0; len-- ) {
2014-02-26 15:19:03 +00:00
if ( bit_has_value(b, from + count, first_value) ) {
count++;
}
}
return count;
}
enum bitset_stream_events {
BITSET_STREAM_UNSET = 0,
BITSET_STREAM_SET = 1,
BITSET_STREAM_ON = 2,
BITSET_STREAM_OFF = 3
};
#define BITSET_STREAM_EVENTS_ENUM_SIZE 4
struct bitset_stream_entry {
enum bitset_stream_events event;
uint64_t from;
uint64_t len;
};
/** Limit the stream size to 1MB for now.
*
* If this is too small, it'll cause requests to stall as the migration lags
* behind the changes made by those requests.
*/
#define BITSET_STREAM_SIZE ( ( 1024 * 1024 ) / sizeof( struct bitset_stream_entry ) )
struct bitset_stream {
struct bitset_stream_entry entries[BITSET_STREAM_SIZE];
int in;
int out;
int size;
pthread_mutex_t mutex;
pthread_cond_t cond_not_full;
pthread_cond_t cond_not_empty;
uint64_t queued_bytes[BITSET_STREAM_EVENTS_ENUM_SIZE];
};
/** An application of a bitset - a bitset mapping represents a file of ''size''
* broken down into ''resolution''-sized chunks. The bit set is assumed to
* represent one bit per chunk. We also bundle a lock so that the set can be
* written reliably by multiple threads.
*/
struct bitset {
pthread_mutex_t lock;
uint64_t size;
int resolution;
struct bitset_stream *stream;
int stream_enabled;
2014-02-26 15:19:03 +00:00
bitfield_word_t bits[];
};
/** Allocate a bitset for a file of the given size, and chunks of the
* given resolution.
*/
static inline struct bitset *bitset_alloc( uint64_t size, int resolution )
{
2014-02-26 15:19:03 +00:00
// calculate a size to allocate that is a multiple of the size of the
// bitfield word
size_t bitfield_size =
BIT_WORDS_FOR_SIZE((( size + resolution - 1 ) / resolution)) * sizeof( bitfield_word_t );
2014-02-26 15:19:03 +00:00
struct bitset *bitset = xmalloc(sizeof( struct bitset ) + bitfield_size);
bitset->size = size;
bitset->resolution = resolution;
/* don't actually need to call pthread_mutex_destroy '*/
pthread_mutex_init(&bitset->lock, NULL);
bitset->stream = xmalloc( sizeof( struct bitset_stream ) );
pthread_mutex_init( &bitset->stream->mutex, NULL );
/* Technically don't need to call pthread_cond_destroy either */
pthread_cond_init( &bitset->stream->cond_not_full, NULL );
pthread_cond_init( &bitset->stream->cond_not_empty, NULL );
return bitset;
}
static inline void bitset_free( struct bitset * set )
2013-09-11 14:41:59 +01:00
{
/* TODO: free our mutex... */
free( set->stream );
set->stream = NULL;
2013-09-11 14:41:59 +01:00
free( set );
}
#define INT_FIRST_AND_LAST \
uint64_t first = from/set->resolution, \
2013-07-24 17:42:08 +01:00
last = ((from+len)-1)/set->resolution, \
bitlen = (last-first)+1
#define BITSET_LOCK \
FATAL_IF_NEGATIVE(pthread_mutex_lock(&set->lock), "Error locking bitset")
#define BITSET_UNLOCK \
FATAL_IF_NEGATIVE(pthread_mutex_unlock(&set->lock), "Error unlocking bitset")
static inline void bitset_stream_enqueue(
struct bitset * set,
enum bitset_stream_events event,
uint64_t from,
uint64_t len
)
{
struct bitset_stream * stream = set->stream;
pthread_mutex_lock( &stream->mutex );
while ( stream->size == BITSET_STREAM_SIZE ) {
pthread_cond_wait( &stream->cond_not_full, &stream->mutex );
}
stream->entries[stream->in].event = event;
stream->entries[stream->in].from = from;
stream->entries[stream->in].len = len;
stream->queued_bytes[event] += len;
stream->size++;
stream->in++;
stream->in %= BITSET_STREAM_SIZE;
pthread_mutex_unlock( & stream->mutex );
pthread_cond_signal( &stream->cond_not_empty );
return;
}
static inline void bitset_stream_dequeue(
struct bitset * set,
struct bitset_stream_entry * out
)
{
struct bitset_stream * stream = set->stream;
struct bitset_stream_entry * dequeued;
pthread_mutex_lock( &stream->mutex );
while ( stream->size == 0 ) {
pthread_cond_wait( &stream->cond_not_empty, &stream->mutex );
}
dequeued = &stream->entries[stream->out];
if ( out != NULL ) {
out->event = dequeued->event;
out->from = dequeued->from;
out->len = dequeued->len;
}
stream->queued_bytes[dequeued->event] -= dequeued->len;
stream->size--;
stream->out++;
stream->out %= BITSET_STREAM_SIZE;
pthread_mutex_unlock( &stream->mutex );
pthread_cond_signal( &stream->cond_not_full );
return;
}
2013-09-23 17:15:56 +01:00
static inline size_t bitset_stream_size( struct bitset * set )
{
2013-09-23 17:15:56 +01:00
size_t size;
pthread_mutex_lock( &set->stream->mutex );
size = set->stream->size;
pthread_mutex_unlock( &set->stream->mutex );
return size;
}
static inline uint64_t bitset_stream_queued_bytes(
struct bitset * set,
enum bitset_stream_events event
)
{
uint64_t total;
pthread_mutex_lock( &set->stream->mutex );
total = set->stream->queued_bytes[event];
pthread_mutex_unlock( &set->stream->mutex );
return total;
}
static inline void bitset_enable_stream( struct bitset * set )
{
BITSET_LOCK;
set->stream_enabled = 1;
bitset_stream_enqueue( set, BITSET_STREAM_ON, 0, set->size );
BITSET_UNLOCK;
}
static inline void bitset_disable_stream( struct bitset * set )
{
BITSET_LOCK;
bitset_stream_enqueue( set, BITSET_STREAM_OFF, 0, set->size );
set->stream_enabled = 0;
BITSET_UNLOCK;
}
/** Set the bits in a bitset which correspond to the given bytes in the larger
* file.
*/
static inline void bitset_set_range(
struct bitset * set,
uint64_t from,
uint64_t len)
{
INT_FIRST_AND_LAST;
BITSET_LOCK;
bit_set_range(set->bits, first, bitlen);
if ( set->stream_enabled ) {
bitset_stream_enqueue( set, BITSET_STREAM_SET, from, len );
}
BITSET_UNLOCK;
}
2012-06-22 10:05:41 +01:00
/** Set every bit in the bitset. */
static inline void bitset_set( struct bitset * set )
2012-06-22 10:05:41 +01:00
{
bitset_set_range(set, 0, set->size);
}
/** Clear the bits in a bitset which correspond to the given bytes in the
* larger file.
*/
static inline void bitset_clear_range(
struct bitset * set,
uint64_t from,
uint64_t len)
{
INT_FIRST_AND_LAST;
BITSET_LOCK;
bit_clear_range(set->bits, first, bitlen);
if ( set->stream_enabled ) {
bitset_stream_enqueue( set, BITSET_STREAM_UNSET, from, len );
}
BITSET_UNLOCK;
}
2012-06-22 10:05:41 +01:00
/** Clear every bit in the bitset. */
static inline void bitset_clear( struct bitset * set )
2012-06-22 10:05:41 +01:00
{
bitset_clear_range(set, 0, set->size);
}
/** As per bitset_run_count but also tells you whether the run it found was set
* or unset, atomically.
*/
static inline uint64_t bitset_run_count_ex(
struct bitset * set,
uint64_t from,
uint64_t len,
int* run_is_set
)
{
uint64_t run;
/* Clip our requests to the end of the bitset, avoiding uint underflow. */
if ( from > set->size ) {
return 0;
}
len = ( len + from ) > set->size ? ( set->size - from ) : len;
INT_FIRST_AND_LAST;
2013-07-24 17:42:08 +01:00
BITSET_LOCK;
run = bit_run_count(set->bits, first, bitlen, run_is_set) * set->resolution;
2013-07-24 17:42:08 +01:00
run -= (from % set->resolution);
BITSET_UNLOCK;
2013-07-24 17:42:08 +01:00
return run;
}
/** Counts the number of contiguous bytes that are represented as a run in
* the bit field.
*/
static inline uint64_t bitset_run_count(
struct bitset * set,
uint64_t from,
uint64_t len)
{
return bitset_run_count_ex( set, from, len, NULL );
}
/** Tests whether the bit field is clear for the given file offset.
*/
static inline int bitset_is_clear_at( struct bitset * set, uint64_t at )
{
return bit_is_clear(set->bits, at/set->resolution);
}
/** Tests whether the bit field is set for the given file offset.
*/
static inline int bitset_is_set_at( struct bitset * set, uint64_t at )
{
return bit_is_set(set->bits, at/set->resolution);
}
#endif